Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migrate stXXX/XXX constant product pool -> stableswap pool #4384

Merged
merged 22 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/upgrades/v15/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import (
// UpgradeName defines the on-chain upgrade name for the Osmosis v15 upgrade.
const UpgradeName = "v15"

// pool ids to migrate
const stOSMO_OSMOPoolId = 833
const stJUNO_JUNOPoolId = 817
const stSTARS_STARSPoolId = 810

mattverse marked this conversation as resolved.
Show resolved Hide resolved
var Upgrade = upgrades.Upgrade{
UpgradeName: UpgradeName,
CreateUpgradeHandler: CreateUpgradeHandler,
Expand Down
5 changes: 5 additions & 0 deletions app/upgrades/v15/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"

gammkeeper "github.com/osmosis-labs/osmosis/v14/x/gamm/keeper"
"github.com/osmosis-labs/osmosis/v14/x/poolmanager"
poolmanagerkeeper "github.com/osmosis-labs/osmosis/v14/x/poolmanager"
)

Expand All @@ -21,3 +22,7 @@ func RegisterOsmoIonMetadata(ctx sdk.Context, bankKeeper bankkeeper.Keeper) {
func SetICQParams(ctx sdk.Context, icqKeeper *icqkeeper.Keeper) {
setICQParams(ctx, icqKeeper)
}

func MigrateBalancerPoolToSolidlyStable(ctx sdk.Context, gammKeeper *gammkeeper.Keeper, poolmanagerKeeper *poolmanager.Keeper, bankKeeper bankkeeper.Keeper, poolId uint64) {
migrateBalancerPoolToSolidlyStable(ctx, gammKeeper, poolmanagerKeeper, bankKeeper, poolId)
}
91 changes: 89 additions & 2 deletions app/upgrades/v15/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,28 @@ import (
"reflect"
"testing"

gamm "github.com/osmosis-labs/osmosis/v14/x/gamm/keeper"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"

"github.com/osmosis-labs/osmosis/v14/app/apptesting"
v15 "github.com/osmosis-labs/osmosis/v14/app/upgrades/v15"
gamm "github.com/osmosis-labs/osmosis/v14/x/gamm/keeper"
balancer "github.com/osmosis-labs/osmosis/v14/x/gamm/pool-models/balancer"
balancertypes "github.com/osmosis-labs/osmosis/v14/x/gamm/pool-models/balancer"
poolmanagertypes "github.com/osmosis-labs/osmosis/v14/x/poolmanager/types"
)

type UpgradeTestSuite struct {
apptesting.KeeperTestHelper
}

var DefaultAcctFunds sdk.Coins = sdk.NewCoins(
sdk.NewCoin("uosmo", sdk.NewInt(10000000000)),
sdk.NewCoin("foo", sdk.NewInt(10000000)),
sdk.NewCoin("bar", sdk.NewInt(10000000)),
sdk.NewCoin("baz", sdk.NewInt(10000000)),
)

func (suite *UpgradeTestSuite) SetupTest() {
suite.Setup()
}
Expand Down Expand Up @@ -67,6 +77,83 @@ func (suite *UpgradeTestSuite) TestMigrateNextPoolIdAndCreatePool() {
suite.Require().Equal(gammPoolCreationFee, poolmanagerPoolCreationFee)
}


func (suite *UpgradeTestSuite) TestMigrateBalancerToStablePools() {
suite.SetupTest() // reset

ctx := suite.Ctx
gammKeeper := suite.App.GAMMKeeper
poolmanagerKeeper := suite.App.PoolManagerKeeper
// bankKeeper := suite.App.BankKeeper
testAccount := suite.TestAccs[0]

// Mint some assets to the accounts.
suite.FundAcc(testAccount, DefaultAcctFunds)

// Create the balancer pool
swapFee, err := sdk.NewDecFromStr("0.003")
exitFee, err := sdk.NewDecFromStr("0.025")
poolID, err := suite.App.PoolManagerKeeper.CreatePool(
suite.Ctx,
balancer.NewMsgCreateBalancerPool(suite.TestAccs[0],
balancer.PoolParams{
SwapFee: swapFee,
ExitFee: exitFee,
},
[]balancertypes.PoolAsset{
{
Weight: sdk.NewInt(100),
Token: sdk.NewCoin("foo", sdk.NewInt(5000000)),
},
{
Weight: sdk.NewInt(200),
Token: sdk.NewCoin("bar", sdk.NewInt(5000000)),
},
},
""),
)
suite.Require().NoError(err)

// join the pool
shareOutAmount := sdk.NewInt(10000000)
tokenInMaxs := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(5000000)), sdk.NewCoin("bar", sdk.NewInt(5000000)))
_, _, err = suite.App.GAMMKeeper.JoinPoolNoSwap(suite.Ctx, testAccount, poolID, shareOutAmount, tokenInMaxs)
Copy link
Member

@p0mvn p0mvn Feb 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this function has sharesOut as one of its returns, we could use these to then use for ExitPool

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, used this below

suite.Require().NoError(err)

// shares before migration
balancerPool, err := gammKeeper.GetPool(suite.Ctx, poolID)
suite.Require().NoError(err)
balancerShares := balancerPool.GetTotalShares()
p0mvn marked this conversation as resolved.
Show resolved Hide resolved
balancerLiquidity := balancerPool.GetTotalPoolLiquidity(ctx).String()
// check balancer pool liquidity using the bank module
balancerBalances := suite.App.BankKeeper.GetAllBalances(ctx, balancerPool.GetAddress())

// test migrating the balancer pool to a stable pool
v15.MigrateBalancerPoolToSolidlyStable(ctx, gammKeeper, poolmanagerKeeper, suite.App.BankKeeper, poolID)

// check that the pool is now a stable pool
stablepool, err := gammKeeper.GetPool(ctx, poolID)
suite.Require().NoError(err)
suite.Require().Equal(stablepool.GetType(), poolmanagertypes.Stableswap)
// check that the number of stableswap LP shares is the same as the number of balancer LP shares
suite.Require().Equal(balancerShares.String(), stablepool.GetTotalShares().String())
p0mvn marked this conversation as resolved.
Show resolved Hide resolved
// check that the pool liquidity is the same
suite.Require().Equal(balancerLiquidity, stablepool.GetTotalPoolLiquidity(ctx).String())
// check pool liquidity using the bank module
stableBalances := suite.App.BankKeeper.GetAllBalances(ctx, stablepool.GetAddress())
suite.Require().Equal(balancerBalances, stableBalances)

// exit the pool
shareInAmount := sdk.NewInt(200000000)
p0mvn marked this conversation as resolved.
Show resolved Hide resolved
tokenOutMins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(10000000)), sdk.NewCoin("bar", sdk.NewInt(10000000)))
_, err = suite.App.GAMMKeeper.ExitPool(suite.Ctx, testAccount, poolID, shareInAmount, tokenOutMins)

asalzmann marked this conversation as resolved.
Show resolved Hide resolved
// join again
_, _, err = suite.App.GAMMKeeper.JoinPoolNoSwap(suite.Ctx, testAccount, poolID, shareOutAmount, tokenInMaxs)
suite.Require().NoError(err)

}

func (suite *UpgradeTestSuite) TestRegisterOsmoIonMetadata() {
suite.SetupTest() // reset

Expand Down
56 changes: 55 additions & 1 deletion app/upgrades/v15/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
"github.com/osmosis-labs/osmosis/v14/wasmbinding"
icqkeeper "github.com/strangelove-ventures/async-icq/v4/keeper"
icqtypes "github.com/strangelove-ventures/async-icq/v4/types"
packetforwardtypes "github.com/strangelove-ventures/packet-forward-middleware/v4/router/types"

"github.com/osmosis-labs/osmosis/v14/wasmbinding"

banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"

bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
Expand All @@ -17,6 +18,8 @@ import (
appParams "github.com/osmosis-labs/osmosis/v14/app/params"
"github.com/osmosis-labs/osmosis/v14/app/upgrades"
gammkeeper "github.com/osmosis-labs/osmosis/v14/x/gamm/keeper"
"github.com/osmosis-labs/osmosis/v14/x/gamm/pool-models/stableswap"
gammtypes "github.com/osmosis-labs/osmosis/v14/x/gamm/types"
"github.com/osmosis-labs/osmosis/v14/x/poolmanager"
poolmanagertypes "github.com/osmosis-labs/osmosis/v14/x/poolmanager/types"
)
Expand Down Expand Up @@ -47,6 +50,10 @@ func CreateUpgradeHandler(
// They are added in this upgrade.
registerOsmoIonMetadata(ctx, keepers.BankKeeper)

// Stride stXXX/XXX pools are being migrated from the standard balancer curve to the
// solidly stable curve.
migrateBalancerPoolsToSolidlyStable(ctx, keepers.GAMMKeeper, keepers.PoolManagerKeeper, keepers.BankKeeper)

return mm.RunMigrations(ctx, configurator, fromVM)
}
}
Expand All @@ -59,6 +66,53 @@ func setICQParams(ctx sdk.Context, icqKeeper *icqkeeper.Keeper) {
icqKeeper.SetParams(ctx, icqparams)
}

func migrateBalancerPoolsToSolidlyStable(ctx sdk.Context, gammKeeper *gammkeeper.Keeper, poolmanagerKeeper *poolmanager.Keeper, bankKeeper bankkeeper.Keeper) {
// migrate stOSMO_OSMOPoolId, stJUNO_JUNOPoolId, stSTARS_STARSPoolId
pools := []uint64{stOSMO_OSMOPoolId, stJUNO_JUNOPoolId, stSTARS_STARSPoolId}
for _, poolId := range pools {
migrateBalancerPoolToSolidlyStable(ctx, gammKeeper, poolmanagerKeeper, bankKeeper, poolId)
}
}

func migrateBalancerPoolToSolidlyStable(ctx sdk.Context, gammKeeper *gammkeeper.Keeper, poolmanagerKeeper *poolmanager.Keeper, bankKeeper bankkeeper.Keeper, poolId uint64) {
// fetch the pool with the given poolId
balancerPool, err := gammKeeper.GetPool(ctx, poolId)
if err != nil {
panic(err)
}

// initialize the stableswap pool
stableswapPool, err := stableswap.NewStableswapPool(
poolId,
stableswap.PoolParams{SwapFee: balancerPool.GetSwapFee(ctx), ExitFee: balancerPool.GetExitFee(ctx)},
balancerPool.GetTotalPoolLiquidity(ctx),
[]uint64{1, 1},
"osmo1k8c2m5cn322akk5wy8lpt87dd2f4yh9afcd7af", // Stride Foundation 2/3 multisig
asalzmann marked this conversation as resolved.
Show resolved Hide resolved
p0mvn marked this conversation as resolved.
Show resolved Hide resolved
"",
)
if err != nil {
panic(err)
}

// ensure the number of stableswap LP shares is the same as the number of balancer LP shares
totalShares := sdk.NewCoin(
gammtypes.GetPoolShareDenom(poolId),
balancerPool.GetTotalShares(),
)
stableswapPool.TotalShares = totalShares
p0mvn marked this conversation as resolved.
Show resolved Hide resolved

balancesBefore := bankKeeper.GetAllBalances(ctx, balancerPool.GetAddress())
// overwrite the balancer pool with the new stableswap pool
asalzmann marked this conversation as resolved.
Show resolved Hide resolved
err = gammKeeper.OverwritePool(ctx, &stableswapPool)
mattverse marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
panic(err)
}
balancesAfter := bankKeeper.GetAllBalances(ctx, stableswapPool.GetAddress())
if !balancesBefore.IsEqual(balancesAfter) {
panic("balances before and after migration are not equal")
}
}

func migrateNextPoolId(ctx sdk.Context, gammKeeper *gammkeeper.Keeper, poolmanagerKeeper *poolmanager.Keeper) {
// N.B: pool id in gamm is to be deprecated in the future
// Instead,it is moved to poolmanager.
Expand Down
17 changes: 17 additions & 0 deletions tests/e2e/configurer/chain/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,23 @@ func (n *NodeConfig) SwapExactAmountIn(tokenInCoin, tokenOutMinAmountInt string,
n.LogActionF("successfully swapped")
}

// osmosisd tx gamm join-pool max-amounts-in pool-id share-amount-out --from <address>
func (n *NodeConfig) JoinPool(maxAmountsIn string, poolId string, shareAmountOut string, from string) {
n.LogActionF("joining pool %s with maxAmountsIn %s to get shareAmountOut (%s) from (%s)", poolId, maxAmountsIn, shareAmountOut, from)
cmd := []string{"osmosisd", "tx", "gamm", "swap-exact-amount-in", maxAmountsIn, poolId, shareAmountOut, fmt.Sprintf("--from=%s", from)}
_, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd)
require.NoError(n.t, err)
n.LogActionF("successfully joined pool")
}

func (n *NodeConfig) ExitPool(from, minAmountsOut string, poolId string, shareAmountIn string) {
n.LogActionF("exiting gamm pool")
cmd := []string{"osmosisd", "tx", "gamm", "exit-pool", minAmountsOut, poolId, shareAmountIn, fmt.Sprintf("--from=%s", from)}
_, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd)
require.NoError(n.t, err)
n.LogActionF("successfully exited pool %s, minAmountsOut %s, shareAmountIn %s", poolId, minAmountsOut, shareAmountIn)
}

func (n *NodeConfig) SubmitUpgradeProposal(upgradeVersion string, upgradeHeight int64, initialDeposit sdk.Coin) {
n.LogActionF("submitting upgrade proposal %s for height %d", upgradeVersion, upgradeHeight)
cmd := []string{"osmosisd", "tx", "gov", "submit-proposal", "software-upgrade", upgradeVersion, fmt.Sprintf("--title=\"%s upgrade\"", upgradeVersion), "--description=\"upgrade proposal submission\"", fmt.Sprintf("--upgrade-height=%d", upgradeHeight), "--upgrade-info=\"\"", "--from=val", fmt.Sprintf("--deposit=%s", initialDeposit)}
Expand Down
19 changes: 19 additions & 0 deletions tests/e2e/configurer/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"strconv"
"testing"
"time"

Expand Down Expand Up @@ -108,6 +109,7 @@ func (uc *UpgradeConfigurer) ConfigureChain(chainConfig *chain.Config) error {
func (uc *UpgradeConfigurer) CreatePreUpgradeState() error {
const lockupWallet = "lockup-wallet"
const lockupWalletSuperfluid = "lockup-wallet-superfluid"
const lpWallet = "lp-wallet"

chainA := uc.chainConfigs[0]
chainANode, err := chainA.GetDefaultNode()
Expand Down Expand Up @@ -140,6 +142,23 @@ func (uc *UpgradeConfigurer) CreatePreUpgradeState() error {
// test lock and add to existing lock for both regular and superfluid lockups (only chainA)
chainA.LockAndAddToExistingLock(sdk.NewInt(1000000000000000000), poolShareDenom, lockupWalletAddrA, lockupWalletSuperfluidAddrA)

// LP to pools 833, 817, 810
// initialize lp wallets
lpWalletAddr := chainANode.CreateWallet(lpWallet)
// JoinPool 833 from lpWallet
// TODO: updaet the amounts based on the pool initialization
p0mvn marked this conversation as resolved.
Show resolved Hide resolved
maxAmountsIn833 := strconv.Itoa(1000000000000000000)
shareAmountOut833 := strconv.Itoa(1000000000000000000)
chainANode.JoinPool(maxAmountsIn833, strconv.Itoa(833), shareAmountOut833, lpWalletAddr)
// JoinPool 817 from lpWallet
maxAmountsIn817 := strconv.Itoa(1000000000000000000)
shareAmountOut817 := strconv.Itoa(1000000000000000000)
chainANode.JoinPool(maxAmountsIn817, strconv.Itoa(817), shareAmountOut817, lpWalletAddr)
// JoinPool 810 from lpWallet
maxAmountsIn810 := strconv.Itoa(1000000000000000000)
shareAmountOut810 := strconv.Itoa(1000000000000000000)
chainANode.JoinPool(maxAmountsIn810, strconv.Itoa(810), shareAmountOut810, lpWalletAddr)

return nil
}

Expand Down
36 changes: 36 additions & 0 deletions tests/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,42 @@ func (s *IntegrationTestSuite) TestGeometricTwapMigration() {
node.SwapExactAmountIn(uosmoIn, minAmountOut, fmt.Sprintf("%d", config.PreUpgradePoolId), otherDenom, swapWalletAddr)
}

// TestMigrateBalancerToStable tests that balancer to stable swap
// pool migrations run successfully. It does so by attempting to
// execute swaps and exit the pool for an account that is LPing
// the pool post-upgrade.
func (s *IntegrationTestSuite) TestMigrateBalancerToStable() {
if s.skipUpgrade {
s.T().Skip("Skipping upgrade tests")
}

const (
// This pool gets initialized pre-upgrade and migrated in the upgrade.
// TODO: Update these amounts to reflect the genesis initialization
swapAmountIn = "1000000uosmo"
swapMinAmountOut = "1"
otherDenom = "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518"
lpWallet = "lp-wallet"
exitMinAmountOut = "1"
exitShareAmountIn = "1"
)

chainA := s.configurer.GetChainConfig(0)
node, err := chainA.GetDefaultNode()
s.Require().NoError(err)

// pools migrated to stable swap
pools := []uint{833, 817, 810}
lpWalletAddr := node.CreateWallet(lpWallet)

for _, poolID := range pools {
// execute a swap on the pool
node.SwapExactAmountIn(swapAmountIn, swapMinAmountOut, fmt.Sprintf("%d", poolID), otherDenom, lpWalletAddr)
// exit the pool
node.ExitPool(lpWalletAddr, exitMinAmountOut, fmt.Sprintf("%d", poolID), exitShareAmountIn)
asalzmann marked this conversation as resolved.
Show resolved Hide resolved
}
}

// TestIBCTokenTransfer tests that IBC token transfers work as expected.
// Additionally, it attempst to create a pool with IBC denoms.
func (s *IntegrationTestSuite) TestIBCTokenTransferAndCreatePool() {
Expand Down
5 changes: 5 additions & 0 deletions x/gamm/keeper/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ func (k Keeper) setPool(ctx sdk.Context, pool poolmanagertypes.PoolI) error {
return nil
}

// Is this the preferred way to call setPool from the upgrade handler?
func (k Keeper) OverwritePool(ctx sdk.Context, pool poolmanagertypes.PoolI) error {
asalzmann marked this conversation as resolved.
Show resolved Hide resolved
return k.setPool(ctx, pool)
}

func (k Keeper) DeletePool(ctx sdk.Context, poolId uint64) error {
store := ctx.KVStore(k.storeKey)
poolKey := types.GetKeyPrefixPools(poolId)
Expand Down