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 all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Misc Improvements
* [#4131](https://github.com/osmosis-labs/osmosis/pull/4141) Add GatherValuesFromStorePrefixWithKeyParser function to osmoutils.
* [#4388](https://github.com/osmosis-labs/osmosis/pull/4388) Increase the max allowed contract size for non-proposal contracts to 3MB
* [#4384](https://github.com/osmosis-labs/osmosis/pull/4384) migrate stXXX/XXX constant product pools 833, 817, 810 to stable swap

### API breaks

Expand Down
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
8 changes: 7 additions & 1 deletion app/upgrades/v15/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import (
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
sdk "github.com/cosmos/cosmos-sdk/types"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
ibcratelimit "github.com/osmosis-labs/osmosis/v14/x/ibc-rate-limit"
icqkeeper "github.com/strangelove-ventures/async-icq/v4/keeper"

ibcratelimit "github.com/osmosis-labs/osmosis/v14/x/ibc-rate-limit"

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 @@ -25,6 +27,10 @@ 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)
}

func SetRateLimits(ctx sdk.Context, accountKeeper *authkeeper.AccountKeeper, rateLimitingICS4Wrapper *ibcratelimit.ICS4Wrapper, wasmKeeper *wasmkeeper.Keeper) {
setRateLimits(ctx, accountKeeper, rateLimitingICS4Wrapper, wasmKeeper)
}
101 changes: 99 additions & 2 deletions app/upgrades/v15/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,30 @@ import (
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"

"github.com/osmosis-labs/osmosis/osmoutils/osmoassert"
ibcratelimittypes "github.com/osmosis-labs/osmosis/v14/x/ibc-rate-limit/types"

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

"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 @@ -77,6 +87,84 @@ 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 := sdk.MustNewDecFromStr("0.003")
exitFee := sdk.MustNewDecFromStr("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(1_000_000_000_000_000)
tokenInMaxs := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(5000000)), sdk.NewCoin("bar", sdk.NewInt(5000000)))
tokenIn, sharesOut, err := suite.App.GAMMKeeper.JoinPoolNoSwap(suite.Ctx, testAccount, poolID, shareOutAmount, tokenInMaxs)
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
exitCoins, err := suite.App.GAMMKeeper.ExitPool(suite.Ctx, testAccount, poolID, sharesOut, sdk.NewCoins())
suite.Require().NoError(err)

suite.validateCons(exitCoins, tokenIn)

// join again
tokenInStable, _, err := suite.App.GAMMKeeper.JoinPoolNoSwap(suite.Ctx, testAccount, poolID, shareOutAmount, tokenInMaxs)
suite.Require().NoError(err)

suite.validateCons(tokenInStable, tokenIn)
}

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

Expand Down Expand Up @@ -159,3 +247,12 @@ func (suite *UpgradeTestSuite) TestSetRateLimits() {
suite.Require().Greaterf(len(state), 0, "state should not be empty")

}

func (suite *UpgradeTestSuite) validateCons(coinsA, coinsB sdk.Coins) {
suite.Require().Equal(len(coinsA), len(coinsB))
for _, coinA := range coinsA {
coinBAmount := coinsB.AmountOf(coinA.Denom)
// minor tolerance due to fees and rounding
osmoassert.DecApproxEq(suite.T(), coinBAmount.ToDec(), coinA.Amount.ToDec(), sdk.NewDec(2))
}
}
53 changes: 53 additions & 0 deletions app/upgrades/v15/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,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"
)

Expand Down Expand Up @@ -59,6 +61,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)

setRateLimits(ctx, keepers.AccountKeeper, keepers.RateLimitingICS4Wrapper, keepers.WasmKeeper)

return mm.RunMigrations(ctx, configurator, fromVM)
Expand All @@ -73,6 +79,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.OverwritePoolV15MigrationUnsafe(ctx, &stableswapPool)
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 setRateLimits(ctx sdk.Context, accountKeeper *authkeeper.AccountKeeper, rateLimitingICS4Wrapper *ibcratelimit.ICS4Wrapper, wasmKeeper *wasmkeeper.Keeper) {
govModule := accountKeeper.GetModuleAddress(govtypes.ModuleName)
contractKeeper := wasmkeeper.NewGovPermissionKeeper(wasmKeeper)
Expand Down
16 changes: 16 additions & 0 deletions tests/e2e/configurer/chain/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,22 @@ func (n *NodeConfig) SwapExactAmountIn(tokenInCoin, tokenOutMinAmountInt string,
n.LogActionF("successfully swapped")
}

func (n *NodeConfig) JoinPoolExactAmountIn(tokenIn string, poolId uint64, shareOutMinAmount string, from string) {
n.LogActionF("join-swap-extern-amount-in (%s) (%s) from (%s), pool id (%d)", tokenIn, shareOutMinAmount, from, poolId)
cmd := []string{"osmosisd", "tx", "gamm", "join-swap-extern-amount-in", tokenIn, shareOutMinAmount, fmt.Sprintf("--pool-id=%d", poolId), 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 uint64, shareAmountIn string) {
n.LogActionF("exiting gamm pool")
cmd := []string{"osmosisd", "tx", "gamm", "exit-pool", fmt.Sprintf("--min-amounts-out=%s", minAmountsOut), fmt.Sprintf("--share-amount-in=%s", shareAmountIn), fmt.Sprintf("--pool-id=%d", poolId), 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 %d, 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
12 changes: 12 additions & 0 deletions tests/e2e/configurer/chain/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,18 @@ func (n *NodeConfig) QueryNumPools() uint64 {
return numPools.NumPools
}

func (n *NodeConfig) QueryPoolType(poolId string) string {
path := fmt.Sprintf("/osmosis/gamm/v1beta1/pool_type/%s", poolId)
bz, err := n.QueryGRPCGateway(path)
require.NoError(n.t, err)

var poolTypeResponse gammtypes.QueryPoolTypeResponse
err = util.Cdc.UnmarshalJSON(bz, &poolTypeResponse)
require.NoError(n.t, err)

return poolTypeResponse.PoolType
}

func (n *NodeConfig) QueryConcentratedPositions(address string) []cltypes.FullPositionByOwnerResult {
path := fmt.Sprintf("/osmosis/concentratedliquidity/v1beta1/positions/%s", address)

Expand Down
2 changes: 2 additions & 0 deletions tests/e2e/configurer/config/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ var (
// creation in case more pools are added to genesis
// in the future
PreUpgradePoolId uint64 = 2

StrideMigrateWallet = "stride-migration"
)
20 changes: 20 additions & 0 deletions tests/e2e/configurer/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,26 @@ 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
amountOfEachTokenToLP := initialization.DefaultStrideDenomBalance / 1_000_000
shareOutMin := "1"

config.StrideMigrateWallet = chainANode.CreateWalletAndFund(config.StrideMigrateWallet, []string{
fmt.Sprintf("%d%s", amountOfEachTokenToLP, initialization.StOsmoDenom),
fmt.Sprintf("%d%s", amountOfEachTokenToLP, initialization.StJunoDenom),
fmt.Sprintf("%d%s", amountOfEachTokenToLP, initialization.StStarsDenom),
})

tokenInStOsmo := fmt.Sprintf("%d%s", amountOfEachTokenToLP, initialization.StOsmoDenom)
chainANode.JoinPoolExactAmountIn(tokenInStOsmo, initialization.StOSMO_OSMOPoolId, shareOutMin, config.StrideMigrateWallet)

tokenInStJuno := fmt.Sprintf("%d%s", amountOfEachTokenToLP, initialization.StJunoDenom)
chainANode.JoinPoolExactAmountIn(tokenInStJuno, initialization.StJUNO_JUNOPoolId, shareOutMin, config.StrideMigrateWallet)

tokenInStStars := fmt.Sprintf("%d%s", amountOfEachTokenToLP, initialization.StStarsDenom)
chainANode.JoinPoolExactAmountIn(tokenInStStars, initialization.StSTARS_STARSPoolId, shareOutMin, config.StrideMigrateWallet)

return nil
}

Expand Down
28 changes: 22 additions & 6 deletions tests/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (
"time"

transfertypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"

"github.com/iancoleman/orderedmap"

poolmanagertypes "github.com/osmosis-labs/osmosis/v14/x/poolmanager/types"

"github.com/osmosis-labs/osmosis/v14/tests/e2e/configurer/chain"
"github.com/osmosis-labs/osmosis/v14/tests/e2e/util"

Expand Down Expand Up @@ -1095,25 +1096,40 @@ func (s *IntegrationTestSuite) TestGeometricTWAP() {
// This test is to be re-enabled for upgrade once the upgrade handler logic is added and
// the balancer pool genesis is backported to v14.
func (s *IntegrationTestSuite) TestStridePoolMigration() {
if s.skipUpgrade {
s.T().Log("Skipping migration test when upgrade is disable. This test depends on running v15 upgrade handler.")
}

const (
// Configurations for tests/e2e/scripts/pool1A.json
// This pool gets initialized pre-upgrade.
minAmountOut = "1"
migrationWallet = "stride-migration"
minAmountOut = "1"
shareAmountIn = "1"
)

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

fundTokens := []string{fmt.Sprintf("1000000%s", initialization.StOsmoDenom), fmt.Sprintf("1000000%s", initialization.StJunoDenom), fmt.Sprintf("1000000%s", initialization.StStarsDenom)}
for _, token := range fundTokens {
node.BankSend(token, initialization.ValidatorWalletName, config.StrideMigrateWallet)
}

otherDenoms := []string{initialization.OsmoDenom, initialization.JunoDenom, initialization.StarsDenom}
swapWalletAddr := node.CreateWalletAndFund(migrationWallet, fundTokens)

migrationPools := []uint64{initialization.StOSMO_OSMOPoolId, initialization.StJUNO_JUNOPoolId, initialization.StSTARS_STARSPoolId}

for i, poolId := range migrationPools {
// Swap to make sure that
node.SwapExactAmountIn(fundTokens[i], minAmountOut, fmt.Sprintf("%d", poolId), otherDenoms[i], swapWalletAddr)
// Query and assert to make sure that pool type is stableswap
poolType := node.QueryPoolType(fmt.Sprintf("%d", poolId))
stableswapType := poolmanagertypes.Stableswap.String()
s.Require().Equal(poolType, stableswapType, "Pool type should be stableswap after upgrade")

// Swap to make sure that migrations did not break anything critical.
node.SwapExactAmountIn(fundTokens[i], minAmountOut, fmt.Sprintf("%d", poolId), otherDenoms[i], config.StrideMigrateWallet)

// Exit one share
node.ExitPool(config.StrideMigrateWallet, "", poolId, shareAmountIn)
}
}
6 changes: 6 additions & 0 deletions x/gamm/keeper/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ func (k Keeper) setPool(ctx sdk.Context, pool poolmanagertypes.PoolI) error {
return nil
}

// OverwritePoolV15MigrationUnsafe is a temporary method for calling from the v15 upgrade handler
// for balancer to stableswap pool migration. Do not use for other purposes.
func (k Keeper) OverwritePoolV15MigrationUnsafe(ctx sdk.Context, pool poolmanagertypes.PoolI) error {
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