From 5f48cb363b74fbcff058c44c98476a671f2e28ac Mon Sep 17 00:00:00 2001 From: mattverse Date: Wed, 2 Aug 2023 19:42:30 +0900 Subject: [PATCH 01/27] WIP --- go.sum | 4 - proto/osmosis/superfluid/tx.proto | 36 ++ x/superfluid/keeper/export_test.go | 4 +- x/superfluid/keeper/keeper.go | 4 +- x/superfluid/keeper/migrate.go | 10 +- x/superfluid/keeper/migrate_test.go | 4 +- x/superfluid/keeper/msg_server.go | 11 + x/superfluid/keeper/stake.go | 142 +++++ x/superfluid/types/errors.go | 11 + x/superfluid/types/expected_keepers.go | 11 + x/superfluid/types/tx.pb.go | 793 ++++++++++++++++++++++--- 11 files changed, 931 insertions(+), 99 deletions(-) diff --git a/go.sum b/go.sum index a9808298f6a..62267978e7d 100644 --- a/go.sum +++ b/go.sum @@ -950,10 +950,6 @@ github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3 h1:Ylmch github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3/go.mod h1:lV6KnqXYD/ayTe7310MHtM3I2q8Z6bBfMAi+bhwPYtI= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230629191111-f375469de8b6 h1:Kmkx5Rh72+LB8AL6dc6fZA+IVR0INu0YIiMF2ScDhaQ= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230629191111-f375469de8b6/go.mod h1:JTym95/bqrSnG5MPcXr1YDhv43JdCeo3p+iDbazoX68= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230709024311-81c831b050de h1:W2lMduMgpNA5zheEIIialw08n1pWJ44Y4t2F924tpDU= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230709024311-81c831b050de/go.mod h1:Pl8Nzx6O6ow/+aqfMoMSz4hX+zz6RrnDYsooptECGxM= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230715164027-b45d8bd42434 h1:MXPrA3sDtqOHYUa9zl4HMGMW+IJwGMqUf6+Hl9nhrCA= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230715164027-b45d8bd42434/go.mod h1:Pl8Nzx6O6ow/+aqfMoMSz4hX+zz6RrnDYsooptECGxM= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230728163612-426afac90c44 h1:UOaBVxEMMv2FS1znU7kHBdtSeZQIjnmXL4r9r19XyBo= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230728163612-426afac90c44/go.mod h1:Pl8Nzx6O6ow/+aqfMoMSz4hX+zz6RrnDYsooptECGxM= github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304 h1:RIrWLzIiZN5Xd2JOfSOtGZaf6V3qEQYg6EaDTAkMnCo= diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index 4c514aa0646..51a8f88a307 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -52,6 +52,11 @@ service Msg { rpc AddToConcentratedLiquiditySuperfluidPosition( MsgAddToConcentratedLiquiditySuperfluidPosition) returns (MsgAddToConcentratedLiquiditySuperfluidPositionResponse); + + // UnbondConvertAndStake breaks all locks / superfluid staked assets, + // converts them to osmo then stakes the osmo to the designated validator. + rpc UnbondConvertAndStake(MsgUnbondConvertAndStake) + returns (MsgUnbondConvertAndStakeResponse); } message MsgSuperfluidDelegate { @@ -231,4 +236,35 @@ message MsgAddToConcentratedLiquiditySuperfluidPositionResponse { (gogoproto.nullable) = false ]; uint64 lock_id = 4 [ (gogoproto.moretags) = "yaml:\"lock_id\"" ]; +} + +// ===================== MsgUnbondConvertAndStake +message MsgUnbondConvertAndStake { + option (amino.name) = "osmosis/unbond-convert-and-stake"; + + uint64 lock_id = 1 [ (gogoproto.moretags) = "yaml:\"lock_id\"" ]; + string sender = 2 [ (gogoproto.moretags) = "yaml:\"sender\"" ]; + string val_addr = 3; + // min_amt_to_stake indicates the minimum amount to stake after conversion + string min_amt_to_stake = 4 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"min_amt_to_stake\"", + (gogoproto.nullable) = false + ]; + cosmos.base.v1beta1.Coin shares_to_convert_and_stake = 5 [ + (gogoproto.moretags) = "yaml:\"shares_to_convert_and_stake\"", + (gogoproto.nullable) = false + ]; +} + +message MsgUnbondConvertAndStakeResponse { + string total_amt_staked = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", + (gogoproto.moretags) = "yaml:\"total_amt_staked\"", + (gogoproto.nullable) = false + ]; + string total_shares = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; } \ No newline at end of file diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 662059c0feb..4ff9c0366cc 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -33,8 +33,8 @@ func (k Keeper) MigrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, return k.migrateNonSuperfluidLockBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, tokenOutMins) } -func (k Keeper) ValidateSharesToMigrateUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { - return k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins) +func (k Keeper) validateSharesToUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { + return k.validateSharesToUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins) } func (k Keeper) RouteMigration(ctx sdk.Context, sender sdk.AccAddress, lockId int64, sharesToMigrate sdk.Coin) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { diff --git a/x/superfluid/keeper/keeper.go b/x/superfluid/keeper/keeper.go index db6c7bc264d..392384184d8 100644 --- a/x/superfluid/keeper/keeper.go +++ b/x/superfluid/keeper/keeper.go @@ -27,6 +27,7 @@ type Keeper struct { gk types.GammKeeper ik types.IncentivesKeeper clk types.ConcentratedKeeper + pmk types.PoolManagerKeeper lms types.LockupMsgServer } @@ -34,7 +35,7 @@ type Keeper struct { var _ govtypes.StakingKeeper = (*Keeper)(nil) // NewKeeper returns an instance of Keeper. -func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, ak authkeeper.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, dk types.CommunityPoolKeeper, ek types.EpochKeeper, lk types.LockupKeeper, gk types.GammKeeper, ik types.IncentivesKeeper, lms types.LockupMsgServer, clk types.ConcentratedKeeper) *Keeper { +func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, ak authkeeper.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, dk types.CommunityPoolKeeper, ek types.EpochKeeper, lk types.LockupKeeper, gk types.GammKeeper, ik types.IncentivesKeeper, lms types.LockupMsgServer, clk types.ConcentratedKeeper, pmk types.PoolManagerKeeper) *Keeper { // set KeyTable if it has not already been set if !paramSpace.HasKeyTable() { paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) @@ -52,6 +53,7 @@ func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, ak authkee gk: gk, ik: ik, clk: clk, + pmk: pmk, lms: lms, } diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index a20d89d2787..e557c22f8e2 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -107,7 +107,7 @@ func (k Keeper) migrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.validateSharesToUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } @@ -153,7 +153,7 @@ func (k Keeper) migrateSuperfluidUnbondingBalancerToConcentrated(ctx sdk.Context // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.validateSharesToUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } @@ -201,7 +201,7 @@ func (k Keeper) migrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.validateSharesToUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } @@ -293,7 +293,7 @@ func (k Keeper) validateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId return poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, nil } -// validateSharesToMigrateUnlockAndExitBalancerPool validates the unlocking and exiting of gamm LP tokens from the Balancer pool. It performs the following steps: +// validateSharesToUnlockAndExitBalancerPool validates the unlocking and exiting of gamm LP tokens from the Balancer pool. It performs the following steps: // // 1. Completes the unlocking process / deletes synthetic locks for the provided lock. // 2. If shares to migrate are not specified, all shares in the lock are migrated. @@ -301,7 +301,7 @@ func (k Keeper) validateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId // 4. Exits the position in the Balancer pool. // 5. Ensures that exactly two coins are returned. // 6. Any remaining shares that were not migrated are re-locked as a new lock for the remaining time on the lock. -func (k Keeper) validateSharesToMigrateUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { +func (k Keeper) validateSharesToUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { // validateMigration ensures that the preMigrationLock contains coins of length 1. gammSharesInLock := lock.Coins[0] diff --git a/x/superfluid/keeper/migrate_test.go b/x/superfluid/keeper/migrate_test.go index 8411e503247..1a8ebf315d7 100644 --- a/x/superfluid/keeper/migrate_test.go +++ b/x/superfluid/keeper/migrate_test.go @@ -797,7 +797,7 @@ func (s *KeeperTestSuite) TestValidateMigration() { } } -func (s *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPool() { +func (s *KeeperTestSuite) TestvalidateSharesToUnlockAndExitBalancerPool() { defaultJoinTime := s.Ctx.BlockTime() type sendTest struct { overwritePreMigrationLock bool @@ -917,7 +917,7 @@ func (s *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPool() } // System under test - exitCoins, err := superfluidKeeper.ValidateSharesToMigrateUnlockAndExitBalancerPool(ctx, poolJoinAcc, balancerPooId, lock, coinsToMigrate, tc.tokenOutMins) + exitCoins, err := superfluidKeeper.validateSharesToUnlockAndExitBalancerPool(ctx, poolJoinAcc, balancerPooId, lock, coinsToMigrate, tc.tokenOutMins) if tc.expectedError != nil { s.Require().Error(err) s.Require().ErrorContains(err, tc.expectedError.Error()) diff --git a/x/superfluid/keeper/msg_server.go b/x/superfluid/keeper/msg_server.go index c29b02b800b..f6b8fe04943 100644 --- a/x/superfluid/keeper/msg_server.go +++ b/x/superfluid/keeper/msg_server.go @@ -242,3 +242,14 @@ func (server msgServer) AddToConcentratedLiquiditySuperfluidPosition(goCtx conte return &types.MsgAddToConcentratedLiquiditySuperfluidPositionResponse{PositionId: newPositionId, Amount0: actualAmount0, Amount1: actualAmount1, LockId: newLockId, NewLiquidity: newLiquidity}, nil } + +func (server msgServer) UnbondConvertAndStake(goCtx context.Context, msg *types.MsgUnbondConvertAndStake) (*types.MsgUnbondConvertAndStakeResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + err := server.keeper.UnbondConvertAndStake(ctx, msg.LockId, msg.Sender, msg.ValAddr, msg.TokenOutMins) + if err != nil { + return nil, err + } + + return &types.MsgUnbondConvertAndStakeResponse{}, nil +} diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 7310c4f2c0c..f301c169105 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -2,10 +2,12 @@ package keeper import ( "fmt" + "strings" errorsmod "cosmossdk.io/errors" "github.com/osmosis-labs/osmosis/osmoutils" + gammtypes "github.com/osmosis-labs/osmosis/v17/x/gamm/types" lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" @@ -656,3 +658,143 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn fn(index+int64(i), delegation) } } + +func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, tokenOutMins sdk.Coins, sharesToConvertAndStake sdk.Coin) error { + lock, err := k.lk.GetLockByID(ctx, lockID) + if err != nil { + return err + } + + senderAddr, err := sdk.AccAddressFromBech32(sender) + if err != nil { + return err + } + + // use routeMigration method to check status of lock(either superfluid staked, superfluid unbonding, vanialla locked, unlocked) + synthLockBeforeMigration, migrationType, err := k.routeMigration(ctx, senderAddr, int64(lockID), sharesToConvertAndStake) + if err != nil { + return err + } + switch migrationType { + case SuperfluidBonded: + err = k.convertSuperfluidBondedToStakedOsmo(ctx, senderAddr, valAddr, lockID, sharesToConvertAndStake, tokenOutMins) + if err != nil { + return err + } + case SuperfluidUnbonding: + + // + case NonSuperfluid: + + case Unlocked: + + default: + return fmt.Errorf("unsupported migration type") + } + + return nil +} + +func (k Keeper) convertSuperfluidBondedToStakedOsmo(ctx sdk.Context, sender sdk.AccAddress, valAddr string, originalLockId uint64, + sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { + // do basic validation before converting + poolIdLeaving, preMigrationLock, err := k.validateUnbondConvertAndStake(ctx, sender, originalLockId, sharesToStake) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + + // undelegate superfluid delegated position instantly without creating unbonding synth lock (just yet). + _, err = k.undelegateCommon(ctx, sender.String(), originalLockId) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + + // Force unlock, validate the provided sharesToStake, and exit the balancer pool. + // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. + exitCoins, err := k.validateSharesToUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToStake, sdk.NewCoins()) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + + totalAmtConverted, shares, err = k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + + return totalAmtConverted, shares, nil +} + +func (k Keeper) validateUnbondConvertAndStake(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (poolIdLeaving uint64, preMigrationLock *lockuptypes.PeriodLock, err error) { + // Defense in depth, ensuring the sharesToMigrate contains gamm pool share prefix. + if !strings.HasPrefix(sharesToMigrate.Denom, gammtypes.GAMMTokenPrefix) { + return 0, &lockuptypes.PeriodLock{}, types.SharesToMigrateDenomPrefixError{Denom: sharesToMigrate.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} + } + + // Get the balancer poolId by parsing the gamm share denom. + poolIdLeaving, err = gammtypes.GetPoolIdFromShareDenom(sharesToMigrate.Denom) + if err != nil { + return 0, &lockuptypes.PeriodLock{}, err + } + + // Check that lockID corresponds to sender and that the denomination of LP shares corresponds to the poolId. + preMigrationLock, err = k.validateGammLockForSuperfluidStaking(ctx, sender, poolIdLeaving, lockId) + if err != nil { + return 0, &lockuptypes.PeriodLock{}, err + } + return poolIdLeaving, preMigrationLock, nil +} + +func (k Keeper) convertGammSharesToOsmoAndStake( + ctx sdk.Context, + sender sdk.AccAddress, valAddr string, + poolIdLeaving uint64, exitCoins sdk.Coins, minAmtToStake sdk.Int, +) (totalAmtCoverted sdk.Int, shares sdk.Dec, err error) { + var nonOsmoCoins sdk.Coins + bondDenom := k.sk.BondDenom(ctx) + + // from the exit coins, separate non-bond denom and bond denom. + for _, exitCoin := range exitCoins { + // if coin is not uosmo, add it to non-osmo Coins + if exitCoin.Denom != bondDenom { + nonOsmoCoins = append(nonOsmoCoins, exitCoin) + } + } + originalBondDenomAmt := exitCoins.AmountOf(bondDenom) + + // track how much non-uosmo tokens we have converted to uosmo + totalAmtCoverted = sdk.ZeroInt() + + // iterate over non-bond denom coins and swap them into bond denom + for _, coinToConvert := range nonOsmoCoins { + tokenOutAmt, err := k.pmk.SwapExactAmountIn(ctx, sender, poolIdLeaving, coinToConvert, bondDenom, sdk.ZeroInt()) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + + totalAmtCoverted = totalAmtCoverted.Add(tokenOutAmt) + } + + // add the converted amount with the amount of osmo from exit coin to get total amount we would be staking + totalAmtToStake := originalBondDenomAmt.Add(totalAmtCoverted) + + // check if the total amount to stake after all conversion is greater than provided min amount to stake + if totalAmtToStake.LT(minAmtToStake) { + return sdk.ZeroInt(), sdk.ZeroDec(), types.TokenConvertedLessThenDesiredStakeError{ + ActualTotalAmtToStake: totalAmtToStake, + ExpectedTotalAmtToStake: minAmtToStake, + } + } + + val, err := k.validateValAddrForDelegate(ctx, valAddr) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + + // delegate now! + shares, err = k.sk.Delegate(ctx, sender, totalAmtToStake, stakingtypes.Unbonded, val, true) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + + return totalAmtCoverted, sdk.ZeroDec(), nil +} diff --git a/x/superfluid/types/errors.go b/x/superfluid/types/errors.go index ada05c155d5..61dbf5625af 100644 --- a/x/superfluid/types/errors.go +++ b/x/superfluid/types/errors.go @@ -6,6 +6,8 @@ import ( errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" ) @@ -115,3 +117,12 @@ type UnexpectedDenomError struct { func (e UnexpectedDenomError) Error() string { return fmt.Sprintf("provided denom (%s) was expected to be formatted as follows: %s", e.ProvidedDenom, e.ExpectedDenom) } + +type TokenConvertedLessThenDesiredStakeError struct { + ActualTotalAmtToStake sdk.Int + ExpectedTotalAmtToStake sdk.Int +} + +func (e TokenConvertedLessThenDesiredStakeError) Error() string { + return fmt.Sprintf("actual amount converted to stake (%s) is less then minimum amount expected to be staked (%s)", e.ActualTotalAmtToStake, e.ExpectedTotalAmtToStake) +} diff --git a/x/superfluid/types/expected_keepers.go b/x/superfluid/types/expected_keepers.go index 9bb7b76d92f..38c9dfcd3ca 100644 --- a/x/superfluid/types/expected_keepers.go +++ b/x/superfluid/types/expected_keepers.go @@ -121,3 +121,14 @@ type ConcentratedKeeper interface { GetUserPositions(ctx sdk.Context, addr sdk.AccAddress, poolId uint64) ([]model.Position, error) GetLockIdFromPositionId(ctx sdk.Context, positionId uint64) (uint64, error) } + +type PoolManagerKeeper interface { + SwapExactAmountIn( + ctx sdk.Context, + sender sdk.AccAddress, + poolId uint64, + tokenIn sdk.Coin, + tokenOutDenom string, + tokenOutMinAmount sdk.Int, + ) (sdk.Int, error) +} diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index e14270d6fbe..7116d87a16d 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -1021,6 +1021,115 @@ func (m *MsgAddToConcentratedLiquiditySuperfluidPositionResponse) GetLockId() ui return 0 } +// ===================== MsgUnbondConvertAndStake +type MsgUnbondConvertAndStake struct { + LockId uint64 `protobuf:"varint,1,opt,name=lock_id,json=lockId,proto3" json:"lock_id,omitempty" yaml:"lock_id"` + Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + ValAddr string `protobuf:"bytes,3,opt,name=val_addr,json=valAddr,proto3" json:"val_addr,omitempty"` + // min_amt_to_stake indicates the minimum amount to stake after conversion + MinAmtToStake github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=min_amt_to_stake,json=minAmtToStake,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_amt_to_stake" yaml:"min_amt_to_stake"` + SharesToConvertAndStake types.Coin `protobuf:"bytes,5,opt,name=shares_to_convert_and_stake,json=sharesToConvertAndStake,proto3" json:"shares_to_convert_and_stake" yaml:"shares_to_convert_and_stake"` +} + +func (m *MsgUnbondConvertAndStake) Reset() { *m = MsgUnbondConvertAndStake{} } +func (m *MsgUnbondConvertAndStake) String() string { return proto.CompactTextString(m) } +func (*MsgUnbondConvertAndStake) ProtoMessage() {} +func (*MsgUnbondConvertAndStake) Descriptor() ([]byte, []int) { + return fileDescriptor_55b645f187d22814, []int{18} +} +func (m *MsgUnbondConvertAndStake) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUnbondConvertAndStake) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUnbondConvertAndStake.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUnbondConvertAndStake) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUnbondConvertAndStake.Merge(m, src) +} +func (m *MsgUnbondConvertAndStake) XXX_Size() int { + return m.Size() +} +func (m *MsgUnbondConvertAndStake) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUnbondConvertAndStake.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUnbondConvertAndStake proto.InternalMessageInfo + +func (m *MsgUnbondConvertAndStake) GetLockId() uint64 { + if m != nil { + return m.LockId + } + return 0 +} + +func (m *MsgUnbondConvertAndStake) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgUnbondConvertAndStake) GetValAddr() string { + if m != nil { + return m.ValAddr + } + return "" +} + +func (m *MsgUnbondConvertAndStake) GetSharesToConvertAndStake() types.Coin { + if m != nil { + return m.SharesToConvertAndStake + } + return types.Coin{} +} + +type MsgUnbondConvertAndStakeResponse struct { + TotalAmtStaked github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=total_amt_staked,json=totalAmtStaked,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_amt_staked" yaml:"total_amt_staked"` + TotalShares github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=total_shares,json=totalShares,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"total_shares"` +} + +func (m *MsgUnbondConvertAndStakeResponse) Reset() { *m = MsgUnbondConvertAndStakeResponse{} } +func (m *MsgUnbondConvertAndStakeResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUnbondConvertAndStakeResponse) ProtoMessage() {} +func (*MsgUnbondConvertAndStakeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_55b645f187d22814, []int{19} +} +func (m *MsgUnbondConvertAndStakeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUnbondConvertAndStakeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUnbondConvertAndStakeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUnbondConvertAndStakeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUnbondConvertAndStakeResponse.Merge(m, src) +} +func (m *MsgUnbondConvertAndStakeResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUnbondConvertAndStakeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUnbondConvertAndStakeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUnbondConvertAndStakeResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgSuperfluidDelegate)(nil), "osmosis.superfluid.MsgSuperfluidDelegate") proto.RegisterType((*MsgSuperfluidDelegateResponse)(nil), "osmosis.superfluid.MsgSuperfluidDelegateResponse") @@ -1040,96 +1149,110 @@ func init() { proto.RegisterType((*MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse)(nil), "osmosis.superfluid.MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse") proto.RegisterType((*MsgAddToConcentratedLiquiditySuperfluidPosition)(nil), "osmosis.superfluid.MsgAddToConcentratedLiquiditySuperfluidPosition") proto.RegisterType((*MsgAddToConcentratedLiquiditySuperfluidPositionResponse)(nil), "osmosis.superfluid.MsgAddToConcentratedLiquiditySuperfluidPositionResponse") + proto.RegisterType((*MsgUnbondConvertAndStake)(nil), "osmosis.superfluid.MsgUnbondConvertAndStake") + proto.RegisterType((*MsgUnbondConvertAndStakeResponse)(nil), "osmosis.superfluid.MsgUnbondConvertAndStakeResponse") } func init() { proto.RegisterFile("osmosis/superfluid/tx.proto", fileDescriptor_55b645f187d22814) } var fileDescriptor_55b645f187d22814 = []byte{ - // 1343 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcf, 0x73, 0xd3, 0xc6, - 0x17, 0x8f, 0xec, 0x90, 0xc0, 0x86, 0x00, 0xd1, 0x97, 0x1f, 0xc6, 0x5f, 0xb0, 0xcc, 0x42, 0x69, - 0xf8, 0x61, 0x29, 0x86, 0x16, 0x98, 0x9c, 0x88, 0xe3, 0xa1, 0x63, 0x1a, 0x4f, 0x19, 0x11, 0xa6, - 0x33, 0x5c, 0x3c, 0xb2, 0x77, 0x23, 0xd4, 0xc8, 0x5a, 0xa3, 0x95, 0x92, 0x30, 0x3d, 0xb5, 0x3d, - 0x74, 0x86, 0x13, 0xc7, 0xde, 0x7a, 0x6e, 0x0f, 0x1d, 0xfe, 0x84, 0x1e, 0x7a, 0x60, 0x7a, 0xe2, - 0xd8, 0x69, 0x67, 0x42, 0x87, 0x1c, 0x7a, 0xcf, 0xb1, 0xa7, 0xce, 0x4a, 0xab, 0xb5, 0x9c, 0x48, - 0x38, 0x32, 0xee, 0xa1, 0x97, 0x44, 0xbb, 0xfb, 0x7e, 0x7c, 0xde, 0xdb, 0xf7, 0x79, 0xbb, 0x6b, - 0xf0, 0x7f, 0x42, 0xbb, 0x84, 0x5a, 0x54, 0xa3, 0x7e, 0x0f, 0xbb, 0x6b, 0xb6, 0x6f, 0x21, 0xcd, - 0xdb, 0x52, 0x7b, 0x2e, 0xf1, 0x88, 0x2c, 0xf3, 0x45, 0xb5, 0xbf, 0x58, 0x3c, 0x69, 0x12, 0x93, - 0x04, 0xcb, 0x1a, 0xfb, 0x0a, 0x25, 0x8b, 0x73, 0x46, 0xd7, 0x72, 0x88, 0x16, 0xfc, 0xe5, 0x53, - 0x25, 0x93, 0x10, 0xd3, 0xc6, 0x5a, 0x30, 0x6a, 0xfb, 0x6b, 0x1a, 0xf2, 0x5d, 0xc3, 0xb3, 0x88, - 0x13, 0xad, 0x77, 0x02, 0xeb, 0x5a, 0xdb, 0xa0, 0x58, 0xdb, 0xa8, 0xb6, 0xb1, 0x67, 0x54, 0xb5, - 0x0e, 0xb1, 0xa2, 0x75, 0x65, 0xaf, 0xbe, 0x67, 0x75, 0x31, 0xf5, 0x8c, 0x6e, 0x8f, 0x0b, 0x5c, - 0x4c, 0x80, 0xde, 0xff, 0x0c, 0x85, 0xe0, 0x77, 0x12, 0x38, 0xd5, 0xa4, 0xe6, 0x43, 0x31, 0x5f, - 0xc7, 0x36, 0x36, 0x0d, 0x0f, 0xcb, 0x57, 0xc0, 0x14, 0xc5, 0x0e, 0xc2, 0x6e, 0x41, 0x2a, 0x4b, - 0xf3, 0x47, 0x6a, 0x73, 0xbb, 0xdb, 0xca, 0xec, 0x33, 0xa3, 0x6b, 0x2f, 0xc2, 0x70, 0x1e, 0xea, - 0x5c, 0x40, 0x3e, 0x03, 0xa6, 0x6d, 0xd2, 0x59, 0x6f, 0x59, 0xa8, 0x90, 0x2b, 0x4b, 0xf3, 0x93, - 0xfa, 0x14, 0x1b, 0x36, 0x90, 0x7c, 0x16, 0x1c, 0xde, 0x30, 0xec, 0x96, 0x81, 0x90, 0x5b, 0xc8, - 0x33, 0x2b, 0xfa, 0xf4, 0x86, 0x61, 0x2f, 0x21, 0xe4, 0x2e, 0x96, 0x9f, 0xff, 0xf5, 0xf2, 0x6a, - 0x42, 0x76, 0x2b, 0x88, 0x03, 0x80, 0x0a, 0x38, 0x9f, 0x88, 0x4c, 0xc7, 0xb4, 0x47, 0x1c, 0x8a, - 0xe1, 0x57, 0x12, 0x38, 0x33, 0x20, 0xf1, 0xc8, 0x41, 0x63, 0x44, 0xbf, 0x08, 0x19, 0xc4, 0xf3, - 0x09, 0x10, 0x7d, 0xe1, 0x07, 0x5e, 0x00, 0x4a, 0x0a, 0x04, 0x01, 0xf3, 0xeb, 0xfd, 0x30, 0xdb, - 0xc4, 0x41, 0x2b, 0xa4, 0xb3, 0x3e, 0x16, 0x98, 0x17, 0x19, 0xcc, 0x52, 0x22, 0x4c, 0xe6, 0xa7, - 0xc2, 0xc4, 0x12, 0x70, 0x46, 0x18, 0x04, 0xce, 0x9f, 0x24, 0x70, 0x29, 0x25, 0x96, 0x25, 0x67, - 0xcc, 0xa0, 0xe5, 0x1a, 0x98, 0x64, 0xb5, 0x1c, 0x54, 0xc5, 0xcc, 0x8d, 0xb3, 0x6a, 0x58, 0xec, - 0x2a, 0x2b, 0x76, 0x95, 0x17, 0xbb, 0xba, 0x4c, 0x2c, 0xa7, 0xf6, 0xbf, 0x57, 0xdb, 0xca, 0xc4, - 0xee, 0xb6, 0x32, 0x13, 0x3a, 0x60, 0x4a, 0x50, 0x0f, 0x74, 0xe1, 0x27, 0xe0, 0xfa, 0x41, 0xf0, - 0x46, 0x01, 0xc6, 0xc1, 0x48, 0x71, 0x30, 0x70, 0x57, 0x02, 0xe7, 0x9a, 0xd4, 0x64, 0xc2, 0x4b, - 0x0e, 0x7a, 0x3f, 0x2e, 0x18, 0xe0, 0x10, 0x03, 0x47, 0x0b, 0xb9, 0x72, 0xfe, 0xdd, 0x91, 0x2d, - 0xb0, 0xc8, 0x7e, 0x7c, 0xa3, 0xcc, 0x9b, 0x96, 0xf7, 0xc4, 0x6f, 0xab, 0x1d, 0xd2, 0xd5, 0x38, - 0xe7, 0xc3, 0x7f, 0x15, 0x8a, 0xd6, 0x35, 0xef, 0x59, 0x0f, 0xd3, 0x40, 0x81, 0xea, 0xa1, 0xe5, - 0x77, 0xb1, 0xea, 0x0a, 0xab, 0x85, 0x4b, 0x51, 0x2d, 0xb0, 0xf0, 0x2a, 0x86, 0x83, 0x2a, 0x49, - 0xf4, 0xba, 0x15, 0xec, 0x76, 0x6a, 0xcc, 0x22, 0x6b, 0xc7, 0x40, 0xae, 0x51, 0xe7, 0x09, 0xcb, - 0x35, 0xea, 0xf0, 0x65, 0x0e, 0x68, 0x4d, 0x6a, 0x2e, 0xbb, 0xd8, 0xf0, 0xf0, 0x3d, 0xdf, 0xb6, - 0x75, 0xc3, 0x31, 0xf1, 0x03, 0x42, 0x2d, 0xd6, 0xbc, 0xfe, 0xdb, 0xf9, 0x93, 0xaf, 0x81, 0xe9, - 0x1e, 0x21, 0x36, 0x2b, 0x91, 0x49, 0x16, 0x71, 0x4d, 0xde, 0xdd, 0x56, 0x8e, 0x85, 0x48, 0xf9, - 0x02, 0xd4, 0xa7, 0xd8, 0x57, 0x03, 0x2d, 0x7e, 0xc8, 0x92, 0x0d, 0xa3, 0x64, 0xaf, 0xf9, 0xb6, - 0x5d, 0x71, 0x59, 0x2e, 0xc2, 0x94, 0xaf, 0xf5, 0x53, 0xfd, 0x14, 0xdc, 0xce, 0x98, 0x31, 0x91, - 0xfd, 0xd3, 0x20, 0x2c, 0xd2, 0xfa, 0x40, 0xc9, 0xd6, 0xe5, 0x12, 0x00, 0x3d, 0x6e, 0xa0, 0x51, - 0xe7, 0xdc, 0x8a, 0xcd, 0xb0, 0xbe, 0x5e, 0x68, 0x52, 0xf3, 0x91, 0xf3, 0x80, 0x10, 0xfb, 0xf3, - 0x27, 0x96, 0x87, 0x6d, 0x8b, 0x7a, 0x18, 0xb1, 0x61, 0x96, 0xed, 0x88, 0x25, 0x24, 0x37, 0x34, - 0x21, 0x97, 0x58, 0x42, 0x94, 0x28, 0x21, 0xbe, 0xc3, 0xa6, 0x2b, 0x9b, 0x7d, 0xe7, 0x15, 0x36, - 0x01, 0xef, 0x83, 0x72, 0x1a, 0x32, 0x11, 0xf6, 0x65, 0x70, 0x1c, 0x6f, 0x59, 0x1e, 0x46, 0x2d, - 0xce, 0x58, 0x5a, 0x90, 0xca, 0xf9, 0xf9, 0x49, 0x7d, 0x36, 0x9c, 0x5e, 0x09, 0x88, 0x4b, 0xe1, - 0x0f, 0x79, 0x70, 0x27, 0x30, 0x66, 0x87, 0x75, 0xdc, 0xb4, 0x4c, 0xd7, 0xf0, 0xf0, 0xc3, 0x27, - 0x86, 0x8b, 0xe9, 0x2a, 0x11, 0xc9, 0x5e, 0x26, 0x4e, 0x07, 0x3b, 0x1e, 0x5b, 0x43, 0x51, 0xe2, - 0x33, 0xa6, 0x21, 0xde, 0xc7, 0xf2, 0xf1, 0x34, 0xf0, 0x05, 0x28, 0x7a, 0x9b, 0x09, 0xe6, 0x68, - 0x00, 0xa0, 0xe5, 0x91, 0x56, 0x37, 0x44, 0x34, 0xbc, 0xd1, 0x95, 0x79, 0xa3, 0x2b, 0x70, 0x04, - 0x7b, 0x2d, 0x40, 0xfd, 0x38, 0xe5, 0x61, 0xf1, 0x28, 0xe5, 0xe7, 0x12, 0x38, 0xe6, 0x91, 0x75, - 0xec, 0xb4, 0x88, 0xef, 0xb5, 0xba, 0x8c, 0x35, 0x93, 0xc3, 0x58, 0xd3, 0xe0, 0x6e, 0x4e, 0x85, - 0x6e, 0x06, 0xd5, 0x61, 0x26, 0x3a, 0x1d, 0x0d, 0x94, 0x3f, 0xf3, 0xbd, 0xa6, 0xe5, 0xd0, 0x45, - 0x85, 0x6d, 0x7e, 0xb1, 0xbf, 0xf9, 0xa2, 0xf9, 0x44, 0xf8, 0x7f, 0xcd, 0x83, 0xbb, 0xa3, 0xee, - 0x95, 0x28, 0x8c, 0xc7, 0x60, 0xda, 0xe8, 0x12, 0xdf, 0xf1, 0x16, 0xf8, 0xa6, 0xdd, 0x65, 0xf1, - 0xfc, 0xbe, 0xad, 0x5c, 0x3e, 0x00, 0xec, 0x86, 0xe3, 0xf5, 0xb7, 0x8d, 0x9b, 0x81, 0x7a, 0x64, - 0xb0, 0x6f, 0xbb, 0x1a, 0x6c, 0xf2, 0x7b, 0xdb, 0xae, 0x0a, 0xdb, 0x55, 0x79, 0x13, 0xcc, 0xd9, - 0xd6, 0x53, 0xdf, 0x42, 0x96, 0xf7, 0xac, 0xd5, 0x09, 0x3a, 0x01, 0x0a, 0x9b, 0x4f, 0xed, 0x7e, - 0x06, 0x2f, 0x75, 0xdc, 0xe9, 0x97, 0xc8, 0x3e, 0x83, 0x50, 0x3f, 0x21, 0xe6, 0xc2, 0x6e, 0x83, - 0xe4, 0x47, 0xe0, 0xc8, 0x17, 0xc4, 0x72, 0x5a, 0xec, 0x76, 0x18, 0xf4, 0xb4, 0x99, 0x1b, 0x45, - 0x35, 0xbc, 0x3a, 0xaa, 0xd1, 0xd5, 0x51, 0x5d, 0x8d, 0xae, 0x8e, 0xb5, 0x73, 0xbc, 0x3c, 0x4e, - 0x84, 0x2e, 0x84, 0x2a, 0x7c, 0xf1, 0x46, 0x91, 0xf4, 0xc3, 0x6c, 0xcc, 0x84, 0xe1, 0x37, 0xf9, - 0xe0, 0x14, 0x58, 0x42, 0x68, 0x95, 0xc4, 0x37, 0x6c, 0x25, 0xf2, 0xdf, 0xef, 0x69, 0x82, 0x6f, - 0xb7, 0xc1, 0x4c, 0xd4, 0xa1, 0xc4, 0x19, 0x5c, 0x3b, 0xbd, 0xbb, 0xad, 0xc8, 0x51, 0x3f, 0x11, - 0x8b, 0x30, 0xd6, 0xcc, 0x50, 0x8c, 0xa8, 0xb9, 0x61, 0x44, 0x6d, 0x45, 0x8c, 0x40, 0x98, 0x5a, - 0x2e, 0x46, 0x0b, 0xc3, 0x89, 0x77, 0x3e, 0x89, 0x11, 0x91, 0x3a, 0xd4, 0x67, 0x83, 0x89, 0x3a, - 0x1f, 0xef, 0x73, 0x50, 0xe5, 0x49, 0x1d, 0xd1, 0x41, 0x75, 0x8f, 0x83, 0xea, 0xe2, 0x55, 0xc6, - 0xa3, 0x0f, 0x22, 0x1e, 0x19, 0x08, 0x55, 0x3c, 0x52, 0xe9, 0xd8, 0xf1, 0x33, 0x3c, 0x4a, 0x0d, - 0xfc, 0x25, 0x1f, 0x9c, 0x2c, 0x59, 0x76, 0x41, 0x30, 0x69, 0xe4, 0xdd, 0x88, 0x51, 0x30, 0xf7, - 0x2f, 0x52, 0x30, 0x3f, 0x6e, 0x0a, 0xae, 0x83, 0x59, 0x07, 0x6f, 0xb6, 0x04, 0x43, 0x0a, 0x87, - 0x02, 0x0f, 0xf7, 0x32, 0xd3, 0xef, 0x64, 0xe8, 0x61, 0xc0, 0x18, 0xd4, 0x8f, 0x3a, 0x78, 0x53, - 0xe4, 0x3d, 0x7e, 0x60, 0xec, 0xbb, 0x48, 0xec, 0x3d, 0x30, 0x6e, 0xfc, 0x0d, 0x40, 0xbe, 0x49, - 0x4d, 0xd9, 0x05, 0x72, 0xd2, 0xe5, 0x49, 0xdd, 0xff, 0xcc, 0x54, 0x13, 0x5f, 0x46, 0xc5, 0xea, - 0x81, 0x45, 0x45, 0x19, 0x6c, 0x81, 0x93, 0x89, 0x0f, 0xa8, 0x6b, 0x43, 0x4d, 0xf5, 0x85, 0x8b, - 0x37, 0x33, 0x08, 0xa7, 0x79, 0x16, 0xcf, 0x8b, 0x83, 0x78, 0x8e, 0x84, 0x0f, 0xe4, 0x79, 0xdf, - 0x43, 0xe0, 0x7b, 0x09, 0x5c, 0x18, 0xfe, 0xcc, 0xb9, 0x93, 0x21, 0xa8, 0x01, 0xcd, 0xe2, 0xdd, - 0x51, 0x35, 0x05, 0xc2, 0x6f, 0x25, 0x70, 0x36, 0xfd, 0x39, 0xb2, 0x90, 0x62, 0x3f, 0x55, 0xa3, - 0x78, 0x27, 0xab, 0x86, 0x40, 0xf2, 0xb3, 0x04, 0xae, 0x67, 0xba, 0xeb, 0x2f, 0xa7, 0xb8, 0xca, - 0x62, 0xa4, 0xf8, 0xe9, 0x18, 0x8c, 0x88, 0x10, 0xbe, 0x04, 0xa7, 0x92, 0xef, 0xc1, 0xd7, 0x53, - 0xbc, 0x24, 0x4a, 0x17, 0x3f, 0xca, 0x22, 0x2d, 0x9c, 0xff, 0x21, 0x81, 0x8f, 0x47, 0xbb, 0x9e, - 0xae, 0xa4, 0xfa, 0x1b, 0xc1, 0x5a, 0x71, 0x75, 0x9c, 0xd6, 0x06, 0xaa, 0x23, 0xd3, 0x1d, 0x20, - 0xad, 0x3a, 0xb2, 0x18, 0x49, 0xad, 0x8e, 0x51, 0xce, 0xc1, 0xda, 0x83, 0x57, 0x6f, 0x4b, 0xd2, - 0xeb, 0xb7, 0x25, 0xe9, 0xcf, 0xb7, 0x25, 0xe9, 0xc5, 0x4e, 0x69, 0xe2, 0xf5, 0x4e, 0x69, 0xe2, - 0xb7, 0x9d, 0xd2, 0xc4, 0xe3, 0x5b, 0xb1, 0x13, 0x81, 0x3b, 0xac, 0xd8, 0x46, 0x9b, 0x46, 0x03, - 0x6d, 0xa3, 0x7a, 0x5b, 0xdb, 0x1a, 0xf8, 0x65, 0x90, 0x9d, 0x12, 0xed, 0xa9, 0xe0, 0x5e, 0x75, - 0xf3, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x81, 0x35, 0xa1, 0x3c, 0x14, 0x00, 0x00, + // 1532 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcf, 0x6f, 0x13, 0xc7, + 0x17, 0xcf, 0xda, 0x21, 0x81, 0x09, 0x09, 0xc9, 0x7e, 0x81, 0x18, 0x03, 0xb6, 0x19, 0x7e, 0x7c, + 0xc3, 0x0f, 0x7b, 0x63, 0x68, 0x01, 0xe5, 0x44, 0x1c, 0x8b, 0x2a, 0x34, 0x51, 0xe9, 0x12, 0x54, + 0x89, 0x8b, 0xb5, 0xf1, 0x4c, 0x96, 0x6d, 0x76, 0x77, 0xc2, 0xce, 0x38, 0x09, 0xea, 0xa9, 0xe5, + 0x50, 0x89, 0x13, 0xc7, 0xde, 0x7a, 0x6e, 0x0f, 0x88, 0x3f, 0xa1, 0x87, 0x1e, 0x50, 0x4f, 0x1c, + 0xab, 0x56, 0x0a, 0x15, 0x1c, 0x2a, 0xf5, 0x98, 0x4b, 0xaf, 0xd5, 0xec, 0xcc, 0xae, 0xd7, 0xf6, + 0x6e, 0x9c, 0x35, 0xe9, 0xa1, 0x17, 0xf0, 0xce, 0xbc, 0xf9, 0xbc, 0xcf, 0x7b, 0xf3, 0x3e, 0xf3, + 0x66, 0x02, 0x4e, 0x13, 0xea, 0x10, 0x6a, 0x51, 0x8d, 0xb6, 0x36, 0xb0, 0xb7, 0x66, 0xb7, 0x2c, + 0xa4, 0xb1, 0xed, 0xca, 0x86, 0x47, 0x18, 0x51, 0x55, 0x39, 0x59, 0x69, 0x4f, 0xe6, 0x8f, 0x9b, + 0xc4, 0x24, 0xfe, 0xb4, 0xc6, 0x7f, 0x09, 0xcb, 0xfc, 0x94, 0xe1, 0x58, 0x2e, 0xd1, 0xfc, 0x7f, + 0xe5, 0x50, 0xc1, 0x24, 0xc4, 0xb4, 0xb1, 0xe6, 0x7f, 0xad, 0xb6, 0xd6, 0x34, 0xd4, 0xf2, 0x0c, + 0x66, 0x11, 0x37, 0x98, 0x6f, 0xfa, 0xe8, 0xda, 0xaa, 0x41, 0xb1, 0xb6, 0x59, 0x5d, 0xc5, 0xcc, + 0xa8, 0x6a, 0x4d, 0x62, 0x05, 0xf3, 0xc5, 0xee, 0xf5, 0xcc, 0x72, 0x30, 0x65, 0x86, 0xb3, 0x21, + 0x0d, 0xce, 0xc7, 0x50, 0x6f, 0xff, 0x14, 0x46, 0xf0, 0x3b, 0x05, 0x9c, 0x58, 0xa6, 0xe6, 0x83, + 0x70, 0xbc, 0x8e, 0x6d, 0x6c, 0x1a, 0x0c, 0xab, 0x97, 0xc1, 0x08, 0xc5, 0x2e, 0xc2, 0x5e, 0x4e, + 0x29, 0x29, 0x33, 0x47, 0x6a, 0x53, 0xbb, 0x3b, 0xc5, 0xf1, 0xa7, 0x86, 0x63, 0xcf, 0x41, 0x31, + 0x0e, 0x75, 0x69, 0xa0, 0x4e, 0x83, 0x51, 0x9b, 0x34, 0xd7, 0x1b, 0x16, 0xca, 0x65, 0x4a, 0xca, + 0xcc, 0xb0, 0x3e, 0xc2, 0x3f, 0x17, 0x91, 0x7a, 0x0a, 0x1c, 0xde, 0x34, 0xec, 0x86, 0x81, 0x90, + 0x97, 0xcb, 0x72, 0x14, 0x7d, 0x74, 0xd3, 0xb0, 0xe7, 0x11, 0xf2, 0xe6, 0x4a, 0xcf, 0xff, 0x7c, + 0x75, 0x25, 0x26, 0xbb, 0x65, 0x24, 0x09, 0xc0, 0x22, 0x38, 0x1b, 0xcb, 0x4c, 0xc7, 0x74, 0x83, + 0xb8, 0x14, 0xc3, 0xaf, 0x15, 0x30, 0xdd, 0x61, 0xf1, 0xd0, 0x45, 0x07, 0xc8, 0x7e, 0x0e, 0x72, + 0x8a, 0x67, 0x63, 0x28, 0xb6, 0x42, 0x3f, 0xf0, 0x1c, 0x28, 0x26, 0x50, 0x08, 0x69, 0x7e, 0xd3, + 0x4b, 0x73, 0x95, 0xb8, 0x68, 0x89, 0x34, 0xd7, 0x0f, 0x84, 0xe6, 0x79, 0x4e, 0xb3, 0x10, 0x4b, + 0x93, 0xfb, 0x29, 0x73, 0xb3, 0x18, 0x9e, 0x01, 0x87, 0x90, 0xe7, 0x4b, 0x05, 0x5c, 0x48, 0x88, + 0x65, 0xde, 0x3d, 0x60, 0xd2, 0x6a, 0x0d, 0x0c, 0xf3, 0x5a, 0xf6, 0xab, 0x62, 0xec, 0xfa, 0xa9, + 0x8a, 0x28, 0xf6, 0x0a, 0x2f, 0xf6, 0x8a, 0x2c, 0xf6, 0xca, 0x02, 0xb1, 0xdc, 0xda, 0xff, 0x5e, + 0xef, 0x14, 0x87, 0x76, 0x77, 0x8a, 0x63, 0xc2, 0x01, 0x5f, 0x04, 0x75, 0x7f, 0x2d, 0xfc, 0x04, + 0x5c, 0xdb, 0x0f, 0xdf, 0x20, 0xc0, 0x28, 0x19, 0x25, 0x4a, 0x06, 0xee, 0x2a, 0xe0, 0xcc, 0x32, + 0x35, 0xb9, 0xf1, 0xbc, 0x8b, 0x3e, 0x4c, 0x0b, 0x06, 0x38, 0xc4, 0xc9, 0xd1, 0x5c, 0xa6, 0x94, + 0xdd, 0x3b, 0xb2, 0x59, 0x1e, 0xd9, 0x8f, 0x6f, 0x8b, 0x33, 0xa6, 0xc5, 0x1e, 0xb7, 0x56, 0x2b, + 0x4d, 0xe2, 0x68, 0x52, 0xf3, 0xe2, 0xbf, 0x32, 0x45, 0xeb, 0x1a, 0x7b, 0xba, 0x81, 0xa9, 0xbf, + 0x80, 0xea, 0x02, 0x79, 0x2f, 0x55, 0x5d, 0xe6, 0xb5, 0x70, 0x21, 0xa8, 0x05, 0x1e, 0x5e, 0xd9, + 0x70, 0x51, 0x39, 0x4e, 0x5e, 0x37, 0xfd, 0xdd, 0x4e, 0x8c, 0x39, 0xcc, 0xda, 0x04, 0xc8, 0x2c, + 0xd6, 0x65, 0xc2, 0x32, 0x8b, 0x75, 0xf8, 0x2a, 0x03, 0xb4, 0x65, 0x6a, 0x2e, 0x78, 0xd8, 0x60, + 0xf8, 0x6e, 0xcb, 0xb6, 0x75, 0xc3, 0x35, 0xf1, 0x7d, 0x42, 0x2d, 0x7e, 0x78, 0xfd, 0xb7, 0xf3, + 0xa7, 0x5e, 0x05, 0xa3, 0x1b, 0x84, 0xd8, 0xbc, 0x44, 0x86, 0x79, 0xc4, 0x35, 0x75, 0x77, 0xa7, + 0x38, 0x21, 0x98, 0xca, 0x09, 0xa8, 0x8f, 0xf0, 0x5f, 0x8b, 0x68, 0xee, 0xff, 0x3c, 0xd9, 0x30, + 0x48, 0xf6, 0x5a, 0xcb, 0xb6, 0xcb, 0x1e, 0xcf, 0x85, 0x48, 0xf9, 0x5a, 0x3b, 0xd5, 0x4f, 0xc0, + 0xad, 0x94, 0x19, 0x0b, 0xb3, 0x7f, 0x12, 0x88, 0x22, 0xad, 0x77, 0x94, 0x6c, 0x5d, 0x2d, 0x00, + 0xb0, 0x21, 0x01, 0x16, 0xeb, 0x52, 0x5b, 0x91, 0x11, 0x7e, 0xae, 0xe7, 0x96, 0xa9, 0xf9, 0xd0, + 0xbd, 0x4f, 0x88, 0xfd, 0xc5, 0x63, 0x8b, 0x61, 0xdb, 0xa2, 0x0c, 0x23, 0xfe, 0x99, 0x66, 0x3b, + 0x22, 0x09, 0xc9, 0xf4, 0x4d, 0xc8, 0x05, 0x9e, 0x90, 0x62, 0x90, 0x90, 0x96, 0xcb, 0x87, 0xcb, + 0x5b, 0x6d, 0xe7, 0x65, 0x3e, 0x00, 0xef, 0x81, 0x52, 0x12, 0xb3, 0x30, 0xec, 0x4b, 0xe0, 0x18, + 0xde, 0xb6, 0x18, 0x46, 0x0d, 0xa9, 0x58, 0x9a, 0x53, 0x4a, 0xd9, 0x99, 0x61, 0x7d, 0x5c, 0x0c, + 0x2f, 0xf9, 0xc2, 0xa5, 0xf0, 0x87, 0x2c, 0xb8, 0xed, 0x83, 0xd9, 0xa2, 0x8e, 0x97, 0x2d, 0xd3, + 0x33, 0x18, 0x7e, 0xf0, 0xd8, 0xf0, 0x30, 0x5d, 0x21, 0x61, 0xb2, 0x17, 0x88, 0xdb, 0xc4, 0x2e, + 0xe3, 0x73, 0x28, 0x48, 0x7c, 0xca, 0x34, 0x44, 0xcf, 0xb1, 0x6c, 0x34, 0x0d, 0x72, 0x02, 0x86, + 0x67, 0x9b, 0x09, 0xa6, 0xa8, 0x4f, 0xa0, 0xc1, 0x48, 0xc3, 0x11, 0x8c, 0xfa, 0x1f, 0x74, 0x25, + 0x79, 0xd0, 0xe5, 0x24, 0x83, 0x6e, 0x04, 0xa8, 0x1f, 0xa3, 0x32, 0x2c, 0x19, 0xa5, 0xfa, 0x5c, + 0x01, 0x13, 0x8c, 0xac, 0x63, 0xb7, 0x41, 0x5a, 0xac, 0xe1, 0x70, 0xd5, 0x0c, 0xf7, 0x53, 0xcd, + 0xa2, 0x74, 0x73, 0x42, 0xb8, 0xe9, 0x5c, 0x0e, 0x53, 0xc9, 0xe9, 0xa8, 0xbf, 0xf8, 0xb3, 0x16, + 0x5b, 0xb6, 0x5c, 0x3a, 0x57, 0xe4, 0x9b, 0x9f, 0x6f, 0x6f, 0x7e, 0x78, 0xf8, 0x04, 0xfc, 0x7f, + 0xc9, 0x82, 0x3b, 0x83, 0xee, 0x55, 0x58, 0x18, 0x8f, 0xc0, 0xa8, 0xe1, 0x90, 0x96, 0xcb, 0x66, + 0xe5, 0xa6, 0xdd, 0xe1, 0xf1, 0xfc, 0xb6, 0x53, 0xbc, 0xb4, 0x0f, 0xda, 0x8b, 0x2e, 0x6b, 0x6f, + 0x9b, 0x84, 0x81, 0x7a, 0x00, 0xd8, 0xc6, 0xae, 0xfa, 0x9b, 0xfc, 0xc1, 0xd8, 0xd5, 0x10, 0xbb, + 0xaa, 0x6e, 0x81, 0x29, 0xdb, 0x7a, 0xd2, 0xb2, 0x90, 0xc5, 0x9e, 0x36, 0x9a, 0xfe, 0x49, 0x80, + 0xc4, 0xe1, 0x53, 0xbb, 0x97, 0xc2, 0x4b, 0x1d, 0x37, 0xdb, 0x25, 0xd2, 0x03, 0x08, 0xf5, 0xc9, + 0x70, 0x4c, 0x9c, 0x36, 0x48, 0x7d, 0x08, 0x8e, 0x7c, 0x49, 0x2c, 0xb7, 0xc1, 0x6f, 0x87, 0xfe, + 0x99, 0x36, 0x76, 0x3d, 0x5f, 0x11, 0x57, 0xc7, 0x4a, 0x70, 0x75, 0xac, 0xac, 0x04, 0x57, 0xc7, + 0xda, 0x19, 0x59, 0x1e, 0x93, 0xc2, 0x45, 0xb8, 0x14, 0xbe, 0x78, 0x5b, 0x54, 0xf4, 0xc3, 0xfc, + 0x9b, 0x1b, 0xc3, 0x67, 0x59, 0xbf, 0x0b, 0xcc, 0x23, 0xb4, 0x42, 0xa2, 0x1b, 0xb6, 0x14, 0xf8, + 0x6f, 0x9f, 0x69, 0xa1, 0xde, 0x6e, 0x81, 0xb1, 0xe0, 0x84, 0x0a, 0x7b, 0x70, 0xed, 0xe4, 0xee, + 0x4e, 0x51, 0x0d, 0xce, 0x93, 0x70, 0x12, 0x46, 0x0e, 0x33, 0x14, 0x11, 0x6a, 0xa6, 0x9f, 0x50, + 0x1b, 0x81, 0x22, 0x10, 0xa6, 0x96, 0x87, 0xd1, 0x6c, 0x7f, 0xe1, 0x9d, 0x8d, 0x53, 0x44, 0xb0, + 0x1c, 0xea, 0xe3, 0xfe, 0x40, 0x5d, 0x7e, 0xf7, 0x38, 0xa8, 0xca, 0xa4, 0x0e, 0xe8, 0xa0, 0xda, + 0xe5, 0xa0, 0x3a, 0x77, 0x85, 0xeb, 0xe8, 0x62, 0xa0, 0x23, 0x03, 0xa1, 0x32, 0x23, 0xe5, 0xa6, + 0x1d, 0xed, 0xe1, 0x41, 0x6a, 0xe0, 0xcf, 0x59, 0xbf, 0xb3, 0xa4, 0xd9, 0x85, 0x50, 0x49, 0x03, + 0xef, 0x46, 0x44, 0x82, 0x99, 0x7f, 0x51, 0x82, 0xd9, 0x83, 0x96, 0xe0, 0x3a, 0x18, 0x77, 0xf1, + 0x56, 0x23, 0x54, 0x48, 0xee, 0x90, 0xef, 0xe1, 0x6e, 0x6a, 0xf9, 0x1d, 0x17, 0x1e, 0x3a, 0xc0, + 0xa0, 0x7e, 0xd4, 0xc5, 0x5b, 0x61, 0xde, 0xa3, 0x0d, 0xa3, 0xe7, 0x22, 0xd1, 0xdd, 0x30, 0xe0, + 0xcb, 0xac, 0x6c, 0xd6, 0xfc, 0xca, 0xba, 0x40, 0xdc, 0x4d, 0xec, 0x31, 0x7e, 0x2d, 0x60, 0xc6, + 0x3a, 0x8e, 0x22, 0x29, 0xfd, 0x90, 0xd2, 0x28, 0x65, 0x8f, 0x5b, 0x90, 0x07, 0x26, 0x1d, 0xcb, + 0x6d, 0x18, 0x0e, 0xe3, 0xfd, 0x87, 0x72, 0x1a, 0x7e, 0x14, 0x47, 0x44, 0xf7, 0x48, 0xb5, 0x1d, + 0xd3, 0xc2, 0x7b, 0x37, 0x1e, 0xd4, 0xc7, 0x1d, 0xcb, 0x9d, 0x77, 0xd8, 0x0a, 0x11, 0x61, 0x3e, + 0x53, 0xc0, 0xe9, 0x76, 0xcf, 0x6b, 0x8a, 0x24, 0x34, 0x0c, 0x17, 0x49, 0xff, 0x87, 0xfa, 0xa9, + 0xec, 0x8a, 0x54, 0x19, 0xec, 0xee, 0x9f, 0x3d, 0x58, 0x50, 0x9f, 0x0e, 0x3a, 0x69, 0x57, 0xb2, + 0xe7, 0x2e, 0x72, 0xf1, 0x95, 0xda, 0x4d, 0xcc, 0x7f, 0x40, 0x49, 0x00, 0x71, 0xad, 0xf3, 0x01, + 0xfe, 0x52, 0xe4, 0x1d, 0x26, 0x66, 0xc3, 0x42, 0x81, 0x51, 0x30, 0xc9, 0x08, 0xe3, 0x29, 0x76, + 0x98, 0x70, 0x8c, 0x64, 0xcf, 0x1a, 0x38, 0x8b, 0xdd, 0x78, 0x50, 0x9f, 0xf0, 0x87, 0xe6, 0x1d, + 0xe6, 0xfb, 0x46, 0xea, 0xe7, 0xe0, 0xa8, 0x30, 0x12, 0x11, 0xca, 0x32, 0xa8, 0xa4, 0xab, 0x71, + 0x7d, 0xcc, 0xc7, 0x10, 0x9d, 0xf9, 0xfa, 0xdf, 0x63, 0x20, 0xbb, 0x4c, 0x4d, 0xd5, 0x03, 0x6a, + 0xdc, 0xd5, 0xbe, 0xd2, 0xfb, 0x47, 0x90, 0x4a, 0xec, 0xbb, 0x3d, 0x5f, 0xdd, 0xb7, 0x69, 0x98, + 0xc3, 0x6d, 0x70, 0x3c, 0xf6, 0x79, 0x7f, 0xb5, 0x2f, 0x54, 0xdb, 0x38, 0x7f, 0x23, 0x85, 0x71, + 0x92, 0xe7, 0xf0, 0xf1, 0xbb, 0x1f, 0xcf, 0x81, 0xf1, 0xbe, 0x3c, 0xf7, 0x3c, 0x53, 0xbf, 0x57, + 0xc0, 0xb9, 0xfe, 0x8f, 0xf0, 0xdb, 0x29, 0x82, 0xea, 0x58, 0x99, 0xbf, 0x33, 0xe8, 0xca, 0x90, + 0xe1, 0xb7, 0x0a, 0x38, 0x95, 0xfc, 0x58, 0x9e, 0x4d, 0xc0, 0x4f, 0x5c, 0x91, 0xbf, 0x9d, 0x76, + 0x45, 0xc8, 0xe4, 0x27, 0x05, 0x5c, 0x4b, 0xf5, 0x12, 0x5d, 0x48, 0x70, 0x95, 0x06, 0x24, 0xff, + 0xe9, 0x01, 0x80, 0x84, 0x21, 0x7c, 0x05, 0x4e, 0xc4, 0xbf, 0xd2, 0xae, 0x25, 0x78, 0x89, 0xb5, + 0xce, 0x7f, 0x94, 0xc6, 0x3a, 0x74, 0xfe, 0xbb, 0x02, 0x3e, 0x1e, 0xec, 0xf1, 0xb4, 0x94, 0xe8, + 0x6f, 0x00, 0xb4, 0xfc, 0xca, 0x41, 0xa2, 0x75, 0x54, 0x47, 0xaa, 0x1b, 0x6a, 0x52, 0x75, 0xa4, + 0x01, 0x49, 0xac, 0x8e, 0x81, 0x6e, 0x69, 0x7e, 0x75, 0xc4, 0x5d, 0x0b, 0x92, 0xab, 0x23, 0xc6, + 0x7a, 0x8f, 0xea, 0xd8, 0xa3, 0x83, 0xd5, 0xee, 0xbf, 0x7e, 0x57, 0x50, 0xde, 0xbc, 0x2b, 0x28, + 0x7f, 0xbc, 0x2b, 0x28, 0x2f, 0xde, 0x17, 0x86, 0xde, 0xbc, 0x2f, 0x0c, 0xfd, 0xfa, 0xbe, 0x30, + 0xf4, 0xe8, 0x66, 0xa4, 0x91, 0x48, 0xe4, 0xb2, 0x6d, 0xac, 0xd2, 0xe0, 0x43, 0xdb, 0xac, 0xde, + 0xd2, 0xb6, 0x3b, 0xfe, 0x68, 0xce, 0x9b, 0xcb, 0xea, 0x88, 0xff, 0xe4, 0xb8, 0xf1, 0x4f, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xb1, 0x31, 0x01, 0x92, 0x57, 0x17, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1159,6 +1282,9 @@ type MsgClient interface { UnPoolWhitelistedPool(ctx context.Context, in *MsgUnPoolWhitelistedPool, opts ...grpc.CallOption) (*MsgUnPoolWhitelistedPoolResponse, error) UnlockAndMigrateSharesToFullRangeConcentratedPosition(ctx context.Context, in *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition, opts ...grpc.CallOption) (*MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse, error) AddToConcentratedLiquiditySuperfluidPosition(ctx context.Context, in *MsgAddToConcentratedLiquiditySuperfluidPosition, opts ...grpc.CallOption) (*MsgAddToConcentratedLiquiditySuperfluidPositionResponse, error) + // UnbondConvertAndStake breaks all locks / superfluid staked assets, + // converts them to osmo then stakes the osmo to the designated validator. + UnbondConvertAndStake(ctx context.Context, in *MsgUnbondConvertAndStake, opts ...grpc.CallOption) (*MsgUnbondConvertAndStakeResponse, error) } type msgClient struct { @@ -1250,6 +1376,15 @@ func (c *msgClient) AddToConcentratedLiquiditySuperfluidPosition(ctx context.Con return out, nil } +func (c *msgClient) UnbondConvertAndStake(ctx context.Context, in *MsgUnbondConvertAndStake, opts ...grpc.CallOption) (*MsgUnbondConvertAndStakeResponse, error) { + out := new(MsgUnbondConvertAndStakeResponse) + err := c.cc.Invoke(ctx, "/osmosis.superfluid.Msg/UnbondConvertAndStake", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { // Execute superfluid delegation for a lockup @@ -1267,6 +1402,9 @@ type MsgServer interface { UnPoolWhitelistedPool(context.Context, *MsgUnPoolWhitelistedPool) (*MsgUnPoolWhitelistedPoolResponse, error) UnlockAndMigrateSharesToFullRangeConcentratedPosition(context.Context, *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) (*MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse, error) AddToConcentratedLiquiditySuperfluidPosition(context.Context, *MsgAddToConcentratedLiquiditySuperfluidPosition) (*MsgAddToConcentratedLiquiditySuperfluidPositionResponse, error) + // UnbondConvertAndStake breaks all locks / superfluid staked assets, + // converts them to osmo then stakes the osmo to the designated validator. + UnbondConvertAndStake(context.Context, *MsgUnbondConvertAndStake) (*MsgUnbondConvertAndStakeResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -1300,6 +1438,9 @@ func (*UnimplementedMsgServer) UnlockAndMigrateSharesToFullRangeConcentratedPosi func (*UnimplementedMsgServer) AddToConcentratedLiquiditySuperfluidPosition(ctx context.Context, req *MsgAddToConcentratedLiquiditySuperfluidPosition) (*MsgAddToConcentratedLiquiditySuperfluidPositionResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddToConcentratedLiquiditySuperfluidPosition not implemented") } +func (*UnimplementedMsgServer) UnbondConvertAndStake(ctx context.Context, req *MsgUnbondConvertAndStake) (*MsgUnbondConvertAndStakeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnbondConvertAndStake not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -1467,6 +1608,24 @@ func _Msg_AddToConcentratedLiquiditySuperfluidPosition_Handler(srv interface{}, return interceptor(ctx, in, info, handler) } +func _Msg_UnbondConvertAndStake_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUnbondConvertAndStake) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UnbondConvertAndStake(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/osmosis.superfluid.Msg/UnbondConvertAndStake", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UnbondConvertAndStake(ctx, req.(*MsgUnbondConvertAndStake)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "osmosis.superfluid.Msg", HandlerType: (*MsgServer)(nil), @@ -1507,6 +1666,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "AddToConcentratedLiquiditySuperfluidPosition", Handler: _Msg_AddToConcentratedLiquiditySuperfluidPosition_Handler, }, + { + MethodName: "UnbondConvertAndStake", + Handler: _Msg_UnbondConvertAndStake_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "osmosis/superfluid/tx.proto", @@ -2248,6 +2411,111 @@ func (m *MsgAddToConcentratedLiquiditySuperfluidPositionResponse) MarshalToSized return len(dAtA) - i, nil } +func (m *MsgUnbondConvertAndStake) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUnbondConvertAndStake) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUnbondConvertAndStake) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.SharesToConvertAndStake.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + { + size := m.MinAmtToStake.Size() + i -= size + if _, err := m.MinAmtToStake.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ValAddr) > 0 { + i -= len(m.ValAddr) + copy(dAtA[i:], m.ValAddr) + i = encodeVarintTx(dAtA, i, uint64(len(m.ValAddr))) + i-- + dAtA[i] = 0x1a + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x12 + } + if m.LockId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.LockId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgUnbondConvertAndStakeResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUnbondConvertAndStakeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUnbondConvertAndStakeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.TotalShares.Size() + i -= size + if _, err := m.TotalShares.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + { + size := m.TotalAmtStaked.Size() + i -= size + if _, err := m.TotalAmtStaked.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -2558,6 +2826,43 @@ func (m *MsgAddToConcentratedLiquiditySuperfluidPositionResponse) Size() (n int) return n } +func (m *MsgUnbondConvertAndStake) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.LockId != 0 { + n += 1 + sovTx(uint64(m.LockId)) + } + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ValAddr) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.MinAmtToStake.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.SharesToConvertAndStake.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUnbondConvertAndStakeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.TotalAmtStaked.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.TotalShares.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -4661,6 +4966,324 @@ func (m *MsgAddToConcentratedLiquiditySuperfluidPositionResponse) Unmarshal(dAtA } return nil } +func (m *MsgUnbondConvertAndStake) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUnbondConvertAndStake: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUnbondConvertAndStake: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field LockId", wireType) + } + m.LockId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.LockId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MinAmtToStake", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.MinAmtToStake.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SharesToConvertAndStake", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SharesToConvertAndStake.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUnbondConvertAndStakeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUnbondConvertAndStakeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUnbondConvertAndStakeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalAmtStaked", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TotalAmtStaked.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalShares", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.TotalShares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 From 25262680aa229e99742870476d1d556aaa946087 Mon Sep 17 00:00:00 2001 From: mattverse Date: Thu, 3 Aug 2023 23:57:20 +0900 Subject: [PATCH 02/27] Almost done, need more test cases... --- .golangci.yml | 1 - app/keepers/keepers.go | 2 +- proto/osmosis/superfluid/tx.proto | 3 +- x/superfluid/keeper/export_test.go | 25 ++- x/superfluid/keeper/migrate.go | 36 ++-- x/superfluid/keeper/migrate_test.go | 9 +- x/superfluid/keeper/msg_server.go | 4 +- x/superfluid/keeper/stake.go | 94 ++++++---- x/superfluid/keeper/stake_test.go | 259 ++++++++++++++++++++++++++++ x/superfluid/types/codec.go | 2 + x/superfluid/types/msgs.go | 29 ++++ x/superfluid/types/tx.pb.go | 208 +++++++++++----------- 12 files changed, 507 insertions(+), 165 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 3e367e1e7ae..2be93b35685 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,7 +9,6 @@ linters: enable: - asciicheck - bidichk - - depguard - durationcheck - errcheck - errname diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 38709d660f2..2a8ecfda10c 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -388,7 +388,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.SuperfluidKeeper = superfluidkeeper.NewKeeper( appKeepers.keys[superfluidtypes.StoreKey], appKeepers.GetSubspace(superfluidtypes.ModuleName), *appKeepers.AccountKeeper, appKeepers.BankKeeper, appKeepers.StakingKeeper, appKeepers.DistrKeeper, appKeepers.EpochsKeeper, appKeepers.LockupKeeper, appKeepers.GAMMKeeper, appKeepers.IncentivesKeeper, - lockupkeeper.NewMsgServerImpl(appKeepers.LockupKeeper), appKeepers.ConcentratedLiquidityKeeper) + lockupkeeper.NewMsgServerImpl(appKeepers.LockupKeeper), appKeepers.ConcentratedLiquidityKeeper, appKeepers.PoolManagerKeeper) mintKeeper := mintkeeper.NewKeeper( appKeepers.keys[minttypes.StoreKey], diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index 51a8f88a307..7c72ae56a4a 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -242,6 +242,7 @@ message MsgAddToConcentratedLiquiditySuperfluidPositionResponse { message MsgUnbondConvertAndStake { option (amino.name) = "osmosis/unbond-convert-and-stake"; + // 0 for no lock. uint64 lock_id = 1 [ (gogoproto.moretags) = "yaml:\"lock_id\"" ]; string sender = 2 [ (gogoproto.moretags) = "yaml:\"sender\"" ]; string val_addr = 3; @@ -263,7 +264,7 @@ message MsgUnbondConvertAndStakeResponse { (gogoproto.moretags) = "yaml:\"total_amt_staked\"", (gogoproto.nullable) = false ]; - string total_shares = 2 [ + string total_shares_delegated = 2 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false ]; diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 4ff9c0366cc..6596d05dc1f 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -33,8 +33,8 @@ func (k Keeper) MigrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, return k.migrateNonSuperfluidLockBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, tokenOutMins) } -func (k Keeper) validateSharesToUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { - return k.validateSharesToUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins) +func (k Keeper) ForceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { + return k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins) } func (k Keeper) RouteMigration(ctx sdk.Context, sender sdk.AccAddress, lockId int64, sharesToMigrate sdk.Coin) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { @@ -60,3 +60,24 @@ func (k Keeper) GetExistingLockRemainingDuration(ctx sdk.Context, lock *lockupty func (k Keeper) DistributeSuperfluidGauges(ctx sdk.Context) { k.distributeSuperfluidGauges(ctx) } + +func (k Keeper) ConvertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, lockId uint64, + sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { + return k.convertLockToStake(ctx, sender, valAddr, lockId, sharesToStake, minAmtToStake) +} +func (k Keeper) ConvertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, + sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { + return k.convertUnlockedToStake(ctx, sender, valAddr, sharesToStake, minAmtToStake) +} + +func (k Keeper) ValidateUnbondConvertAndStake(ctx sdk.Context, sender sdk.AccAddress, sharesToMigrate sdk.Coin) (poolIdLeaving uint64, preMigrationLock *lockuptypes.PeriodLock, err error) { + return k.validateUnbondConvertAndStake(ctx, sender, sharesToMigrate) +} + +func (k Keeper) ConvertGammSharesToOsmoAndStake( + ctx sdk.Context, + sender sdk.AccAddress, valAddr string, + poolIdLeaving uint64, exitCoins sdk.Coins, minAmtToStake sdk.Int, +) (totalAmtCoverted sdk.Int, shares sdk.Dec, err error) { + return k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake) +} diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index e557c22f8e2..ef54b1f36be 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -97,9 +97,7 @@ func (k Keeper) migrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, // Superfluid undelegate the portion of shares the user is migrating from the superfluid delegated position. // If all shares are being migrated, this deletes the connection between the gamm lock and the intermediate account, deletes the synthetic lock, and burns the synthetic osmo. - intermediateAccount := types.SuperfluidIntermediaryAccount{} - - intermediateAccount, err = k.SuperfluidUndelegateToConcentratedPosition(ctx, sender.String(), originalLockId) + intermediateAccount, err := k.SuperfluidUndelegateToConcentratedPosition(ctx, sender.String(), originalLockId) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } @@ -107,11 +105,16 @@ func (k Keeper) migrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.validateSharesToUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } + // Defense in depth, ensuring we are returning exactly two coins. + if len(exitCoins) != 2 { + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, types.TwoTokenBalancerPoolError{NumberOfTokens: len(exitCoins)} + } + // Create a full range (min to max tick) concentrated liquidity position, lock it, and superfluid delegate it. positionId, amount0, amount1, liquidity, concentratedLockId, err = k.clk.CreateFullRangePositionLocked(ctx, poolIdEntering, sender, exitCoins, remainingLockTime) if err != nil { @@ -153,11 +156,16 @@ func (k Keeper) migrateSuperfluidUnbondingBalancerToConcentrated(ctx sdk.Context // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.validateSharesToUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } + // Defense in depth, ensuring we are returning exactly two coins. + if len(exitCoins) != 2 { + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, types.TwoTokenBalancerPoolError{NumberOfTokens: len(exitCoins)} + } + // Create a full range (min to max tick) concentrated liquidity position. positionId, amount0, amount1, liquidity, concentratedLockId, err = k.clk.CreateFullRangePositionUnlocking(ctx, poolIdEntering, sender, exitCoins, remainingLockTime) if err != nil { @@ -201,11 +209,16 @@ func (k Keeper) migrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.validateSharesToUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } + // Defense in depth, ensuring we are returning exactly two coins. + if len(exitCoins) != 2 { + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, types.TwoTokenBalancerPoolError{NumberOfTokens: len(exitCoins)} + } + // Create a new lock that is unlocking for the remaining time of the old lock. // Regardless of the previous lock's status, we create a new lock that is unlocking. // This is because locking without superfluid is pointless in the context of concentrated liquidity. @@ -293,7 +306,7 @@ func (k Keeper) validateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId return poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, nil } -// validateSharesToUnlockAndExitBalancerPool validates the unlocking and exiting of gamm LP tokens from the Balancer pool. It performs the following steps: +// forceUnlockAndExitBalancerPool validates the unlocking and exiting of gamm LP tokens from the Balancer pool. It performs the following steps: // // 1. Completes the unlocking process / deletes synthetic locks for the provided lock. // 2. If shares to migrate are not specified, all shares in the lock are migrated. @@ -301,7 +314,7 @@ func (k Keeper) validateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId // 4. Exits the position in the Balancer pool. // 5. Ensures that exactly two coins are returned. // 6. Any remaining shares that were not migrated are re-locked as a new lock for the remaining time on the lock. -func (k Keeper) validateSharesToUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { +func (k Keeper) forceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { // validateMigration ensures that the preMigrationLock contains coins of length 1. gammSharesInLock := lock.Coins[0] @@ -320,7 +333,7 @@ func (k Keeper) validateSharesToUnlockAndExitBalancerPool(ctx sdk.Context, sende return sdk.Coins{}, types.MigratePartialSharesError{SharesToMigrate: sharesToMigrate.Amount.String(), SharesInLock: gammSharesInLock.Amount.String()} } - // Force migrate, which breaks and deletes associated synthetic locks. + // Force migrate, which breaks and deletes associated synthetic locks (if exists). err = k.lk.ForceUnlock(ctx, *lock) if err != nil { return sdk.Coins{}, err @@ -332,10 +345,5 @@ func (k Keeper) validateSharesToUnlockAndExitBalancerPool(ctx sdk.Context, sende return sdk.Coins{}, err } - // Defense in depth, ensuring we are returning exactly two coins. - if len(exitCoins) != 2 { - return sdk.Coins{}, types.TwoTokenBalancerPoolError{NumberOfTokens: len(exitCoins)} - } - return exitCoins, nil } diff --git a/x/superfluid/keeper/migrate_test.go b/x/superfluid/keeper/migrate_test.go index 1a8ebf315d7..0d4fd50c25a 100644 --- a/x/superfluid/keeper/migrate_test.go +++ b/x/superfluid/keeper/migrate_test.go @@ -797,7 +797,7 @@ func (s *KeeperTestSuite) TestValidateMigration() { } } -func (s *KeeperTestSuite) TestvalidateSharesToUnlockAndExitBalancerPool() { +func (s *KeeperTestSuite) TestforceUnlockAndExitBalancerPool() { defaultJoinTime := s.Ctx.BlockTime() type sendTest struct { overwritePreMigrationLock bool @@ -831,11 +831,6 @@ func (s *KeeperTestSuite) TestvalidateSharesToUnlockAndExitBalancerPool() { overwritePoolId: true, expectedError: fmt.Errorf("pool with ID %d does not exist", 2), }, - "error: attempt to leave a pool that has more than two denoms": { - percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), - overwritePool: true, - expectedError: types.TwoTokenBalancerPoolError{NumberOfTokens: 4}, - }, "error: happy path (full shares), token out mins is more than exit coins": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), tokenOutMins: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(100000))), @@ -917,7 +912,7 @@ func (s *KeeperTestSuite) TestvalidateSharesToUnlockAndExitBalancerPool() { } // System under test - exitCoins, err := superfluidKeeper.validateSharesToUnlockAndExitBalancerPool(ctx, poolJoinAcc, balancerPooId, lock, coinsToMigrate, tc.tokenOutMins) + exitCoins, err := superfluidKeeper.ForceUnlockAndExitBalancerPool(ctx, poolJoinAcc, balancerPooId, lock, coinsToMigrate, tc.tokenOutMins) if tc.expectedError != nil { s.Require().Error(err) s.Require().ErrorContains(err, tc.expectedError.Error()) diff --git a/x/superfluid/keeper/msg_server.go b/x/superfluid/keeper/msg_server.go index f6b8fe04943..95a03291e42 100644 --- a/x/superfluid/keeper/msg_server.go +++ b/x/superfluid/keeper/msg_server.go @@ -246,10 +246,10 @@ func (server msgServer) AddToConcentratedLiquiditySuperfluidPosition(goCtx conte func (server msgServer) UnbondConvertAndStake(goCtx context.Context, msg *types.MsgUnbondConvertAndStake) (*types.MsgUnbondConvertAndStakeResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - err := server.keeper.UnbondConvertAndStake(ctx, msg.LockId, msg.Sender, msg.ValAddr, msg.TokenOutMins) + totalAmtConverted, totalSharesDelegated, err := server.keeper.UnbondConvertAndStake(ctx, msg.LockId, msg.Sender, msg.ValAddr, msg.MinAmtToStake, msg.SharesToConvertAndStake) if err != nil { return nil, err } - return &types.MsgUnbondConvertAndStakeResponse{}, nil + return &types.MsgUnbondConvertAndStakeResponse{TotalAmtStaked: totalAmtConverted, TotalSharesDelegated: totalSharesDelegated}, nil } diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index f301c169105..2f2172d690e 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -313,6 +313,7 @@ func (k Keeper) SuperfluidUndelegateToConcentratedPosition(ctx sdk.Context, send // partialUndelegateCommon acts similarly to undelegateCommon, but undelegates a partial amount of the lock's delegation rather than the full amount. The amount // that is undelegated is placed in a new lock. This function returns the intermediary account associated with the original lock ID as well as the new lock that was created. // An error is returned if the amount to undelegate is greater than the locked amount. +// nolint: unused func (k Keeper) partialUndelegateCommon(ctx sdk.Context, sender string, lockID uint64, amountToUndelegate sdk.Coin) (intermediaryAcc types.SuperfluidIntermediaryAccount, newlock *lockuptypes.PeriodLock, err error) { lock, err := k.lk.GetLockByID(ctx, lockID) if err != nil { @@ -659,59 +660,89 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn } } -func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, tokenOutMins sdk.Coins, sharesToConvertAndStake sdk.Coin) error { - lock, err := k.lk.GetLockByID(ctx, lockID) - if err != nil { - return err - } - +// UnbondConvertAndStake converts given lock to osmo and stakes it to given validator. +// Supports conversion of 1)superfluid bonded 2)superfluid undelegating 3)vanilla unlocking 4) unlocked locks. +func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, minAmtToStake sdk.Int, + sharesToConvertAndStake sdk.Coin) (totalAmtConverted sdk.Int, totalSharesDelegated sdk.Dec, err error) { senderAddr, err := sdk.AccAddressFromBech32(sender) if err != nil { - return err + return sdk.ZeroInt(), sdk.ZeroDec(), err } // use routeMigration method to check status of lock(either superfluid staked, superfluid unbonding, vanialla locked, unlocked) - synthLockBeforeMigration, migrationType, err := k.routeMigration(ctx, senderAddr, int64(lockID), sharesToConvertAndStake) + _, migrationType, err := k.routeMigration(ctx, senderAddr, int64(lockID), sharesToConvertAndStake) if err != nil { - return err + return sdk.ZeroInt(), sdk.ZeroDec(), err } - switch migrationType { - case SuperfluidBonded: - err = k.convertSuperfluidBondedToStakedOsmo(ctx, senderAddr, valAddr, lockID, sharesToConvertAndStake, tokenOutMins) + + // if superfluid bonded, first change it into superfluid undelegate to burn minted osmo and instantly undelegate. + if migrationType == MigrationType(0) { + _, err = k.undelegateCommon(ctx, sender, lockID) if err != nil { - return err + return sdk.ZeroInt(), sdk.ZeroDec(), err } - case SuperfluidUnbonding: - - // - case NonSuperfluid: - - case Unlocked: + } - default: - return fmt.Errorf("unsupported migration type") + if migrationType == MigrationType(0) || migrationType == MigrationType(1) || migrationType == MigrationType(2) { + totalAmtConverted, totalSharesDelegated, err = k.convertLockToStake(ctx, senderAddr, valAddr, lockID, sharesToConvertAndStake, minAmtToStake) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + } else if migrationType == MigrationType(3) { + totalAmtConverted, totalSharesDelegated, err = k.convertUnlockedToStake(ctx, senderAddr, valAddr, sharesToConvertAndStake, minAmtToStake) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + } else { + return sdk.ZeroInt(), sdk.ZeroDec(), fmt.Errorf("unsupported migration type") } - return nil + return totalAmtConverted, totalSharesDelegated, nil } -func (k Keeper) convertSuperfluidBondedToStakedOsmo(ctx sdk.Context, sender sdk.AccAddress, valAddr string, originalLockId uint64, +// convertLockToStake handles locks that are superfluid bonded, superfluid unbonding, vanilla locked(unlocking) locks. +// Deletes all associated state, converts the lock itself to staking delegation by going through exit pool and swap. +func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, lockId uint64, sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { // do basic validation before converting - poolIdLeaving, preMigrationLock, err := k.validateUnbondConvertAndStake(ctx, sender, originalLockId, sharesToStake) + poolIdLeaving, _, err := k.validateUnbondConvertAndStake(ctx, sender, sharesToStake) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } - // undelegate superfluid delegated position instantly without creating unbonding synth lock (just yet). - _, err = k.undelegateCommon(ctx, sender.String(), originalLockId) + // Check that lockID corresponds to sender and that the denomination of LP shares corresponds to the poolId. + preMigrationLock, err := k.validateGammLockForSuperfluidStaking(ctx, sender, poolIdLeaving, lockId) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } // Force unlock, validate the provided sharesToStake, and exit the balancer pool. // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. - exitCoins, err := k.validateSharesToUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToStake, sdk.NewCoins()) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToStake, sdk.NewCoins()) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + + totalAmtConverted, shares, err = k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + + return totalAmtConverted, shares, nil +} + +// convertUnlockedToStake converts unlocked lock shares to osmo and stakes. +func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, + sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { + // do basic validation before converting + poolIdLeaving, _, err := k.validateUnbondConvertAndStake(ctx, sender, sharesToStake) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + + // Exit the balancer pool position. + // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. + exitCoins, err := k.gk.ExitPool(ctx, sender, poolIdLeaving, sharesToStake.Amount, sdk.NewCoins()) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } @@ -724,7 +755,7 @@ func (k Keeper) convertSuperfluidBondedToStakedOsmo(ctx sdk.Context, sender sdk. return totalAmtConverted, shares, nil } -func (k Keeper) validateUnbondConvertAndStake(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (poolIdLeaving uint64, preMigrationLock *lockuptypes.PeriodLock, err error) { +func (k Keeper) validateUnbondConvertAndStake(ctx sdk.Context, sender sdk.AccAddress, sharesToMigrate sdk.Coin) (poolIdLeaving uint64, preMigrationLock *lockuptypes.PeriodLock, err error) { // Defense in depth, ensuring the sharesToMigrate contains gamm pool share prefix. if !strings.HasPrefix(sharesToMigrate.Denom, gammtypes.GAMMTokenPrefix) { return 0, &lockuptypes.PeriodLock{}, types.SharesToMigrateDenomPrefixError{Denom: sharesToMigrate.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} @@ -736,11 +767,6 @@ func (k Keeper) validateUnbondConvertAndStake(ctx sdk.Context, sender sdk.AccAdd return 0, &lockuptypes.PeriodLock{}, err } - // Check that lockID corresponds to sender and that the denomination of LP shares corresponds to the poolId. - preMigrationLock, err = k.validateGammLockForSuperfluidStaking(ctx, sender, poolIdLeaving, lockId) - if err != nil { - return 0, &lockuptypes.PeriodLock{}, err - } return poolIdLeaving, preMigrationLock, nil } @@ -796,5 +822,5 @@ func (k Keeper) convertGammSharesToOsmoAndStake( return sdk.ZeroInt(), sdk.ZeroDec(), err } - return totalAmtCoverted, sdk.ZeroDec(), nil + return totalAmtToStake, shares, nil } diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 7eed33376a0..772158a0b13 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -6,11 +6,14 @@ import ( abci "github.com/tendermint/tendermint/abci/types" cltypes "github.com/osmosis-labs/osmosis/v17/x/concentrated-liquidity/types" + "github.com/osmosis-labs/osmosis/v17/x/gamm/pool-models/balancer" + gammtypes "github.com/osmosis-labs/osmosis/v17/x/gamm/types" lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" "github.com/osmosis-labs/osmosis/v17/x/superfluid/keeper" "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" errorsmod "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -1028,6 +1031,262 @@ func (s *KeeperTestSuite) TestRefreshIntermediaryDelegationAmounts() { } } +func (s *KeeperTestSuite) TestConvertLockToStake() { + defaultJoinTime := s.Ctx.BlockTime() + type tc struct { + superfluidUndelegating bool + unlocking bool + + useMinAmountToStake bool + usepartialShares bool + senderIsNotOwnerOfLock bool + + expectedError bool + } + testCases := map[string]tc{ + "lock that is superfluid delegated": {}, + "lock that is superfluid undelegating": { + unlocking: true, + superfluidUndelegating: true, + }, + "lock that is unlocking": { + unlocking: true, + superfluidUndelegating: false, + }, + // error cases + "error: min amount to stake greater than actual amount": { + useMinAmountToStake: true, + expectedError: true, + }, + "error: use partial shares": { + usepartialShares: true, + expectedError: true, + }, + "error: use different lock ": { + usepartialShares: true, + expectedError: true, + }, + "error: sender is not owner of lock ": { + senderIsNotOwnerOfLock: true, + expectedError: true, + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.SetupTest() + s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) + // We bundle all migration setup into a single function to avoid repeating the same code for each test case. + _, _, lock, _, _, _, _, originalValAddr := s.SetupUnbondConvertAndStakeTest(s.Ctx, true, tc.superfluidUndelegating, false, false) + + // testing params + sender := sdk.MustAccAddressFromBech32(lock.Owner) + if tc.senderIsNotOwnerOfLock { + sender = s.TestAccs[1] + } + valAddr := s.SetupValidator(stakingtypes.Bonded) + sharesToStake := lock.Coins + if tc.usepartialShares { + sharesToStake[0].Amount = lock.Coins[0].Amount.Quo(sdk.NewInt(2)) + } + minAmountToStake := sdk.ZeroInt() + if tc.useMinAmountToStake { + minAmountToStake = sdk.NewInt(999999999) + } + + balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) + + // system under test + totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.ConvertLockToStake(s.Ctx, sender, valAddr.String(), lock.ID, sharesToStake[0], minAmountToStake) + if tc.expectedError { + s.Require().Error(err) + return + } + s.Require().NoError(err) + + // Staking & Delegation check + // check if old delegation is succesfully deleted + _, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, originalValAddr) + s.Require().False(found) + // check if delegation amount matches + delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) + s.Require().True(found) + s.Require().Equal(delegation.Shares, sharesDelegated) + s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) + + // Superfluid check + // The synthetic lockup should be deleted. + _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.StakingSyntheticDenom(lock.Coins[0].Denom, valAddr.String())) + s.Require().Error(err) + + // Lock check + _, err = s.App.LockupKeeper.GetLockByID(s.Ctx, lock.ID) + s.Require().Error(err) + + // Bank check + balanceAfterConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) + s.Require().True(balanceBeforeConvertLockToStake.IsEqual(balanceAfterConvertLockToStake)) + }) + } +} + +func (s *KeeperTestSuite) TestConvertUnlockedToStake() { + defaultJoinTime := s.Ctx.BlockTime() + type tc struct { + useMinAmountToStake bool + expectedError bool + } + testCases := map[string]tc{ + "lock that is superfluid delegated": {}, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.SetupTest() + s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) + + // We bundle all migration setup into a single function to avoid repeating the same code for each test case. + _, _, _, _, sender, _, shareOut, _ := s.SetupUnbondConvertAndStakeTest(s.Ctx, false, false, false, true) + + // testing params + valAddr := s.SetupValidator(stakingtypes.Bonded) + minAmtToStake := sdk.ZeroInt() + + // system under test + totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.ConvertUnlockedToStake(s.Ctx, sender, valAddr.String(), shareOut, minAmtToStake) + if tc.expectedError { + s.Require().Error(err) + return + } + s.Require().NoError(err) + + // Staking & Delegation check + // check if delegation amount matches + delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) + s.Require().True(found) + s.Require().Equal(delegation.Shares, sharesDelegated) + s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) + + // + + // // Superfluid check + // // The synthetic lockup should be deleted. + // _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.StakingSyntheticDenom(lock.Coins[0].Denom, valAddr.String())) + // s.Require().Error(err) + + // // Lock check + // _, err = s.App.LockupKeeper.GetLockByID(s.Ctx, lock.ID) + // s.Require().Error(err) + + // // Bank check + // balanceAfterConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) + // s.Require().True(balanceBeforeConvertLockToStake.IsEqual(balanceAfterConvertLockToStake)) + }) + } +} + +func (s *KeeperTestSuite) SetupUnbondConvertAndStakeTest(ctx sdk.Context, superfluidDelegated, superfluidUndelegating, unlocking, noLock bool) (joinPoolAmt sdk.Coins, balancerIntermediaryAcc types.SuperfluidIntermediaryAccount, balancerLock *lockuptypes.PeriodLock, poolCreateAcc, poolJoinAcc sdk.AccAddress, balancerPooId uint64, balancerPoolShareOut sdk.Coin, valAddr sdk.ValAddress) { + bankKeeper := s.App.BankKeeper + gammKeeper := s.App.GAMMKeeper + superfluidKeeper := s.App.SuperfluidKeeper + lockupKeeper := s.App.LockupKeeper + stakingKeeper := s.App.StakingKeeper + poolmanagerKeeper := s.App.PoolManagerKeeper + + // Generate and fund two accounts. + // Account 1 will be the account that creates the pool. + // Account 2 will be the account that joins the pool. + delAddrs := CreateRandomAccounts(2) + poolCreateAcc = delAddrs[0] + poolJoinAcc = delAddrs[1] + for _, acc := range delAddrs { + err := simapp.FundAccount(bankKeeper, ctx, acc, defaultAcctFunds) + s.Require().NoError(err) + } + + // Set up a single validator. + valAddr = s.SetupValidator(stakingtypes.Bonded) + + // Create a balancer pool of "stake" and "foo". + msg := balancer.NewMsgCreateBalancerPool(poolCreateAcc, balancer.PoolParams{ + SwapFee: sdk.NewDecWithPrec(1, 2), + ExitFee: sdk.NewDec(0), + }, defaultPoolAssets, defaultFutureGovernor) + balancerPooId, err := poolmanagerKeeper.CreatePool(ctx, msg) + s.Require().NoError(err) + + // Join the balancer pool. + // Note the account balance before and after joining the pool. + balanceBeforeJoin := bankKeeper.GetAllBalances(ctx, poolJoinAcc) + _, _, err = gammKeeper.JoinPoolNoSwap(ctx, poolJoinAcc, balancerPooId, gammtypes.OneShare.MulRaw(50), sdk.Coins{}) + s.Require().NoError(err) + balanceAfterJoin := bankKeeper.GetAllBalances(ctx, poolJoinAcc) + + // The balancer join pool amount is the difference between the account balance before and after joining the pool. + joinPoolAmt, _ = balanceBeforeJoin.SafeSub(balanceAfterJoin) + + // Determine the balancer pool's LP token denomination. + balancerPoolDenom := gammtypes.GetPoolShareDenom(balancerPooId) + + // Register the balancer pool's LP token as a superfluid asset + err = superfluidKeeper.AddNewSuperfluidAsset(ctx, types.SuperfluidAsset{ + Denom: balancerPoolDenom, + AssetType: types.SuperfluidAssetTypeLPShare, + }) + s.Require().NoError(err) + + // Note how much of the balancer pool's LP token the account that joined the pool has. + balancerPoolShareOut = bankKeeper.GetBalance(ctx, poolJoinAcc, balancerPoolDenom) + + // The unbonding duration is the same as the staking module's unbonding duration. + unbondingDuration := stakingKeeper.GetParams(ctx).UnbondingTime + + // Lock the LP tokens for the duration of the unbonding period. + originalGammLockId := uint64(0) + if !noLock { + originalGammLockId = s.LockTokens(poolJoinAcc, sdk.NewCoins(balancerPoolShareOut), unbondingDuration) + } + + // Superfluid delegate the balancer lock if the test case requires it. + // Note the intermediary account that was created. + if superfluidDelegated { + err = superfluidKeeper.SuperfluidDelegate(ctx, poolJoinAcc.String(), originalGammLockId, valAddr.String()) + s.Require().NoError(err) + intermediaryAccConnection := superfluidKeeper.GetLockIdIntermediaryAccountConnection(ctx, originalGammLockId) + balancerIntermediaryAcc = superfluidKeeper.GetIntermediaryAccount(ctx, intermediaryAccConnection) + } + + // Superfluid undelegate the lock if the test case requires it. + if superfluidUndelegating { + err = superfluidKeeper.SuperfluidUndelegate(ctx, poolJoinAcc.String(), originalGammLockId) + s.Require().NoError(err) + } + + // Unlock the balancer lock if the test case requires it. + if unlocking { + // If lock was superfluid staked, we can't unlock via `BeginUnlock`, + // we need to unlock lock via `SuperfluidUnbondLock` + if superfluidUndelegating { + err = superfluidKeeper.SuperfluidUnbondLock(ctx, originalGammLockId, poolJoinAcc.String()) + s.Require().NoError(err) + } else { + lock, err := lockupKeeper.GetLockByID(ctx, originalGammLockId) + s.Require().NoError(err) + _, err = lockupKeeper.BeginUnlock(ctx, originalGammLockId, lock.Coins) + s.Require().NoError(err) + } + } + + balancerLock = &lockuptypes.PeriodLock{} + if !noLock { + balancerLock, err = lockupKeeper.GetLockByID(ctx, originalGammLockId) + s.Require().NoError(err) + } + + s.Require().NoError(err) + return joinPoolAmt, balancerIntermediaryAcc, balancerLock, poolCreateAcc, poolJoinAcc, balancerPooId, balancerPoolShareOut, valAddr +} + // type superfluidRedelegation struct { // lockId uint64 // oldValIndex int64 diff --git a/x/superfluid/types/codec.go b/x/superfluid/types/codec.go index aff8c223d21..4485fbb0af0 100644 --- a/x/superfluid/types/codec.go +++ b/x/superfluid/types/codec.go @@ -22,6 +22,7 @@ func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition{}, "osmosis/unlock-and-migrate", nil) cdc.RegisterConcrete(&MsgCreateFullRangePositionAndSuperfluidDelegate{}, "osmosis/full-range-and-sf-delegate", nil) cdc.RegisterConcrete(&MsgAddToConcentratedLiquiditySuperfluidPosition{}, "osmosis/add-to-cl-superfluid-position", nil) + cdc.RegisterConcrete(&MsgUnbondConvertAndStake{}, "osmosis/unbond-convert-and-stake", nil) } func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { @@ -36,6 +37,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { &MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition{}, &MsgCreateFullRangePositionAndSuperfluidDelegate{}, &MsgAddToConcentratedLiquiditySuperfluidPosition{}, + &MsgUnbondConvertAndStake{}, ) registry.RegisterImplementations( diff --git a/x/superfluid/types/msgs.go b/x/superfluid/types/msgs.go index b752105464d..78e49e9bb92 100644 --- a/x/superfluid/types/msgs.go +++ b/x/superfluid/types/msgs.go @@ -21,6 +21,7 @@ const ( TypeMsgUnlockAndMigrateShares = "unlock_and_migrate_shares" TypeMsgCreateFullRangePositionAndSuperfluidDelegate = "create_full_range_position_and_delegate" TypeMsgAddToConcentratedLiquiditySuperfluidPosition = "add_to_concentrated_liquidity_superfluid_position" + TypeMsgUnbondConvertAndStake = "add_to_concentrated_liquidity_superfluid_position" ) var _ sdk.Msg = &MsgSuperfluidDelegate{} @@ -392,3 +393,31 @@ func (msg MsgAddToConcentratedLiquiditySuperfluidPosition) GetSigners() []sdk.Ac } return []sdk.AccAddress{sender} } + +var _ sdk.Msg = &MsgUnbondConvertAndStake{} + +func (msg MsgUnbondConvertAndStake) Route() string { return RouterKey } +func (msg MsgUnbondConvertAndStake) Type() string { + return TypeMsgUnbondConvertAndStake +} + +func (msg MsgUnbondConvertAndStake) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return fmt.Errorf("Invalid sender address (%s)", err) + } + + return nil +} + +func (msg MsgUnbondConvertAndStake) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgUnbondConvertAndStake) GetSigners() []sdk.AccAddress { + sender, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{sender} +} diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index 7116d87a16d..4063455c71a 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -1023,6 +1023,7 @@ func (m *MsgAddToConcentratedLiquiditySuperfluidPositionResponse) GetLockId() ui // ===================== MsgUnbondConvertAndStake type MsgUnbondConvertAndStake struct { + // 0 for no lock. LockId uint64 `protobuf:"varint,1,opt,name=lock_id,json=lockId,proto3" json:"lock_id,omitempty" yaml:"lock_id"` Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` ValAddr string `protobuf:"bytes,3,opt,name=val_addr,json=valAddr,proto3" json:"val_addr,omitempty"` @@ -1093,8 +1094,8 @@ func (m *MsgUnbondConvertAndStake) GetSharesToConvertAndStake() types.Coin { } type MsgUnbondConvertAndStakeResponse struct { - TotalAmtStaked github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=total_amt_staked,json=totalAmtStaked,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_amt_staked" yaml:"total_amt_staked"` - TotalShares github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=total_shares,json=totalShares,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"total_shares"` + TotalAmtStaked github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=total_amt_staked,json=totalAmtStaked,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_amt_staked" yaml:"total_amt_staked"` + TotalSharesDelegated github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=total_shares_delegated,json=totalSharesDelegated,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"total_shares_delegated"` } func (m *MsgUnbondConvertAndStakeResponse) Reset() { *m = MsgUnbondConvertAndStakeResponse{} } @@ -1156,103 +1157,104 @@ func init() { func init() { proto.RegisterFile("osmosis/superfluid/tx.proto", fileDescriptor_55b645f187d22814) } var fileDescriptor_55b645f187d22814 = []byte{ - // 1532 bytes of a gzipped FileDescriptorProto + // 1538 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcf, 0x6f, 0x13, 0xc7, - 0x17, 0xcf, 0xda, 0x21, 0x81, 0x09, 0x09, 0xc9, 0x7e, 0x81, 0x18, 0x03, 0xb6, 0x19, 0x7e, 0x7c, - 0xc3, 0x0f, 0x7b, 0x63, 0x68, 0x01, 0xe5, 0x44, 0x1c, 0x8b, 0x2a, 0x34, 0x51, 0xe9, 0x12, 0x54, - 0x89, 0x8b, 0xb5, 0xf1, 0x4c, 0x96, 0x6d, 0x76, 0x77, 0xc2, 0xce, 0x38, 0x09, 0xea, 0xa9, 0xe5, - 0x50, 0x89, 0x13, 0xc7, 0xde, 0x7a, 0x6e, 0x0f, 0x88, 0x3f, 0xa1, 0x87, 0x1e, 0x50, 0x4f, 0x1c, - 0xab, 0x56, 0x0a, 0x15, 0x1c, 0x2a, 0xf5, 0x98, 0x4b, 0xaf, 0xd5, 0xec, 0xcc, 0xae, 0xd7, 0xf6, - 0x6e, 0x9c, 0x35, 0xe9, 0xa1, 0x17, 0xf0, 0xce, 0xbc, 0xf9, 0xbc, 0xcf, 0x7b, 0xf3, 0x3e, 0xf3, - 0x66, 0x02, 0x4e, 0x13, 0xea, 0x10, 0x6a, 0x51, 0x8d, 0xb6, 0x36, 0xb0, 0xb7, 0x66, 0xb7, 0x2c, - 0xa4, 0xb1, 0xed, 0xca, 0x86, 0x47, 0x18, 0x51, 0x55, 0x39, 0x59, 0x69, 0x4f, 0xe6, 0x8f, 0x9b, - 0xc4, 0x24, 0xfe, 0xb4, 0xc6, 0x7f, 0x09, 0xcb, 0xfc, 0x94, 0xe1, 0x58, 0x2e, 0xd1, 0xfc, 0x7f, - 0xe5, 0x50, 0xc1, 0x24, 0xc4, 0xb4, 0xb1, 0xe6, 0x7f, 0xad, 0xb6, 0xd6, 0x34, 0xd4, 0xf2, 0x0c, - 0x66, 0x11, 0x37, 0x98, 0x6f, 0xfa, 0xe8, 0xda, 0xaa, 0x41, 0xb1, 0xb6, 0x59, 0x5d, 0xc5, 0xcc, - 0xa8, 0x6a, 0x4d, 0x62, 0x05, 0xf3, 0xc5, 0xee, 0xf5, 0xcc, 0x72, 0x30, 0x65, 0x86, 0xb3, 0x21, - 0x0d, 0xce, 0xc7, 0x50, 0x6f, 0xff, 0x14, 0x46, 0xf0, 0x3b, 0x05, 0x9c, 0x58, 0xa6, 0xe6, 0x83, - 0x70, 0xbc, 0x8e, 0x6d, 0x6c, 0x1a, 0x0c, 0xab, 0x97, 0xc1, 0x08, 0xc5, 0x2e, 0xc2, 0x5e, 0x4e, - 0x29, 0x29, 0x33, 0x47, 0x6a, 0x53, 0xbb, 0x3b, 0xc5, 0xf1, 0xa7, 0x86, 0x63, 0xcf, 0x41, 0x31, - 0x0e, 0x75, 0x69, 0xa0, 0x4e, 0x83, 0x51, 0x9b, 0x34, 0xd7, 0x1b, 0x16, 0xca, 0x65, 0x4a, 0xca, - 0xcc, 0xb0, 0x3e, 0xc2, 0x3f, 0x17, 0x91, 0x7a, 0x0a, 0x1c, 0xde, 0x34, 0xec, 0x86, 0x81, 0x90, - 0x97, 0xcb, 0x72, 0x14, 0x7d, 0x74, 0xd3, 0xb0, 0xe7, 0x11, 0xf2, 0xe6, 0x4a, 0xcf, 0xff, 0x7c, - 0x75, 0x25, 0x26, 0xbb, 0x65, 0x24, 0x09, 0xc0, 0x22, 0x38, 0x1b, 0xcb, 0x4c, 0xc7, 0x74, 0x83, - 0xb8, 0x14, 0xc3, 0xaf, 0x15, 0x30, 0xdd, 0x61, 0xf1, 0xd0, 0x45, 0x07, 0xc8, 0x7e, 0x0e, 0x72, - 0x8a, 0x67, 0x63, 0x28, 0xb6, 0x42, 0x3f, 0xf0, 0x1c, 0x28, 0x26, 0x50, 0x08, 0x69, 0x7e, 0xd3, - 0x4b, 0x73, 0x95, 0xb8, 0x68, 0x89, 0x34, 0xd7, 0x0f, 0x84, 0xe6, 0x79, 0x4e, 0xb3, 0x10, 0x4b, - 0x93, 0xfb, 0x29, 0x73, 0xb3, 0x18, 0x9e, 0x01, 0x87, 0x90, 0xe7, 0x4b, 0x05, 0x5c, 0x48, 0x88, - 0x65, 0xde, 0x3d, 0x60, 0xd2, 0x6a, 0x0d, 0x0c, 0xf3, 0x5a, 0xf6, 0xab, 0x62, 0xec, 0xfa, 0xa9, - 0x8a, 0x28, 0xf6, 0x0a, 0x2f, 0xf6, 0x8a, 0x2c, 0xf6, 0xca, 0x02, 0xb1, 0xdc, 0xda, 0xff, 0x5e, - 0xef, 0x14, 0x87, 0x76, 0x77, 0x8a, 0x63, 0xc2, 0x01, 0x5f, 0x04, 0x75, 0x7f, 0x2d, 0xfc, 0x04, - 0x5c, 0xdb, 0x0f, 0xdf, 0x20, 0xc0, 0x28, 0x19, 0x25, 0x4a, 0x06, 0xee, 0x2a, 0xe0, 0xcc, 0x32, - 0x35, 0xb9, 0xf1, 0xbc, 0x8b, 0x3e, 0x4c, 0x0b, 0x06, 0x38, 0xc4, 0xc9, 0xd1, 0x5c, 0xa6, 0x94, - 0xdd, 0x3b, 0xb2, 0x59, 0x1e, 0xd9, 0x8f, 0x6f, 0x8b, 0x33, 0xa6, 0xc5, 0x1e, 0xb7, 0x56, 0x2b, - 0x4d, 0xe2, 0x68, 0x52, 0xf3, 0xe2, 0xbf, 0x32, 0x45, 0xeb, 0x1a, 0x7b, 0xba, 0x81, 0xa9, 0xbf, - 0x80, 0xea, 0x02, 0x79, 0x2f, 0x55, 0x5d, 0xe6, 0xb5, 0x70, 0x21, 0xa8, 0x05, 0x1e, 0x5e, 0xd9, - 0x70, 0x51, 0x39, 0x4e, 0x5e, 0x37, 0xfd, 0xdd, 0x4e, 0x8c, 0x39, 0xcc, 0xda, 0x04, 0xc8, 0x2c, - 0xd6, 0x65, 0xc2, 0x32, 0x8b, 0x75, 0xf8, 0x2a, 0x03, 0xb4, 0x65, 0x6a, 0x2e, 0x78, 0xd8, 0x60, - 0xf8, 0x6e, 0xcb, 0xb6, 0x75, 0xc3, 0x35, 0xf1, 0x7d, 0x42, 0x2d, 0x7e, 0x78, 0xfd, 0xb7, 0xf3, - 0xa7, 0x5e, 0x05, 0xa3, 0x1b, 0x84, 0xd8, 0xbc, 0x44, 0x86, 0x79, 0xc4, 0x35, 0x75, 0x77, 0xa7, - 0x38, 0x21, 0x98, 0xca, 0x09, 0xa8, 0x8f, 0xf0, 0x5f, 0x8b, 0x68, 0xee, 0xff, 0x3c, 0xd9, 0x30, - 0x48, 0xf6, 0x5a, 0xcb, 0xb6, 0xcb, 0x1e, 0xcf, 0x85, 0x48, 0xf9, 0x5a, 0x3b, 0xd5, 0x4f, 0xc0, - 0xad, 0x94, 0x19, 0x0b, 0xb3, 0x7f, 0x12, 0x88, 0x22, 0xad, 0x77, 0x94, 0x6c, 0x5d, 0x2d, 0x00, - 0xb0, 0x21, 0x01, 0x16, 0xeb, 0x52, 0x5b, 0x91, 0x11, 0x7e, 0xae, 0xe7, 0x96, 0xa9, 0xf9, 0xd0, - 0xbd, 0x4f, 0x88, 0xfd, 0xc5, 0x63, 0x8b, 0x61, 0xdb, 0xa2, 0x0c, 0x23, 0xfe, 0x99, 0x66, 0x3b, - 0x22, 0x09, 0xc9, 0xf4, 0x4d, 0xc8, 0x05, 0x9e, 0x90, 0x62, 0x90, 0x90, 0x96, 0xcb, 0x87, 0xcb, - 0x5b, 0x6d, 0xe7, 0x65, 0x3e, 0x00, 0xef, 0x81, 0x52, 0x12, 0xb3, 0x30, 0xec, 0x4b, 0xe0, 0x18, - 0xde, 0xb6, 0x18, 0x46, 0x0d, 0xa9, 0x58, 0x9a, 0x53, 0x4a, 0xd9, 0x99, 0x61, 0x7d, 0x5c, 0x0c, - 0x2f, 0xf9, 0xc2, 0xa5, 0xf0, 0x87, 0x2c, 0xb8, 0xed, 0x83, 0xd9, 0xa2, 0x8e, 0x97, 0x2d, 0xd3, - 0x33, 0x18, 0x7e, 0xf0, 0xd8, 0xf0, 0x30, 0x5d, 0x21, 0x61, 0xb2, 0x17, 0x88, 0xdb, 0xc4, 0x2e, - 0xe3, 0x73, 0x28, 0x48, 0x7c, 0xca, 0x34, 0x44, 0xcf, 0xb1, 0x6c, 0x34, 0x0d, 0x72, 0x02, 0x86, - 0x67, 0x9b, 0x09, 0xa6, 0xa8, 0x4f, 0xa0, 0xc1, 0x48, 0xc3, 0x11, 0x8c, 0xfa, 0x1f, 0x74, 0x25, - 0x79, 0xd0, 0xe5, 0x24, 0x83, 0x6e, 0x04, 0xa8, 0x1f, 0xa3, 0x32, 0x2c, 0x19, 0xa5, 0xfa, 0x5c, - 0x01, 0x13, 0x8c, 0xac, 0x63, 0xb7, 0x41, 0x5a, 0xac, 0xe1, 0x70, 0xd5, 0x0c, 0xf7, 0x53, 0xcd, - 0xa2, 0x74, 0x73, 0x42, 0xb8, 0xe9, 0x5c, 0x0e, 0x53, 0xc9, 0xe9, 0xa8, 0xbf, 0xf8, 0xb3, 0x16, - 0x5b, 0xb6, 0x5c, 0x3a, 0x57, 0xe4, 0x9b, 0x9f, 0x6f, 0x6f, 0x7e, 0x78, 0xf8, 0x04, 0xfc, 0x7f, - 0xc9, 0x82, 0x3b, 0x83, 0xee, 0x55, 0x58, 0x18, 0x8f, 0xc0, 0xa8, 0xe1, 0x90, 0x96, 0xcb, 0x66, - 0xe5, 0xa6, 0xdd, 0xe1, 0xf1, 0xfc, 0xb6, 0x53, 0xbc, 0xb4, 0x0f, 0xda, 0x8b, 0x2e, 0x6b, 0x6f, - 0x9b, 0x84, 0x81, 0x7a, 0x00, 0xd8, 0xc6, 0xae, 0xfa, 0x9b, 0xfc, 0xc1, 0xd8, 0xd5, 0x10, 0xbb, - 0xaa, 0x6e, 0x81, 0x29, 0xdb, 0x7a, 0xd2, 0xb2, 0x90, 0xc5, 0x9e, 0x36, 0x9a, 0xfe, 0x49, 0x80, - 0xc4, 0xe1, 0x53, 0xbb, 0x97, 0xc2, 0x4b, 0x1d, 0x37, 0xdb, 0x25, 0xd2, 0x03, 0x08, 0xf5, 0xc9, - 0x70, 0x4c, 0x9c, 0x36, 0x48, 0x7d, 0x08, 0x8e, 0x7c, 0x49, 0x2c, 0xb7, 0xc1, 0x6f, 0x87, 0xfe, - 0x99, 0x36, 0x76, 0x3d, 0x5f, 0x11, 0x57, 0xc7, 0x4a, 0x70, 0x75, 0xac, 0xac, 0x04, 0x57, 0xc7, - 0xda, 0x19, 0x59, 0x1e, 0x93, 0xc2, 0x45, 0xb8, 0x14, 0xbe, 0x78, 0x5b, 0x54, 0xf4, 0xc3, 0xfc, - 0x9b, 0x1b, 0xc3, 0x67, 0x59, 0xbf, 0x0b, 0xcc, 0x23, 0xb4, 0x42, 0xa2, 0x1b, 0xb6, 0x14, 0xf8, - 0x6f, 0x9f, 0x69, 0xa1, 0xde, 0x6e, 0x81, 0xb1, 0xe0, 0x84, 0x0a, 0x7b, 0x70, 0xed, 0xe4, 0xee, - 0x4e, 0x51, 0x0d, 0xce, 0x93, 0x70, 0x12, 0x46, 0x0e, 0x33, 0x14, 0x11, 0x6a, 0xa6, 0x9f, 0x50, - 0x1b, 0x81, 0x22, 0x10, 0xa6, 0x96, 0x87, 0xd1, 0x6c, 0x7f, 0xe1, 0x9d, 0x8d, 0x53, 0x44, 0xb0, - 0x1c, 0xea, 0xe3, 0xfe, 0x40, 0x5d, 0x7e, 0xf7, 0x38, 0xa8, 0xca, 0xa4, 0x0e, 0xe8, 0xa0, 0xda, - 0xe5, 0xa0, 0x3a, 0x77, 0x85, 0xeb, 0xe8, 0x62, 0xa0, 0x23, 0x03, 0xa1, 0x32, 0x23, 0xe5, 0xa6, - 0x1d, 0xed, 0xe1, 0x41, 0x6a, 0xe0, 0xcf, 0x59, 0xbf, 0xb3, 0xa4, 0xd9, 0x85, 0x50, 0x49, 0x03, - 0xef, 0x46, 0x44, 0x82, 0x99, 0x7f, 0x51, 0x82, 0xd9, 0x83, 0x96, 0xe0, 0x3a, 0x18, 0x77, 0xf1, - 0x56, 0x23, 0x54, 0x48, 0xee, 0x90, 0xef, 0xe1, 0x6e, 0x6a, 0xf9, 0x1d, 0x17, 0x1e, 0x3a, 0xc0, - 0xa0, 0x7e, 0xd4, 0xc5, 0x5b, 0x61, 0xde, 0xa3, 0x0d, 0xa3, 0xe7, 0x22, 0xd1, 0xdd, 0x30, 0xe0, - 0xcb, 0xac, 0x6c, 0xd6, 0xfc, 0xca, 0xba, 0x40, 0xdc, 0x4d, 0xec, 0x31, 0x7e, 0x2d, 0x60, 0xc6, - 0x3a, 0x8e, 0x22, 0x29, 0xfd, 0x90, 0xd2, 0x28, 0x65, 0x8f, 0x5b, 0x90, 0x07, 0x26, 0x1d, 0xcb, - 0x6d, 0x18, 0x0e, 0xe3, 0xfd, 0x87, 0x72, 0x1a, 0x7e, 0x14, 0x47, 0x44, 0xf7, 0x48, 0xb5, 0x1d, - 0xd3, 0xc2, 0x7b, 0x37, 0x1e, 0xd4, 0xc7, 0x1d, 0xcb, 0x9d, 0x77, 0xd8, 0x0a, 0x11, 0x61, 0x3e, - 0x53, 0xc0, 0xe9, 0x76, 0xcf, 0x6b, 0x8a, 0x24, 0x34, 0x0c, 0x17, 0x49, 0xff, 0x87, 0xfa, 0xa9, - 0xec, 0x8a, 0x54, 0x19, 0xec, 0xee, 0x9f, 0x3d, 0x58, 0x50, 0x9f, 0x0e, 0x3a, 0x69, 0x57, 0xb2, - 0xe7, 0x2e, 0x72, 0xf1, 0x95, 0xda, 0x4d, 0xcc, 0x7f, 0x40, 0x49, 0x00, 0x71, 0xad, 0xf3, 0x01, - 0xfe, 0x52, 0xe4, 0x1d, 0x26, 0x66, 0xc3, 0x42, 0x81, 0x51, 0x30, 0xc9, 0x08, 0xe3, 0x29, 0x76, - 0x98, 0x70, 0x8c, 0x64, 0xcf, 0x1a, 0x38, 0x8b, 0xdd, 0x78, 0x50, 0x9f, 0xf0, 0x87, 0xe6, 0x1d, - 0xe6, 0xfb, 0x46, 0xea, 0xe7, 0xe0, 0xa8, 0x30, 0x12, 0x11, 0xca, 0x32, 0xa8, 0xa4, 0xab, 0x71, - 0x7d, 0xcc, 0xc7, 0x10, 0x9d, 0xf9, 0xfa, 0xdf, 0x63, 0x20, 0xbb, 0x4c, 0x4d, 0xd5, 0x03, 0x6a, - 0xdc, 0xd5, 0xbe, 0xd2, 0xfb, 0x47, 0x90, 0x4a, 0xec, 0xbb, 0x3d, 0x5f, 0xdd, 0xb7, 0x69, 0x98, - 0xc3, 0x6d, 0x70, 0x3c, 0xf6, 0x79, 0x7f, 0xb5, 0x2f, 0x54, 0xdb, 0x38, 0x7f, 0x23, 0x85, 0x71, - 0x92, 0xe7, 0xf0, 0xf1, 0xbb, 0x1f, 0xcf, 0x81, 0xf1, 0xbe, 0x3c, 0xf7, 0x3c, 0x53, 0xbf, 0x57, - 0xc0, 0xb9, 0xfe, 0x8f, 0xf0, 0xdb, 0x29, 0x82, 0xea, 0x58, 0x99, 0xbf, 0x33, 0xe8, 0xca, 0x90, - 0xe1, 0xb7, 0x0a, 0x38, 0x95, 0xfc, 0x58, 0x9e, 0x4d, 0xc0, 0x4f, 0x5c, 0x91, 0xbf, 0x9d, 0x76, - 0x45, 0xc8, 0xe4, 0x27, 0x05, 0x5c, 0x4b, 0xf5, 0x12, 0x5d, 0x48, 0x70, 0x95, 0x06, 0x24, 0xff, - 0xe9, 0x01, 0x80, 0x84, 0x21, 0x7c, 0x05, 0x4e, 0xc4, 0xbf, 0xd2, 0xae, 0x25, 0x78, 0x89, 0xb5, - 0xce, 0x7f, 0x94, 0xc6, 0x3a, 0x74, 0xfe, 0xbb, 0x02, 0x3e, 0x1e, 0xec, 0xf1, 0xb4, 0x94, 0xe8, - 0x6f, 0x00, 0xb4, 0xfc, 0xca, 0x41, 0xa2, 0x75, 0x54, 0x47, 0xaa, 0x1b, 0x6a, 0x52, 0x75, 0xa4, - 0x01, 0x49, 0xac, 0x8e, 0x81, 0x6e, 0x69, 0x7e, 0x75, 0xc4, 0x5d, 0x0b, 0x92, 0xab, 0x23, 0xc6, - 0x7a, 0x8f, 0xea, 0xd8, 0xa3, 0x83, 0xd5, 0xee, 0xbf, 0x7e, 0x57, 0x50, 0xde, 0xbc, 0x2b, 0x28, - 0x7f, 0xbc, 0x2b, 0x28, 0x2f, 0xde, 0x17, 0x86, 0xde, 0xbc, 0x2f, 0x0c, 0xfd, 0xfa, 0xbe, 0x30, - 0xf4, 0xe8, 0x66, 0xa4, 0x91, 0x48, 0xe4, 0xb2, 0x6d, 0xac, 0xd2, 0xe0, 0x43, 0xdb, 0xac, 0xde, - 0xd2, 0xb6, 0x3b, 0xfe, 0x68, 0xce, 0x9b, 0xcb, 0xea, 0x88, 0xff, 0xe4, 0xb8, 0xf1, 0x4f, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xb1, 0x31, 0x01, 0x92, 0x57, 0x17, 0x00, 0x00, + 0x17, 0xcf, 0xda, 0x21, 0x81, 0x09, 0x09, 0xc9, 0x7e, 0x03, 0x31, 0x06, 0x6c, 0x33, 0xfc, 0xf8, + 0x86, 0x1f, 0xf6, 0xc6, 0xf0, 0xfd, 0x02, 0xca, 0x89, 0x38, 0x16, 0x95, 0x69, 0xac, 0xa2, 0x25, + 0xa8, 0x12, 0x17, 0x6b, 0xed, 0x99, 0x2c, 0xdb, 0xec, 0xee, 0x98, 0x9d, 0x71, 0x12, 0xd4, 0x53, + 0xcb, 0xa1, 0x12, 0x27, 0x8e, 0xbd, 0xf5, 0xdc, 0x1e, 0x10, 0x7f, 0x42, 0x0f, 0x3d, 0xa0, 0x9e, + 0x38, 0x56, 0xad, 0x14, 0x2a, 0x38, 0xf4, 0x9e, 0x4b, 0x4f, 0x95, 0xaa, 0xd9, 0x9d, 0x5d, 0xaf, + 0xed, 0xdd, 0x38, 0x6b, 0xd2, 0x43, 0x2f, 0xe0, 0x9d, 0x79, 0xf3, 0xde, 0xe7, 0xbd, 0x79, 0x9f, + 0xf7, 0xde, 0x04, 0x9c, 0x21, 0xd4, 0x22, 0xd4, 0xa0, 0x0a, 0xed, 0xb4, 0xb1, 0xb3, 0x61, 0x76, + 0x0c, 0xa4, 0xb0, 0x9d, 0x52, 0xdb, 0x21, 0x8c, 0xc8, 0xb2, 0xd8, 0x2c, 0x75, 0x37, 0xb3, 0xf3, + 0x3a, 0xd1, 0x89, 0xbb, 0xad, 0xf0, 0x5f, 0x9e, 0x64, 0x76, 0x4e, 0xb3, 0x0c, 0x9b, 0x28, 0xee, + 0xbf, 0x62, 0x29, 0xa7, 0x13, 0xa2, 0x9b, 0x58, 0x71, 0xbf, 0x9a, 0x9d, 0x0d, 0x05, 0x75, 0x1c, + 0x8d, 0x19, 0xc4, 0xf6, 0xf7, 0x5b, 0xae, 0x76, 0xa5, 0xa9, 0x51, 0xac, 0x6c, 0x95, 0x9b, 0x98, + 0x69, 0x65, 0xa5, 0x45, 0x0c, 0x7f, 0x3f, 0xdf, 0x7f, 0x9e, 0x19, 0x16, 0xa6, 0x4c, 0xb3, 0xda, + 0x42, 0xe0, 0x42, 0x04, 0xf4, 0xee, 0x4f, 0x4f, 0x08, 0x7e, 0x2b, 0x81, 0x93, 0x75, 0xaa, 0x3f, + 0x0c, 0xd6, 0xab, 0xd8, 0xc4, 0xba, 0xc6, 0xb0, 0x7c, 0x05, 0x4c, 0x50, 0x6c, 0x23, 0xec, 0x64, + 0xa4, 0x82, 0xb4, 0x78, 0xac, 0x32, 0xb7, 0xb7, 0x9b, 0x9f, 0x7e, 0xa6, 0x59, 0xe6, 0x32, 0xf4, + 0xd6, 0xa1, 0x2a, 0x04, 0xe4, 0x05, 0x30, 0x69, 0x92, 0xd6, 0x66, 0xc3, 0x40, 0x99, 0x54, 0x41, + 0x5a, 0x1c, 0x57, 0x27, 0xf8, 0x67, 0x0d, 0xc9, 0xa7, 0xc1, 0xd1, 0x2d, 0xcd, 0x6c, 0x68, 0x08, + 0x39, 0x99, 0x34, 0xd7, 0xa2, 0x4e, 0x6e, 0x69, 0xe6, 0x0a, 0x42, 0xce, 0x72, 0xe1, 0xc5, 0x1f, + 0xaf, 0xaf, 0x46, 0x44, 0xb7, 0x88, 0x04, 0x00, 0x98, 0x07, 0xe7, 0x22, 0x91, 0xa9, 0x98, 0xb6, + 0x89, 0x4d, 0x31, 0xfc, 0x4a, 0x02, 0x0b, 0x3d, 0x12, 0x8f, 0x6c, 0x74, 0x88, 0xe8, 0x97, 0x21, + 0x87, 0x78, 0x2e, 0x02, 0x62, 0x27, 0xb0, 0x03, 0xcf, 0x83, 0x7c, 0x0c, 0x84, 0x00, 0xe6, 0xd7, + 0x83, 0x30, 0x9b, 0xc4, 0x46, 0x6b, 0xa4, 0xb5, 0x79, 0x28, 0x30, 0x2f, 0x70, 0x98, 0xb9, 0x48, + 0x98, 0xdc, 0x4e, 0x91, 0x8b, 0x45, 0xe0, 0xf4, 0x31, 0x04, 0x38, 0x5f, 0x49, 0xe0, 0x62, 0x8c, + 0x2f, 0x2b, 0xf6, 0x21, 0x83, 0x96, 0x2b, 0x60, 0x9c, 0xe7, 0xb2, 0x9b, 0x15, 0x53, 0x37, 0x4e, + 0x97, 0xbc, 0x64, 0x2f, 0xf1, 0x64, 0x2f, 0x89, 0x64, 0x2f, 0xad, 0x12, 0xc3, 0xae, 0xfc, 0xe7, + 0xcd, 0x6e, 0x7e, 0x6c, 0x6f, 0x37, 0x3f, 0xe5, 0x19, 0xe0, 0x87, 0xa0, 0xea, 0x9e, 0x85, 0x9f, + 0x80, 0xeb, 0x07, 0xc1, 0xeb, 0x3b, 0x18, 0x06, 0x23, 0x85, 0xc1, 0xc0, 0x3d, 0x09, 0x9c, 0xad, + 0x53, 0x9d, 0x0b, 0xaf, 0xd8, 0xe8, 0xe3, 0xb8, 0xa0, 0x81, 0x23, 0x1c, 0x1c, 0xcd, 0xa4, 0x0a, + 0xe9, 0xfd, 0x3d, 0x5b, 0xe2, 0x9e, 0xfd, 0xf0, 0x2e, 0xbf, 0xa8, 0x1b, 0xec, 0x49, 0xa7, 0x59, + 0x6a, 0x11, 0x4b, 0x11, 0x9c, 0xf7, 0xfe, 0x2b, 0x52, 0xb4, 0xa9, 0xb0, 0x67, 0x6d, 0x4c, 0xdd, + 0x03, 0x54, 0xf5, 0x34, 0xef, 0xc7, 0xaa, 0x2b, 0x3c, 0x17, 0x2e, 0xfa, 0xb9, 0xc0, 0xdd, 0x2b, + 0x6a, 0x36, 0x2a, 0x46, 0xd1, 0xeb, 0x96, 0x7b, 0xdb, 0xb1, 0x3e, 0x07, 0x51, 0x9b, 0x01, 0xa9, + 0x5a, 0x55, 0x04, 0x2c, 0x55, 0xab, 0xc2, 0xd7, 0x29, 0xa0, 0xd4, 0xa9, 0xbe, 0xea, 0x60, 0x8d, + 0xe1, 0x7b, 0x1d, 0xd3, 0x54, 0x35, 0x5b, 0xc7, 0x0f, 0x08, 0x35, 0x78, 0xf1, 0xfa, 0x77, 0xc7, + 0x4f, 0xbe, 0x06, 0x26, 0xdb, 0x84, 0x98, 0x3c, 0x45, 0xc6, 0xb9, 0xc7, 0x15, 0x79, 0x6f, 0x37, + 0x3f, 0xe3, 0x21, 0x15, 0x1b, 0x50, 0x9d, 0xe0, 0xbf, 0x6a, 0x68, 0xf9, 0xbf, 0x3c, 0xd8, 0xd0, + 0x0f, 0xf6, 0x46, 0xc7, 0x34, 0x8b, 0x0e, 0x8f, 0x85, 0x17, 0xf2, 0x8d, 0x6e, 0xa8, 0x9f, 0x82, + 0xdb, 0x09, 0x23, 0x16, 0x44, 0xff, 0x14, 0xf0, 0x92, 0xb4, 0xda, 0x93, 0xb2, 0x55, 0x39, 0x07, + 0x40, 0x5b, 0x28, 0xa8, 0x55, 0x05, 0xb7, 0x42, 0x2b, 0xbc, 0xae, 0x67, 0xea, 0x54, 0x7f, 0x64, + 0x3f, 0x20, 0xc4, 0xfc, 0xfc, 0x89, 0xc1, 0xb0, 0x69, 0x50, 0x86, 0x11, 0xff, 0x4c, 0x72, 0x1d, + 0xa1, 0x80, 0xa4, 0x86, 0x06, 0xe4, 0x22, 0x0f, 0x48, 0xde, 0x0f, 0x48, 0xc7, 0xe6, 0xcb, 0xc5, + 0xed, 0xae, 0xf1, 0x22, 0x5f, 0x80, 0xf7, 0x41, 0x21, 0x0e, 0x59, 0xe0, 0xf6, 0x65, 0x70, 0x02, + 0xef, 0x18, 0x0c, 0xa3, 0x86, 0x60, 0x2c, 0xcd, 0x48, 0x85, 0xf4, 0xe2, 0xb8, 0x3a, 0xed, 0x2d, + 0xaf, 0xb9, 0xc4, 0xa5, 0xf0, 0xfb, 0x34, 0xb8, 0xe3, 0x2a, 0x33, 0xbd, 0x3c, 0xae, 0x1b, 0xba, + 0xa3, 0x31, 0xfc, 0xf0, 0x89, 0xe6, 0x60, 0xba, 0x4e, 0x82, 0x60, 0xaf, 0x12, 0xbb, 0x85, 0x6d, + 0xc6, 0xf7, 0x90, 0x1f, 0xf8, 0x84, 0x61, 0x08, 0xd7, 0xb1, 0x74, 0x38, 0x0c, 0x62, 0x03, 0x06, + 0xb5, 0x4d, 0x07, 0x73, 0xd4, 0x05, 0xd0, 0x60, 0xa4, 0x61, 0x79, 0x88, 0x86, 0x17, 0xba, 0x82, + 0x28, 0x74, 0x19, 0x81, 0xa0, 0x5f, 0x03, 0x54, 0x4f, 0x50, 0xe1, 0x96, 0xf0, 0x52, 0x7e, 0x21, + 0x81, 0x19, 0x46, 0x36, 0xb1, 0xdd, 0x20, 0x1d, 0xd6, 0xb0, 0x38, 0x6b, 0xc6, 0x87, 0xb1, 0xa6, + 0x26, 0xcc, 0x9c, 0xf4, 0xcc, 0xf4, 0x1e, 0x87, 0x89, 0xe8, 0x74, 0xdc, 0x3d, 0xfc, 0x59, 0x87, + 0xd5, 0x0d, 0x9b, 0x2e, 0xe7, 0xf9, 0xe5, 0x67, 0xbb, 0x97, 0x1f, 0x14, 0x1f, 0x1f, 0xff, 0xcf, + 0x69, 0x70, 0x77, 0xd4, 0xbb, 0x0a, 0x12, 0xe3, 0x31, 0x98, 0xd4, 0x2c, 0xd2, 0xb1, 0xd9, 0x92, + 0xb8, 0xb4, 0xbb, 0xdc, 0x9f, 0x5f, 0x77, 0xf3, 0x97, 0x0f, 0x00, 0xbb, 0x66, 0xb3, 0xee, 0xb5, + 0x09, 0x35, 0x50, 0xf5, 0x15, 0x76, 0x75, 0x97, 0xdd, 0x4b, 0xfe, 0x68, 0xdd, 0xe5, 0x40, 0x77, + 0x59, 0xde, 0x06, 0x73, 0xa6, 0xf1, 0xb4, 0x63, 0x20, 0x83, 0x3d, 0x6b, 0xb4, 0xdc, 0x4a, 0x80, + 0xbc, 0xe2, 0x53, 0xb9, 0x9f, 0xc0, 0x4a, 0x15, 0xb7, 0xba, 0x29, 0x32, 0xa0, 0x10, 0xaa, 0xb3, + 0xc1, 0x9a, 0x57, 0x6d, 0x90, 0xfc, 0x08, 0x1c, 0xfb, 0x82, 0x18, 0x76, 0x83, 0x4f, 0x87, 0x6e, + 0x4d, 0x9b, 0xba, 0x91, 0x2d, 0x79, 0xa3, 0x63, 0xc9, 0x1f, 0x1d, 0x4b, 0xeb, 0xfe, 0xe8, 0x58, + 0x39, 0x2b, 0xd2, 0x63, 0xd6, 0x33, 0x11, 0x1c, 0x85, 0x2f, 0xdf, 0xe5, 0x25, 0xf5, 0x28, 0xff, + 0xe6, 0xc2, 0xf0, 0x79, 0xda, 0xed, 0x02, 0x2b, 0x08, 0xad, 0x93, 0xf0, 0x85, 0xad, 0xf9, 0xf6, + 0xbb, 0x35, 0x2d, 0xe0, 0xdb, 0x6d, 0x30, 0xe5, 0x57, 0xa8, 0xa0, 0x07, 0x57, 0x4e, 0xed, 0xed, + 0xe6, 0x65, 0xbf, 0x9e, 0x04, 0x9b, 0x30, 0x54, 0xcc, 0x50, 0x88, 0xa8, 0xa9, 0x61, 0x44, 0x6d, + 0xf8, 0x8c, 0x40, 0x98, 0x1a, 0x0e, 0x46, 0x4b, 0xc3, 0x89, 0x77, 0x2e, 0x8a, 0x11, 0xfe, 0x71, + 0xa8, 0x4e, 0xbb, 0x0b, 0x55, 0xf1, 0x3d, 0x60, 0xa0, 0x2c, 0x82, 0x3a, 0xa2, 0x81, 0x72, 0x9f, + 0x81, 0xf2, 0xf2, 0x55, 0xce, 0xa3, 0x4b, 0x3e, 0x8f, 0x34, 0x84, 0x8a, 0x8c, 0x14, 0x5b, 0x66, + 0xb8, 0x87, 0xfb, 0xa1, 0x81, 0x3f, 0xa5, 0xdd, 0xce, 0x92, 0xe4, 0x16, 0x02, 0x26, 0x8d, 0x7c, + 0x1b, 0x21, 0x0a, 0xa6, 0xfe, 0x41, 0x0a, 0xa6, 0x0f, 0x9b, 0x82, 0x9b, 0x60, 0xda, 0xc6, 0xdb, + 0x8d, 0x80, 0x21, 0x99, 0x23, 0xae, 0x85, 0x7b, 0x89, 0xe9, 0x37, 0xef, 0x59, 0xe8, 0x51, 0x06, + 0xd5, 0xe3, 0x36, 0xde, 0x0e, 0xe2, 0x1e, 0x6e, 0x18, 0x03, 0x83, 0x44, 0x7f, 0xc3, 0x80, 0xaf, + 0xd2, 0xa2, 0x59, 0xf3, 0x91, 0x75, 0x95, 0xd8, 0x5b, 0xd8, 0x61, 0x7c, 0x2c, 0x60, 0xda, 0x26, + 0x0e, 0x6b, 0x92, 0x86, 0x69, 0x4a, 0xc2, 0x94, 0x7d, 0xa6, 0x20, 0x07, 0xcc, 0x5a, 0x86, 0xdd, + 0xd0, 0x2c, 0xc6, 0xfb, 0x0f, 0xe5, 0x30, 0x5c, 0x2f, 0x8e, 0x79, 0xdd, 0x23, 0xd1, 0x75, 0x2c, + 0x78, 0xd6, 0xfb, 0xf5, 0x41, 0x75, 0xda, 0x32, 0xec, 0x15, 0x8b, 0xad, 0x13, 0xcf, 0xcd, 0xe7, + 0x12, 0x38, 0xd3, 0xed, 0x79, 0x2d, 0x2f, 0x08, 0x0d, 0xcd, 0x46, 0xc2, 0xfe, 0x91, 0x61, 0x2c, + 0xbb, 0x2a, 0x58, 0x06, 0xfb, 0xfb, 0xe7, 0x80, 0x2e, 0xa8, 0x2e, 0xf8, 0x9d, 0xb4, 0x2f, 0xd8, + 0xcb, 0x97, 0x38, 0xf9, 0x0a, 0xdd, 0x26, 0xe6, 0x3e, 0xa0, 0x84, 0x02, 0x6f, 0xac, 0x73, 0x15, + 0xfc, 0x25, 0x89, 0x19, 0x26, 0xe2, 0xc2, 0x02, 0x82, 0x51, 0x30, 0xcb, 0x08, 0xe3, 0x21, 0xb6, + 0x98, 0x67, 0x18, 0x89, 0x9e, 0x35, 0x72, 0x14, 0xfb, 0xf5, 0x41, 0x75, 0xc6, 0x5d, 0x5a, 0xb1, + 0x98, 0x6b, 0x1b, 0xc9, 0x08, 0x9c, 0xf2, 0x84, 0x84, 0xff, 0xfe, 0x0c, 0x8a, 0x44, 0x42, 0x94, + 0x92, 0x65, 0xbb, 0x3a, 0xef, 0x6a, 0xf3, 0x7a, 0xb4, 0x3f, 0x9c, 0xa2, 0x1b, 0x7f, 0x4e, 0x81, + 0x74, 0x9d, 0xea, 0xb2, 0x03, 0xe4, 0xa8, 0x69, 0xbf, 0x34, 0xf8, 0x77, 0x91, 0x52, 0xe4, 0x53, + 0x3e, 0x5b, 0x3e, 0xb0, 0x68, 0x10, 0xd6, 0x1d, 0x30, 0x1f, 0xf9, 0xe2, 0xbf, 0x36, 0x54, 0x55, + 0x57, 0x38, 0x7b, 0x33, 0x81, 0x70, 0x9c, 0xe5, 0xe0, 0x3d, 0x7c, 0x10, 0xcb, 0xbe, 0xf0, 0x81, + 0x2c, 0x0f, 0xbc, 0x5c, 0xbf, 0x93, 0xc0, 0xf9, 0xe1, 0xef, 0xf2, 0x3b, 0x09, 0x9c, 0xea, 0x39, + 0x99, 0xbd, 0x3b, 0xea, 0xc9, 0x00, 0xe1, 0x37, 0x12, 0x38, 0x1d, 0xff, 0x7e, 0x5e, 0x8a, 0xd1, + 0x1f, 0x7b, 0x22, 0x7b, 0x27, 0xe9, 0x89, 0x00, 0xc9, 0x8f, 0x12, 0xb8, 0x9e, 0xe8, 0x71, 0xba, + 0x1a, 0x63, 0x2a, 0x89, 0x92, 0xec, 0xa7, 0x87, 0xa0, 0x24, 0x70, 0xe1, 0x4b, 0x70, 0x32, 0xfa, + 0xe1, 0x76, 0x3d, 0xc6, 0x4a, 0xa4, 0x74, 0xf6, 0x7f, 0x49, 0xa4, 0x03, 0xe3, 0xbf, 0x49, 0xe0, + 0xff, 0xa3, 0xbd, 0xa7, 0xd6, 0x62, 0xed, 0x8d, 0xa0, 0x2d, 0xbb, 0x7e, 0x98, 0xda, 0x7a, 0xb2, + 0x23, 0xd1, 0xd0, 0x1a, 0x97, 0x1d, 0x49, 0x94, 0xc4, 0x66, 0xc7, 0x48, 0x83, 0x9b, 0x9b, 0x1d, + 0x51, 0x93, 0x42, 0x7c, 0x76, 0x44, 0x48, 0xef, 0x93, 0x1d, 0xfb, 0x34, 0xb5, 0xca, 0x83, 0x37, + 0xef, 0x73, 0xd2, 0xdb, 0xf7, 0x39, 0xe9, 0xf7, 0xf7, 0x39, 0xe9, 0xe5, 0x87, 0xdc, 0xd8, 0xdb, + 0x0f, 0xb9, 0xb1, 0x5f, 0x3e, 0xe4, 0xc6, 0x1e, 0xdf, 0x0a, 0x75, 0x14, 0xa1, 0xb9, 0x68, 0x6a, + 0x4d, 0xea, 0x7f, 0x28, 0x5b, 0xe5, 0xdb, 0xca, 0x4e, 0xcf, 0xdf, 0xd1, 0x79, 0x97, 0x69, 0x4e, + 0xb8, 0xaf, 0x90, 0x9b, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x83, 0x52, 0xc3, 0x0d, 0x6a, 0x17, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2494,9 +2496,9 @@ func (m *MsgUnbondConvertAndStakeResponse) MarshalToSizedBuffer(dAtA []byte) (in var l int _ = l { - size := m.TotalShares.Size() + size := m.TotalSharesDelegated.Size() i -= size - if _, err := m.TotalShares.MarshalTo(dAtA[i:]); err != nil { + if _, err := m.TotalSharesDelegated.MarshalTo(dAtA[i:]); err != nil { return 0, err } i = encodeVarintTx(dAtA, i, uint64(size)) @@ -2858,7 +2860,7 @@ func (m *MsgUnbondConvertAndStakeResponse) Size() (n int) { _ = l l = m.TotalAmtStaked.Size() n += 1 + l + sovTx(uint64(l)) - l = m.TotalShares.Size() + l = m.TotalSharesDelegated.Size() n += 1 + l + sovTx(uint64(l)) return n } @@ -5231,7 +5233,7 @@ func (m *MsgUnbondConvertAndStakeResponse) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalShares", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field TotalSharesDelegated", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -5259,7 +5261,7 @@ func (m *MsgUnbondConvertAndStakeResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.TotalShares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.TotalSharesDelegated.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex From 9f1068565b440f982a848110e5af5accec5789c1 Mon Sep 17 00:00:00 2001 From: mattverse Date: Fri, 4 Aug 2023 22:17:01 +0900 Subject: [PATCH 03/27] Finish adding test cases --- x/superfluid/keeper/export_test.go | 4 +- x/superfluid/keeper/stake.go | 26 +-- x/superfluid/keeper/stake_test.go | 248 +++++++++++++++++++++++++++-- 3 files changed, 253 insertions(+), 25 deletions(-) diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 6596d05dc1f..8ead95ccf19 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -70,8 +70,8 @@ func (k Keeper) ConvertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, v return k.convertUnlockedToStake(ctx, sender, valAddr, sharesToStake, minAmtToStake) } -func (k Keeper) ValidateUnbondConvertAndStake(ctx sdk.Context, sender sdk.AccAddress, sharesToMigrate sdk.Coin) (poolIdLeaving uint64, preMigrationLock *lockuptypes.PeriodLock, err error) { - return k.validateUnbondConvertAndStake(ctx, sender, sharesToMigrate) +func (k Keeper) ValidateUnbondConvertAndStake(ctx sdk.Context, sharesToMigrate sdk.Coin) (poolIdLeaving uint64, err error) { + return k.validateUnbondConvertAndStake(ctx, sharesToMigrate) } func (k Keeper) ConvertGammSharesToOsmoAndStake( diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 2f2172d690e..d0a12cf733a 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -705,20 +705,20 @@ func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, va func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, lockId uint64, sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { // do basic validation before converting - poolIdLeaving, _, err := k.validateUnbondConvertAndStake(ctx, sender, sharesToStake) + poolIdLeaving, err := k.validateUnbondConvertAndStake(ctx, sharesToStake) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } // Check that lockID corresponds to sender and that the denomination of LP shares corresponds to the poolId. - preMigrationLock, err := k.validateGammLockForSuperfluidStaking(ctx, sender, poolIdLeaving, lockId) + lock, err := k.validateGammLockForSuperfluidStaking(ctx, sender, poolIdLeaving, lockId) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } // Force unlock, validate the provided sharesToStake, and exit the balancer pool. // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. - exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToStake, sdk.NewCoins()) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToStake, sdk.NewCoins()) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } @@ -735,7 +735,7 @@ func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAd func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { // do basic validation before converting - poolIdLeaving, _, err := k.validateUnbondConvertAndStake(ctx, sender, sharesToStake) + poolIdLeaving, err := k.validateUnbondConvertAndStake(ctx, sharesToStake) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } @@ -755,21 +755,27 @@ func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, v return totalAmtConverted, shares, nil } -func (k Keeper) validateUnbondConvertAndStake(ctx sdk.Context, sender sdk.AccAddress, sharesToMigrate sdk.Coin) (poolIdLeaving uint64, preMigrationLock *lockuptypes.PeriodLock, err error) { +// validateUnbondConvertAndStake validates +// - given token has gamm share prefix +// - gamm share denom is in correct format +func (k Keeper) validateUnbondConvertAndStake(ctx sdk.Context, sharesToStake sdk.Coin) (poolIdLeaving uint64, err error) { // Defense in depth, ensuring the sharesToMigrate contains gamm pool share prefix. - if !strings.HasPrefix(sharesToMigrate.Denom, gammtypes.GAMMTokenPrefix) { - return 0, &lockuptypes.PeriodLock{}, types.SharesToMigrateDenomPrefixError{Denom: sharesToMigrate.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} + if !strings.HasPrefix(sharesToStake.Denom, gammtypes.GAMMTokenPrefix) { + return 0, types.SharesToMigrateDenomPrefixError{Denom: sharesToStake.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} } // Get the balancer poolId by parsing the gamm share denom. - poolIdLeaving, err = gammtypes.GetPoolIdFromShareDenom(sharesToMigrate.Denom) + poolIdLeaving, err = gammtypes.GetPoolIdFromShareDenom(sharesToStake.Denom) if err != nil { - return 0, &lockuptypes.PeriodLock{}, err + return 0, err } - return poolIdLeaving, preMigrationLock, nil + return poolIdLeaving, nil } +// convertGammSharesToOsmoAndStake converts given gamm shares to osmo by swapping in the given pool +// then stakes it to the designated validator. +// minAmtToStake works as slippage bound, and would error if total amount being staked is less than min amount to stake. func (k Keeper) convertGammSharesToOsmoAndStake( ctx sdk.Context, sender sdk.AccAddress, valAddr string, diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 772158a0b13..1472ccd9eb0 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1031,6 +1031,86 @@ func (s *KeeperTestSuite) TestRefreshIntermediaryDelegationAmounts() { } } +func (s *KeeperTestSuite) TestUnbondConvertAndStake() { + defaultJoinTime := s.Ctx.BlockTime() + type tc struct { + superfluidUndelegating bool + unlocking bool + unlocked bool + + expectedError bool + } + testCases := map[string]tc{ + "lock that is superfluid delegated": {}, + "lock that is superfluid undelegating": { + unlocking: true, + superfluidUndelegating: true, + }, + "lock that is unlocking": { + unlocking: true, + superfluidUndelegating: false, + }, + "unlocked gamm shares": { + unlocking: true, + superfluidUndelegating: false, + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.SetupTest() + s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) + // We bundle all migration setup into a single function to avoid repeating the same code for each test case. + _, _, lock, _, _, _, _, originalValAddr := s.SetupUnbondConvertAndStakeTest(s.Ctx, true, tc.superfluidUndelegating, false, false) + + // testing params + sender := sdk.MustAccAddressFromBech32(lock.Owner) + valAddr := s.SetupValidator(stakingtypes.Bonded) + sharesToStake := lock.Coins + minAmountToStake := sdk.ZeroInt() + + balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) + + // system under test + totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lock.ID, sender.String(), valAddr.String(), minAmountToStake, sharesToStake[0]) + if tc.expectedError { + s.Require().Error(err) + return + } + s.Require().NoError(err) + + // Staking & Delegation check + // check if old delegation is succesfully deleted + _, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, originalValAddr) + s.Require().False(found) + // check if delegation amount matches + delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) + s.Require().True(found) + s.Require().Equal(delegation.Shares, sharesDelegated) + s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) + + // Superfluid check + // The synthetic lockup should be deleted. + _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.StakingSyntheticDenom(lock.Coins[0].Denom, valAddr.String())) + s.Require().Error(err) + + // intermediary account should have been deleted in all case + if lock.ID != 0 { + _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.UnstakingSyntheticDenom(lock.Coins[0].Denom, valAddr.String())) + s.Require().Error(err) + } + + // Lock check + _, err = s.App.LockupKeeper.GetLockByID(s.Ctx, lock.ID) + s.Require().Error(err) + + // Bank check + balanceAfterConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) + s.Require().True(balanceBeforeConvertLockToStake.IsEqual(balanceAfterConvertLockToStake)) + }) + } +} + func (s *KeeperTestSuite) TestConvertLockToStake() { defaultJoinTime := s.Ctx.BlockTime() type tc struct { @@ -1133,11 +1213,19 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { func (s *KeeperTestSuite) TestConvertUnlockedToStake() { defaultJoinTime := s.Ctx.BlockTime() type tc struct { + usePartialShares bool useMinAmountToStake bool expectedError bool } testCases := map[string]tc{ - "lock that is superfluid delegated": {}, + "convert unlocked gamm shares": {}, + "convert partial shares": { + usePartialShares: true, + }, + "min amount to stake exceeds exit pool amount": { + useMinAmountToStake: true, + expectedError: true, + }, } for name, tc := range testCases { @@ -1146,11 +1234,26 @@ func (s *KeeperTestSuite) TestConvertUnlockedToStake() { s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) // We bundle all migration setup into a single function to avoid repeating the same code for each test case. - _, _, _, _, sender, _, shareOut, _ := s.SetupUnbondConvertAndStakeTest(s.Ctx, false, false, false, true) + _, _, _, _, sender, poolId, shareOut, _ := s.SetupUnbondConvertAndStakeTest(s.Ctx, false, false, false, true) // testing params valAddr := s.SetupValidator(stakingtypes.Bonded) minAmtToStake := sdk.ZeroInt() + if tc.useMinAmountToStake { + minAmtToStake = sdk.NewInt(9999999999) + } + sharesToStake := shareOut + if tc.usePartialShares { + sharesToStake.Amount = sharesToStake.Amount.Quo(sdk.NewInt(2)) + } + + balanceBeforeConvert := s.App.BankKeeper.GetBalance(s.Ctx, sender, shareOut.Denom) + s.Require().True(!balanceBeforeConvert.Amount.IsZero()) + + bondDenom := s.App.StakingKeeper.BondDenom(s.Ctx) + totalPoolLiquidityBeforeConvert, err := s.App.GAMMKeeper.GetTotalPoolLiquidity(s.Ctx, poolId) + s.Require().NoError(err) + bondDenomPoolAmtBeforeConvert := totalPoolLiquidityBeforeConvert.AmountOf(bondDenom) // system under test totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.ConvertUnlockedToStake(s.Ctx, sender, valAddr.String(), shareOut, minAmtToStake) @@ -1160,6 +1263,13 @@ func (s *KeeperTestSuite) TestConvertUnlockedToStake() { } s.Require().NoError(err) + // gamm check + totalPoolLiquidityAfterConvert, err := s.App.GAMMKeeper.GetTotalPoolLiquidity(s.Ctx, poolId) + s.Require().NoError(err) + // check that pool liquidity have reduced + bondDenomPoolAmtAfterConvert := totalPoolLiquidityAfterConvert.AmountOf(bondDenom) + s.Require().True(bondDenomPoolAmtAfterConvert.LT(bondDenomPoolAmtBeforeConvert)) + // Staking & Delegation check // check if delegation amount matches delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) @@ -1167,20 +1277,132 @@ func (s *KeeperTestSuite) TestConvertUnlockedToStake() { s.Require().Equal(delegation.Shares, sharesDelegated) s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) - // + // Bank check + balanceAfterConvertLockToStake := s.App.BankKeeper.GetBalance(s.Ctx, sender, shareOut.Denom) + s.Require().True(balanceAfterConvertLockToStake.IsZero()) + }) + } +} - // // Superfluid check - // // The synthetic lockup should be deleted. - // _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.StakingSyntheticDenom(lock.Coins[0].Denom, valAddr.String())) - // s.Require().Error(err) +func (s *KeeperTestSuite) TestValidateUnbondConvertAndStake() { + type tc struct { + sharesToMigrate sdk.Coin + expectedError bool + } + testCases := map[string]tc{ + "happy case": { + sharesToMigrate: sdk.NewInt64Coin("gamm/pool/1", 100), + }, + "non gamm token prefix": { + sharesToMigrate: sdk.NewInt64Coin("uosmo", 100), + expectedError: true, + }, + "invalid gamm token": { + sharesToMigrate: sdk.NewInt64Coin("gamm/pool/invalid", 100), + expectedError: true, + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.SetupTest() - // // Lock check - // _, err = s.App.LockupKeeper.GetLockByID(s.Ctx, lock.ID) - // s.Require().Error(err) + poolIdLeaving, err := s.App.SuperfluidKeeper.ValidateUnbondConvertAndStake(s.Ctx, tc.sharesToMigrate) + if tc.expectedError { + s.Require().Error(err) + return + } + s.Require().NoError(err) + s.Require().Equal(poolIdLeaving, uint64(1)) + }) + } +} - // // Bank check - // balanceAfterConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) - // s.Require().True(balanceBeforeConvertLockToStake.IsEqual(balanceAfterConvertLockToStake)) +func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { + type tc struct { + useInvalidValAddr bool + useMinAmtToStake bool + + expectedError bool + } + testCases := map[string]tc{ + "happy case": {}, + "error: invalid val address": { + useInvalidValAddr: true, + expectedError: true, + }, + "error: min amount to stake exceeds actual amount staking": { + useMinAmtToStake: true, + expectedError: true, + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.SetupTest() + bondDenom := s.App.StakingKeeper.BondDenom(s.Ctx) + + // use setup helper function to setup pool, fund account with gamm shares + // note that we're not creating any locks here. + _, _, _, _, sender, poolId, shareOut, _ := s.SetupUnbondConvertAndStakeTest(s.Ctx, false, false, false, true) + // exit pool + exitCoins, err := s.App.GAMMKeeper.ExitPool(s.Ctx, sender, poolId, shareOut.Amount, sdk.NewCoins()) + s.Require().NoError(err) + + // test params + valAddr := s.SetupValidator(stakingtypes.Bonded) + valAddrString := valAddr.String() + if tc.useInvalidValAddr { + valAddrString = s.TestAccs[0].String() + } + minAmtToStake := sdk.ZeroInt() + if tc.useMinAmtToStake { + minAmtToStake = sdk.NewInt(999999999) + } + + // mark expected shares before swap + nonStakeDenomCoin := exitCoins.FilterDenoms([]string{"foo"})[0] + stakeDenomCoin := exitCoins.AmountOf("stake") + // use cache context to get expected amount after swap without changing test state + cc, _ := s.Ctx.CacheContext() + tokenOutAmt, err := s.App.PoolManagerKeeper.SwapExactAmountIn(cc, sender, poolId, nonStakeDenomCoin, bondDenom, sdk.ZeroInt()) + s.Require().NoError(err) + expectedTotalAmtStaked := tokenOutAmt.Add(stakeDenomCoin) + + // mark pool liquidity + pool, err := s.App.GAMMKeeper.GetPoolAndPoke(s.Ctx, poolId) + s.Require().NoError(err) + poolLiquidityBeforeSwap := pool.GetTotalPoolLiquidity(s.Ctx) + poolBeforeBondDenomAmt := poolLiquidityBeforeSwap.AmountOf("stake") + poolBeforeNonBondDenomAmt := poolLiquidityBeforeSwap.AmountOf("foo") + + // system under test. + totalAmtConverted, shares, err := s.App.SuperfluidKeeper.ConvertGammSharesToOsmoAndStake(s.Ctx, sender, valAddrString, poolId, exitCoins, minAmtToStake) + if tc.expectedError { + s.Require().Error(err) + return + } + s.Require().NoError(err) + + // check that total Amount converted is equal to (swap result + original stake denom amount) + s.Require().True(expectedTotalAmtStaked.Equal(totalAmtConverted)) + s.Require().True(expectedTotalAmtStaked.Equal(shares.RoundInt())) + + // check staking + delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) + s.Require().True(found) + s.Require().True(delegation.Shares.Equal(shares)) + + // check pool + pool, err = s.App.GAMMKeeper.GetPoolAndPoke(s.Ctx, poolId) + s.Require().NoError(err) + poolLiquidityAfterSwap := pool.GetTotalPoolLiquidity(s.Ctx) + poolAfterBondDenomAmt := poolLiquidityAfterSwap.AmountOf("stake") + poolAfterNonBondDenomAmt := poolLiquidityAfterSwap.AmountOf("foo") + // we swapped from non-bond denom to bond denom, + // thus bond denom token in pool should have decreased, non bond denom token should have increased + s.Require().True(poolBeforeBondDenomAmt.GT(poolAfterBondDenomAmt)) + s.Require().True(poolBeforeNonBondDenomAmt.LT(poolAfterNonBondDenomAmt)) }) } } From 8516a9b422c2ff8137e0c6247e129fa0e1b6cf4a Mon Sep 17 00:00:00 2001 From: mattverse Date: Fri, 4 Aug 2023 23:15:44 +0900 Subject: [PATCH 04/27] Use val set --- app/keepers/keepers.go | 10 +++--- proto/osmosis/superfluid/tx.proto | 8 ++++- x/superfluid/keeper/keeper.go | 24 ++++++++------ x/superfluid/keeper/stake.go | 30 +++++++++++++++-- x/superfluid/keeper/stake_test.go | 46 +++++++++++++++++++++++--- x/superfluid/types/errors.go | 7 ++++ x/superfluid/types/expected_keepers.go | 5 +++ x/superfluid/types/tx.pb.go | 16 ++++++--- 8 files changed, 117 insertions(+), 29 deletions(-) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 2a8ecfda10c..c4e117dd920 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -385,11 +385,6 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.ConcentratedLiquidityKeeper.SetIncentivesKeeper(appKeepers.IncentivesKeeper) appKeepers.GAMMKeeper.SetIncentivesKeeper(appKeepers.IncentivesKeeper) - appKeepers.SuperfluidKeeper = superfluidkeeper.NewKeeper( - appKeepers.keys[superfluidtypes.StoreKey], appKeepers.GetSubspace(superfluidtypes.ModuleName), - *appKeepers.AccountKeeper, appKeepers.BankKeeper, appKeepers.StakingKeeper, appKeepers.DistrKeeper, appKeepers.EpochsKeeper, appKeepers.LockupKeeper, appKeepers.GAMMKeeper, appKeepers.IncentivesKeeper, - lockupkeeper.NewMsgServerImpl(appKeepers.LockupKeeper), appKeepers.ConcentratedLiquidityKeeper, appKeepers.PoolManagerKeeper) - mintKeeper := mintkeeper.NewKeeper( appKeepers.keys[minttypes.StoreKey], appKeepers.GetSubspace(minttypes.ModuleName), @@ -437,6 +432,11 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.ValidatorSetPreferenceKeeper = &validatorSetPreferenceKeeper + appKeepers.SuperfluidKeeper = superfluidkeeper.NewKeeper( + appKeepers.keys[superfluidtypes.StoreKey], appKeepers.GetSubspace(superfluidtypes.ModuleName), + *appKeepers.AccountKeeper, appKeepers.BankKeeper, appKeepers.StakingKeeper, appKeepers.DistrKeeper, appKeepers.EpochsKeeper, appKeepers.LockupKeeper, appKeepers.GAMMKeeper, appKeepers.IncentivesKeeper, + lockupkeeper.NewMsgServerImpl(appKeepers.LockupKeeper), appKeepers.ConcentratedLiquidityKeeper, appKeepers.PoolManagerKeeper, appKeepers.ValidatorSetPreferenceKeeper) + // The last arguments can contain custom message handlers, and custom query handlers, // if we want to allow any custom callbacks supportedFeatures := "iterator,staking,stargate,osmosis,cosmwasm_1_1,cosmwasm_1_2" diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index 7c72ae56a4a..dbf40ac52c2 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -242,9 +242,13 @@ message MsgAddToConcentratedLiquiditySuperfluidPositionResponse { message MsgUnbondConvertAndStake { option (amino.name) = "osmosis/unbond-convert-and-stake"; - // 0 for no lock. + // lock ID to convert and stake. + // lock id with 0 should be provided if converting liquid gamm shares to stake uint64 lock_id = 1 [ (gogoproto.moretags) = "yaml:\"lock_id\"" ]; string sender = 2 [ (gogoproto.moretags) = "yaml:\"sender\"" ]; + // validator address to delegate to. + // If provided empty string, we use validator returned from valset-preference + // module. string val_addr = 3; // min_amt_to_stake indicates the minimum amount to stake after conversion string min_amt_to_stake = 4 [ @@ -252,6 +256,8 @@ message MsgUnbondConvertAndStake { (gogoproto.moretags) = "yaml:\"min_amt_to_stake\"", (gogoproto.nullable) = false ]; + // shares_to_convert_and_stake indicates amount of gamm shares to convert and + // stake. cosmos.base.v1beta1.Coin shares_to_convert_and_stake = 5 [ (gogoproto.moretags) = "yaml:\"shares_to_convert_and_stake\"", (gogoproto.nullable) = false diff --git a/x/superfluid/keeper/keeper.go b/x/superfluid/keeper/keeper.go index 392384184d8..3670570caa6 100644 --- a/x/superfluid/keeper/keeper.go +++ b/x/superfluid/keeper/keeper.go @@ -18,16 +18,17 @@ type Keeper struct { storeKey sdk.StoreKey paramSpace paramtypes.Subspace - ak authkeeper.AccountKeeper - bk types.BankKeeper - sk types.StakingKeeper - ck types.CommunityPoolKeeper - ek types.EpochKeeper - lk types.LockupKeeper - gk types.GammKeeper - ik types.IncentivesKeeper - clk types.ConcentratedKeeper - pmk types.PoolManagerKeeper + ak authkeeper.AccountKeeper + bk types.BankKeeper + sk types.StakingKeeper + ck types.CommunityPoolKeeper + ek types.EpochKeeper + lk types.LockupKeeper + gk types.GammKeeper + ik types.IncentivesKeeper + clk types.ConcentratedKeeper + pmk types.PoolManagerKeeper + vspk types.ValSetPreferenceKeeper lms types.LockupMsgServer } @@ -35,7 +36,7 @@ type Keeper struct { var _ govtypes.StakingKeeper = (*Keeper)(nil) // NewKeeper returns an instance of Keeper. -func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, ak authkeeper.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, dk types.CommunityPoolKeeper, ek types.EpochKeeper, lk types.LockupKeeper, gk types.GammKeeper, ik types.IncentivesKeeper, lms types.LockupMsgServer, clk types.ConcentratedKeeper, pmk types.PoolManagerKeeper) *Keeper { +func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, ak authkeeper.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, dk types.CommunityPoolKeeper, ek types.EpochKeeper, lk types.LockupKeeper, gk types.GammKeeper, ik types.IncentivesKeeper, lms types.LockupMsgServer, clk types.ConcentratedKeeper, pmk types.PoolManagerKeeper, vspk types.ValSetPreferenceKeeper) *Keeper { // set KeyTable if it has not already been set if !paramSpace.HasKeyTable() { paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) @@ -54,6 +55,7 @@ func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, ak authkee ik: ik, clk: clk, pmk: pmk, + vspk: vspk, lms: lms, } diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index d0a12cf733a..c4841c4f63f 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -662,6 +662,7 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn // UnbondConvertAndStake converts given lock to osmo and stakes it to given validator. // Supports conversion of 1)superfluid bonded 2)superfluid undelegating 3)vanilla unlocking 4) unlocked locks. +// If valAddr is empty, we attempt to get staking preference from valset-pref module and stake to the given validator. func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, minAmtToStake sdk.Int, sharesToConvertAndStake sdk.Coin) (totalAmtConverted sdk.Int, totalSharesDelegated sdk.Dec, err error) { senderAddr, err := sdk.AccAddressFromBech32(sender) @@ -776,6 +777,7 @@ func (k Keeper) validateUnbondConvertAndStake(ctx sdk.Context, sharesToStake sdk // convertGammSharesToOsmoAndStake converts given gamm shares to osmo by swapping in the given pool // then stakes it to the designated validator. // minAmtToStake works as slippage bound, and would error if total amount being staked is less than min amount to stake. +// If valAddr is empty, we attempt to get staking preference from valset-pref module and stake to the given validator. func (k Keeper) convertGammSharesToOsmoAndStake( ctx sdk.Context, sender sdk.AccAddress, valAddr string, @@ -817,9 +819,31 @@ func (k Keeper) convertGammSharesToOsmoAndStake( } } - val, err := k.validateValAddrForDelegate(ctx, valAddr) - if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + var val stakingtypes.Validator + // if given valAddr is empty, we use delegation preference given from valset-pref module. + if valAddr == "" { + delegationPref, err := k.vspk.GetDelegationPreferences(ctx, sender.String()) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } + if len(delegationPref.Preferences) != 1 { + return sdk.ZeroInt(), sdk.ZeroDec(), types.MultipleValFromValsetError{} + } + valOperAddr := delegationPref.Preferences[0].ValOperAddress + valAddr, err := sdk.ValAddressFromBech32(valOperAddr) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), fmt.Errorf("validator address not formatted") + } + validator, found := k.sk.GetValidator(ctx, valAddr) + if !found { + return sdk.ZeroInt(), sdk.ZeroDec(), fmt.Errorf("validator not found %s", validator) + } + val = validator + } else { + val, err = k.validateValAddrForDelegate(ctx, valAddr) + if err != nil { + return sdk.ZeroInt(), sdk.ZeroDec(), err + } } // delegate now! diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 1472ccd9eb0..6ae607191ec 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1320,13 +1320,22 @@ func (s *KeeperTestSuite) TestValidateUnbondConvertAndStake() { func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { type tc struct { - useInvalidValAddr bool - useMinAmtToStake bool + useInvalidValAddr bool + useMinAmtToStake bool + useValSetPrefSingleVal bool + useValSetPrefMultipleVal bool expectedError bool } testCases := map[string]tc{ "happy case": {}, + "use val set preference (single validator)": { + useValSetPrefSingleVal: true, + }, + "error: multiple validator returned from valset pref": { + useValSetPrefMultipleVal: true, + expectedError: true, + }, "error: invalid val address": { useInvalidValAddr: true, expectedError: true, @@ -1355,6 +1364,30 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { if tc.useInvalidValAddr { valAddrString = s.TestAccs[0].String() } + + stakeCoin := sdk.NewInt64Coin(bondDenom, 100000) + if tc.useValSetPrefSingleVal || tc.useValSetPrefMultipleVal { + valAddrString = "" + + s.FundAcc(sender, sdk.NewCoins(stakeCoin)) + validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr) + s.Require().True(found) + + _, err = s.App.StakingKeeper.Delegate(s.Ctx, sender, stakeCoin.Amount, stakingtypes.Unbonded, validator, true) + s.Require().NoError(err) + } + + // if test case is setting multiple validator, stake one more time to a different validator + if tc.useValSetPrefMultipleVal { + valAddr2 := s.SetupValidator(stakingtypes.Bonded) + stakeCoin := sdk.NewInt64Coin(bondDenom, 100000) + s.FundAcc(sender, sdk.NewCoins(stakeCoin)) + validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr2) + s.Require().True(found) + _, err = s.App.StakingKeeper.Delegate(s.Ctx, sender, stakeCoin.Amount, stakingtypes.Unbonded, validator, true) + s.Require().NoError(err) + } + minAmtToStake := sdk.ZeroInt() if tc.useMinAmtToStake { minAmtToStake = sdk.NewInt(999999999) @@ -1362,7 +1395,7 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { // mark expected shares before swap nonStakeDenomCoin := exitCoins.FilterDenoms([]string{"foo"})[0] - stakeDenomCoin := exitCoins.AmountOf("stake") + stakeDenomCoin := exitCoins.AmountOf(bondDenom) // use cache context to get expected amount after swap without changing test state cc, _ := s.Ctx.CacheContext() tokenOutAmt, err := s.App.PoolManagerKeeper.SwapExactAmountIn(cc, sender, poolId, nonStakeDenomCoin, bondDenom, sdk.ZeroInt()) @@ -1391,7 +1424,12 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { // check staking delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) s.Require().True(found) - s.Require().True(delegation.Shares.Equal(shares)) + // if we have used val set pref, we also need to count extra amount we have delegated + if tc.useValSetPrefSingleVal { + s.Require().True(delegation.Shares.Sub(stakeCoin.Amount.ToDec()).Equal(shares)) + } else { + s.Require().True(delegation.Shares.Equal(shares)) + } // check pool pool, err = s.App.GAMMKeeper.GetPoolAndPoke(s.Ctx, poolId) diff --git a/x/superfluid/types/errors.go b/x/superfluid/types/errors.go index 61dbf5625af..3038e54345a 100644 --- a/x/superfluid/types/errors.go +++ b/x/superfluid/types/errors.go @@ -126,3 +126,10 @@ type TokenConvertedLessThenDesiredStakeError struct { func (e TokenConvertedLessThenDesiredStakeError) Error() string { return fmt.Sprintf("actual amount converted to stake (%s) is less then minimum amount expected to be staked (%s)", e.ActualTotalAmtToStake, e.ExpectedTotalAmtToStake) } + +type MultipleValFromValsetError struct { +} + +func (e MultipleValFromValsetError) Error() string { + return fmt.Sprintf("Multiple validator from valset-preference exists") +} diff --git a/x/superfluid/types/expected_keepers.go b/x/superfluid/types/expected_keepers.go index 38c9dfcd3ca..cb9ad0db2a8 100644 --- a/x/superfluid/types/expected_keepers.go +++ b/x/superfluid/types/expected_keepers.go @@ -13,6 +13,7 @@ import ( gammmigration "github.com/osmosis-labs/osmosis/v17/x/gamm/types/migration" incentivestypes "github.com/osmosis-labs/osmosis/v17/x/incentives/types" lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" + valsetpreftypes "github.com/osmosis-labs/osmosis/v17/x/valset-pref/types" epochstypes "github.com/osmosis-labs/osmosis/x/epochs/types" ) @@ -132,3 +133,7 @@ type PoolManagerKeeper interface { tokenOutMinAmount sdk.Int, ) (sdk.Int, error) } + +type ValSetPreferenceKeeper interface { + GetDelegationPreferences(ctx sdk.Context, delegator string) (valsetpreftypes.ValidatorSetPreferences, error) +} diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index 4063455c71a..601192b561f 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -1023,13 +1023,19 @@ func (m *MsgAddToConcentratedLiquiditySuperfluidPositionResponse) GetLockId() ui // ===================== MsgUnbondConvertAndStake type MsgUnbondConvertAndStake struct { - // 0 for no lock. - LockId uint64 `protobuf:"varint,1,opt,name=lock_id,json=lockId,proto3" json:"lock_id,omitempty" yaml:"lock_id"` - Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + // lock ID to convert and stake. + // lock id with 0 should be provided if converting liquid gamm shares to stake + LockId uint64 `protobuf:"varint,1,opt,name=lock_id,json=lockId,proto3" json:"lock_id,omitempty" yaml:"lock_id"` + Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + // validator address to delegate to. + // If provided empty string, we use validator returned from valset-preference + // module. ValAddr string `protobuf:"bytes,3,opt,name=val_addr,json=valAddr,proto3" json:"val_addr,omitempty"` // min_amt_to_stake indicates the minimum amount to stake after conversion - MinAmtToStake github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=min_amt_to_stake,json=minAmtToStake,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_amt_to_stake" yaml:"min_amt_to_stake"` - SharesToConvertAndStake types.Coin `protobuf:"bytes,5,opt,name=shares_to_convert_and_stake,json=sharesToConvertAndStake,proto3" json:"shares_to_convert_and_stake" yaml:"shares_to_convert_and_stake"` + MinAmtToStake github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=min_amt_to_stake,json=minAmtToStake,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_amt_to_stake" yaml:"min_amt_to_stake"` + // shares_to_convert_and_stake indicates amount of gamm shares to convert and + // stake. + SharesToConvertAndStake types.Coin `protobuf:"bytes,5,opt,name=shares_to_convert_and_stake,json=sharesToConvertAndStake,proto3" json:"shares_to_convert_and_stake" yaml:"shares_to_convert_and_stake"` } func (m *MsgUnbondConvertAndStake) Reset() { *m = MsgUnbondConvertAndStake{} } From 4986e0e4fc6faa0f41d470597efbc372a938e6fe Mon Sep 17 00:00:00 2001 From: mattverse Date: Sat, 5 Aug 2023 00:14:48 +0900 Subject: [PATCH 05/27] Add changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c03b678c98..e7945206015 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#5883](https://github.com/osmosis-labs/osmosis/pull/5883) feat: Uninitialize empty ticks * [#5874](https://github.com/osmosis-labs/osmosis/pull/5874) Remove Partial Migration from superfluid migration to CL * [#5901](https://github.com/osmosis-labs/osmosis/pull/5901) Adding support for CW pools in ProtoRev +* [#5949](https://github.com/osmosis-labs/osmosis/pull/5949) Add message to convert from superfluid / locks to native staking directly. ### BugFix From 600031343d6355fe689de899cf40ed25f271f6fc Mon Sep 17 00:00:00 2001 From: devbot-wizard <141283918+devbot-wizard@users.noreply.github.com> Date: Mon, 7 Aug 2023 15:37:18 +0000 Subject: [PATCH 06/27] Fix simple problems --- CHANGELOG.md | 5 +---- go.sum | 7 ------- x/superfluid/types/tx.pb.go | 9 +++++---- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34328e114ba..5232ac95952 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,11 +51,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#5883](https://github.com/osmosis-labs/osmosis/pull/5883) feat: Uninitialize empty ticks * [#5874](https://github.com/osmosis-labs/osmosis/pull/5874) Remove Partial Migration from superfluid migration to CL * [#5901](https://github.com/osmosis-labs/osmosis/pull/5901) Adding support for CW pools in ProtoRev -<<<<<<< HEAD -* [#5949](https://github.com/osmosis-labs/osmosis/pull/5949) Add message to convert from superfluid / locks to native staking directly. -======= * [#5937](https://github.com/osmosis-labs/osmosis/pull/5937) feat: add SetScalingFactorController gov prop ->>>>>>> main +* [#5949](https://github.com/osmosis-labs/osmosis/pull/5949) Add message to convert from superfluid / locks to native staking directly. ### BugFix diff --git a/go.sum b/go.sum index 02e989f68e0..a57c409861e 100644 --- a/go.sum +++ b/go.sum @@ -951,17 +951,10 @@ github.com/osmosis-labs/cosmos-sdk v0.45.0-rc1.0.20230703010110-ed4eb883f2a6 h1: github.com/osmosis-labs/cosmos-sdk v0.45.0-rc1.0.20230703010110-ed4eb883f2a6/go.mod h1:9KGhMg+7ZWgZ50Wa/x8w/jN19O0TSqYLlqUj+2wwxLU= github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3 h1:YlmchqTmlwdWSmrRmXKR+PcU96ntOd8u10vTaTZdcNY= github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3/go.mod h1:lV6KnqXYD/ayTe7310MHtM3I2q8Z6bBfMAi+bhwPYtI= -<<<<<<< HEAD -github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230629191111-f375469de8b6 h1:Kmkx5Rh72+LB8AL6dc6fZA+IVR0INu0YIiMF2ScDhaQ= -github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230629191111-f375469de8b6/go.mod h1:JTym95/bqrSnG5MPcXr1YDhv43JdCeo3p+iDbazoX68= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230728163612-426afac90c44 h1:UOaBVxEMMv2FS1znU7kHBdtSeZQIjnmXL4r9r19XyBo= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230728163612-426afac90c44/go.mod h1:Pl8Nzx6O6ow/+aqfMoMSz4hX+zz6RrnDYsooptECGxM= -======= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230804142026-a81cfe3ddde7 h1:NTR4zfrPMP4pJ5T60zyZumBAnTWmTAQX/JSZLGrM9jI= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230804142026-a81cfe3ddde7/go.mod h1:UlftwozB+QObT3o0YfkuuyL9fsVdgoWt0dm6J7MLYnU= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230804142026-a81cfe3ddde7 h1:uwP/LzPE/edqNKf/Tpn8y9L6EO3JHOVwb+zq1gjL1hE= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230804142026-a81cfe3ddde7/go.mod h1:Pl8Nzx6O6ow/+aqfMoMSz4hX+zz6RrnDYsooptECGxM= ->>>>>>> main github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304 h1:RIrWLzIiZN5Xd2JOfSOtGZaf6V3qEQYg6EaDTAkMnCo= github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304/go.mod h1:yPWoJTj5RKrXKUChAicp+G/4Ni/uVEpp27mi/FF/L9c= github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.6 h1:PjfLL5rwwm44CeLnNQssrFgmj4BdeIS5DriKYhGz7IM= diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index 601192b561f..4562e228c76 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -6,6 +6,11 @@ package types import ( context "context" fmt "fmt" + io "io" + math "math" + math_bits "math/bits" + time "time" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/cosmos-sdk/types/tx/amino" @@ -17,10 +22,6 @@ import ( grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" - io "io" - math "math" - math_bits "math/bits" - time "time" ) // Reference imports to suppress errors if they are not otherwise used. From 343be7363326e5405403f195716a554be168f63e Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Mon, 7 Aug 2023 17:43:59 +0200 Subject: [PATCH 07/27] Update tx.pb.go --- x/superfluid/types/tx.pb.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index 4562e228c76..601192b561f 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -6,11 +6,6 @@ package types import ( context "context" fmt "fmt" - io "io" - math "math" - math_bits "math/bits" - time "time" - github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/types" _ "github.com/cosmos/cosmos-sdk/types/tx/amino" @@ -22,6 +17,10 @@ import ( grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + time "time" ) // Reference imports to suppress errors if they are not otherwise used. From 1bef46333cb2d4b3464f6779e33bea9275e1e41d Mon Sep 17 00:00:00 2001 From: mattverse Date: Tue, 8 Aug 2023 15:55:22 +0900 Subject: [PATCH 08/27] Remove partial unlocking --- proto/osmosis/superfluid/tx.proto | 6 - x/superfluid/keeper/export_test.go | 16 +- x/superfluid/keeper/migrate.go | 4 +- x/superfluid/keeper/migrate_test.go | 8 +- x/superfluid/keeper/msg_server.go | 2 +- x/superfluid/keeper/stake.go | 75 ++------- x/superfluid/keeper/stake_test.go | 126 +------------- x/superfluid/types/tx.pb.go | 249 +++++++++++----------------- 8 files changed, 122 insertions(+), 364 deletions(-) diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index dbf40ac52c2..25fe4f501e2 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -256,12 +256,6 @@ message MsgUnbondConvertAndStake { (gogoproto.moretags) = "yaml:\"min_amt_to_stake\"", (gogoproto.nullable) = false ]; - // shares_to_convert_and_stake indicates amount of gamm shares to convert and - // stake. - cosmos.base.v1beta1.Coin shares_to_convert_and_stake = 5 [ - (gogoproto.moretags) = "yaml:\"shares_to_convert_and_stake\"", - (gogoproto.nullable) = false - ]; } message MsgUnbondConvertAndStakeResponse { diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 8ead95ccf19..b05e4baf389 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -37,8 +37,8 @@ func (k Keeper) ForceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAd return k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins) } -func (k Keeper) RouteMigration(ctx sdk.Context, sender sdk.AccAddress, lockId int64, sharesToMigrate sdk.Coin) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { - return k.routeMigration(ctx, sender, lockId, sharesToMigrate) +func (k Keeper) RouteMigration(ctx sdk.Context, sender sdk.AccAddress, lockId int64) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { + return k.routeMigration(ctx, sender, lockId) } func (k Keeper) ValidateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (poolIdLeaving, poolIdEntering uint64, preMigrationLock *lockuptypes.PeriodLock, remainingLockTime time.Duration, err error) { @@ -62,16 +62,8 @@ func (k Keeper) DistributeSuperfluidGauges(ctx sdk.Context) { } func (k Keeper) ConvertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, lockId uint64, - sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { - return k.convertLockToStake(ctx, sender, valAddr, lockId, sharesToStake, minAmtToStake) -} -func (k Keeper) ConvertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, - sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { - return k.convertUnlockedToStake(ctx, sender, valAddr, sharesToStake, minAmtToStake) -} - -func (k Keeper) ValidateUnbondConvertAndStake(ctx sdk.Context, sharesToMigrate sdk.Coin) (poolIdLeaving uint64, err error) { - return k.validateUnbondConvertAndStake(ctx, sharesToMigrate) + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { + return k.convertLockToStake(ctx, sender, valAddr, lockId, minAmtToStake) } func (k Keeper) ConvertGammSharesToOsmoAndStake( diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index ef54b1f36be..a6d5016ae99 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -47,7 +47,7 @@ const ( // // Errors if the lock is not found, if the lock is not a balancer pool lock, or if the lock is not owned by the sender. func (k Keeper) RouteLockedBalancerToConcentratedMigration(ctx sdk.Context, sender sdk.AccAddress, providedLockId int64, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, poolIdLeaving, poolIdEntering, concentratedLockId uint64, err error) { - synthLockBeforeMigration, migrationType, err := k.routeMigration(ctx, sender, providedLockId, sharesToMigrate) + synthLockBeforeMigration, migrationType, err := k.routeMigration(ctx, sender, providedLockId) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } @@ -232,7 +232,7 @@ func (k Keeper) migrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, // routeMigration determines the status of the provided lock which is used to determine the method for migration. // It also returns the underlying synthetic locks of the provided lock, if any exist. -func (k Keeper) routeMigration(ctx sdk.Context, sender sdk.AccAddress, providedLockId int64, sharesToMigrate sdk.Coin) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { +func (k Keeper) routeMigration(ctx sdk.Context, sender sdk.AccAddress, providedLockId int64) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { // As a hack around to get frontend working, we decided to allow negative values for the provided lock ID to indicate that the user wants to migrate shares that are not locked. if providedLockId <= 0 { return lockuptypes.SyntheticLock{}, Unlocked, nil diff --git a/x/superfluid/keeper/migrate_test.go b/x/superfluid/keeper/migrate_test.go index 0d4fd50c25a..bf5f3eb325a 100644 --- a/x/superfluid/keeper/migrate_test.go +++ b/x/superfluid/keeper/migrate_test.go @@ -304,7 +304,7 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated() { // RouteMigration is called via the migration message router and is always run prior to the migration itself. // We use it here just to retrieve the synthetic lock before the migration. - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId), coinsToMigrate) + synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId)) s.Require().NoError(err) s.Require().Equal(migrationType, keeper.SuperfluidBonded) @@ -459,7 +459,7 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrated() coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() // RouteMigration is called via the migration message router and is always run prior to the migration itself - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId), coinsToMigrate) + synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId)) s.Require().NoError(err) s.Require().Equal(migrationType, keeper.SuperfluidUnbonding) @@ -574,7 +574,7 @@ func (s *KeeperTestSuite) TestMigrateNonSuperfluidLockBalancerToConcentrated() { coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() // RouteMigration is called via the migration message router and is always run prior to the migration itself - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId), coinsToMigrate) + synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId)) s.Require().NoError(err) s.Require().Equal((lockuptypes.SyntheticLock{}), synthLockBeforeMigration) s.Require().Equal(migrationType, keeper.NonSuperfluid) @@ -657,7 +657,7 @@ func (s *KeeperTestSuite) TestMigrateUnlockedPositionFromBalancerToConcentrated( coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() // RouteMigration is called via the migration message router and is always run prior to the migration itself - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, 0, coinsToMigrate) + synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, 0) s.Require().NoError(err) s.Require().Equal((lockuptypes.SyntheticLock{}), synthLockBeforeMigration) s.Require().Equal(migrationType, keeper.Unlocked) diff --git a/x/superfluid/keeper/msg_server.go b/x/superfluid/keeper/msg_server.go index 95a03291e42..7155949a81a 100644 --- a/x/superfluid/keeper/msg_server.go +++ b/x/superfluid/keeper/msg_server.go @@ -246,7 +246,7 @@ func (server msgServer) AddToConcentratedLiquiditySuperfluidPosition(goCtx conte func (server msgServer) UnbondConvertAndStake(goCtx context.Context, msg *types.MsgUnbondConvertAndStake) (*types.MsgUnbondConvertAndStakeResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - totalAmtConverted, totalSharesDelegated, err := server.keeper.UnbondConvertAndStake(ctx, msg.LockId, msg.Sender, msg.ValAddr, msg.MinAmtToStake, msg.SharesToConvertAndStake) + totalAmtConverted, totalSharesDelegated, err := server.keeper.UnbondConvertAndStake(ctx, msg.LockId, msg.Sender, msg.ValAddr, msg.MinAmtToStake) if err != nil { return nil, err } diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index c4841c4f63f..c88df045464 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -2,7 +2,6 @@ package keeper import ( "fmt" - "strings" errorsmod "cosmossdk.io/errors" @@ -663,39 +662,34 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn // UnbondConvertAndStake converts given lock to osmo and stakes it to given validator. // Supports conversion of 1)superfluid bonded 2)superfluid undelegating 3)vanilla unlocking 4) unlocked locks. // If valAddr is empty, we attempt to get staking preference from valset-pref module and stake to the given validator. -func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, minAmtToStake sdk.Int, - sharesToConvertAndStake sdk.Coin) (totalAmtConverted sdk.Int, totalSharesDelegated sdk.Dec, err error) { +func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, totalSharesDelegated sdk.Dec, err error) { senderAddr, err := sdk.AccAddressFromBech32(sender) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } // use routeMigration method to check status of lock(either superfluid staked, superfluid unbonding, vanialla locked, unlocked) - _, migrationType, err := k.routeMigration(ctx, senderAddr, int64(lockID), sharesToConvertAndStake) + _, migrationType, err := k.routeMigration(ctx, senderAddr, int64(lockID)) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } // if superfluid bonded, first change it into superfluid undelegate to burn minted osmo and instantly undelegate. - if migrationType == MigrationType(0) { + if migrationType == SuperfluidBonded { _, err = k.undelegateCommon(ctx, sender, lockID) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } } - if migrationType == MigrationType(0) || migrationType == MigrationType(1) || migrationType == MigrationType(2) { - totalAmtConverted, totalSharesDelegated, err = k.convertLockToStake(ctx, senderAddr, valAddr, lockID, sharesToConvertAndStake, minAmtToStake) + if migrationType == SuperfluidBonded || migrationType == SuperfluidUnbonding || migrationType == NonSuperfluid { + totalAmtConverted, totalSharesDelegated, err = k.convertLockToStake(ctx, senderAddr, valAddr, lockID, minAmtToStake) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } - } else if migrationType == MigrationType(3) { - totalAmtConverted, totalSharesDelegated, err = k.convertUnlockedToStake(ctx, senderAddr, valAddr, sharesToConvertAndStake, minAmtToStake) - if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err - } - } else { - return sdk.ZeroInt(), sdk.ZeroDec(), fmt.Errorf("unsupported migration type") + } else { // liquid gamm shares without locks are not supported + return sdk.ZeroInt(), sdk.ZeroDec(), fmt.Errorf("unsupported staking conversion type") } return totalAmtConverted, totalSharesDelegated, nil @@ -704,22 +698,21 @@ func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, va // convertLockToStake handles locks that are superfluid bonded, superfluid unbonding, vanilla locked(unlocking) locks. // Deletes all associated state, converts the lock itself to staking delegation by going through exit pool and swap. func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, lockId uint64, - sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { - // do basic validation before converting - poolIdLeaving, err := k.validateUnbondConvertAndStake(ctx, sharesToStake) + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { + lock, err := k.lk.GetLockByID(ctx, lockId) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } - // Check that lockID corresponds to sender and that the denomination of LP shares corresponds to the poolId. - lock, err := k.validateGammLockForSuperfluidStaking(ctx, sender, poolIdLeaving, lockId) + lockCoin := lock.Coins[0] + poolIdLeaving, err := gammtypes.GetPoolIdFromShareDenom(lockCoin.Denom) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } // Force unlock, validate the provided sharesToStake, and exit the balancer pool. // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. - exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToStake, sdk.NewCoins()) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, lockCoin, sdk.NewCoins()) if err != nil { return sdk.ZeroInt(), sdk.ZeroDec(), err } @@ -732,48 +725,6 @@ func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAd return totalAmtConverted, shares, nil } -// convertUnlockedToStake converts unlocked lock shares to osmo and stakes. -func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, - sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { - // do basic validation before converting - poolIdLeaving, err := k.validateUnbondConvertAndStake(ctx, sharesToStake) - if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err - } - - // Exit the balancer pool position. - // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. - exitCoins, err := k.gk.ExitPool(ctx, sender, poolIdLeaving, sharesToStake.Amount, sdk.NewCoins()) - if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err - } - - totalAmtConverted, shares, err = k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake) - if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err - } - - return totalAmtConverted, shares, nil -} - -// validateUnbondConvertAndStake validates -// - given token has gamm share prefix -// - gamm share denom is in correct format -func (k Keeper) validateUnbondConvertAndStake(ctx sdk.Context, sharesToStake sdk.Coin) (poolIdLeaving uint64, err error) { - // Defense in depth, ensuring the sharesToMigrate contains gamm pool share prefix. - if !strings.HasPrefix(sharesToStake.Denom, gammtypes.GAMMTokenPrefix) { - return 0, types.SharesToMigrateDenomPrefixError{Denom: sharesToStake.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} - } - - // Get the balancer poolId by parsing the gamm share denom. - poolIdLeaving, err = gammtypes.GetPoolIdFromShareDenom(sharesToStake.Denom) - if err != nil { - return 0, err - } - - return poolIdLeaving, nil -} - // convertGammSharesToOsmoAndStake converts given gamm shares to osmo by swapping in the given pool // then stakes it to the designated validator. // minAmtToStake works as slippage bound, and would error if total amount being staked is less than min amount to stake. diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 6ae607191ec..20a599583b2 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1066,13 +1066,12 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { // testing params sender := sdk.MustAccAddressFromBech32(lock.Owner) valAddr := s.SetupValidator(stakingtypes.Bonded) - sharesToStake := lock.Coins minAmountToStake := sdk.ZeroInt() balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) // system under test - totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lock.ID, sender.String(), valAddr.String(), minAmountToStake, sharesToStake[0]) + totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lock.ID, sender.String(), valAddr.String(), minAmountToStake) if tc.expectedError { s.Require().Error(err) return @@ -1118,7 +1117,6 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { unlocking bool useMinAmountToStake bool - usepartialShares bool senderIsNotOwnerOfLock bool expectedError bool @@ -1138,14 +1136,6 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { useMinAmountToStake: true, expectedError: true, }, - "error: use partial shares": { - usepartialShares: true, - expectedError: true, - }, - "error: use different lock ": { - usepartialShares: true, - expectedError: true, - }, "error: sender is not owner of lock ": { senderIsNotOwnerOfLock: true, expectedError: true, @@ -1165,10 +1155,6 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { sender = s.TestAccs[1] } valAddr := s.SetupValidator(stakingtypes.Bonded) - sharesToStake := lock.Coins - if tc.usepartialShares { - sharesToStake[0].Amount = lock.Coins[0].Amount.Quo(sdk.NewInt(2)) - } minAmountToStake := sdk.ZeroInt() if tc.useMinAmountToStake { minAmountToStake = sdk.NewInt(999999999) @@ -1177,7 +1163,7 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) // system under test - totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.ConvertLockToStake(s.Ctx, sender, valAddr.String(), lock.ID, sharesToStake[0], minAmountToStake) + totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.ConvertLockToStake(s.Ctx, sender, valAddr.String(), lock.ID, minAmountToStake) if tc.expectedError { s.Require().Error(err) return @@ -1210,114 +1196,6 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { } } -func (s *KeeperTestSuite) TestConvertUnlockedToStake() { - defaultJoinTime := s.Ctx.BlockTime() - type tc struct { - usePartialShares bool - useMinAmountToStake bool - expectedError bool - } - testCases := map[string]tc{ - "convert unlocked gamm shares": {}, - "convert partial shares": { - usePartialShares: true, - }, - "min amount to stake exceeds exit pool amount": { - useMinAmountToStake: true, - expectedError: true, - }, - } - - for name, tc := range testCases { - s.Run(name, func() { - s.SetupTest() - s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) - - // We bundle all migration setup into a single function to avoid repeating the same code for each test case. - _, _, _, _, sender, poolId, shareOut, _ := s.SetupUnbondConvertAndStakeTest(s.Ctx, false, false, false, true) - - // testing params - valAddr := s.SetupValidator(stakingtypes.Bonded) - minAmtToStake := sdk.ZeroInt() - if tc.useMinAmountToStake { - minAmtToStake = sdk.NewInt(9999999999) - } - sharesToStake := shareOut - if tc.usePartialShares { - sharesToStake.Amount = sharesToStake.Amount.Quo(sdk.NewInt(2)) - } - - balanceBeforeConvert := s.App.BankKeeper.GetBalance(s.Ctx, sender, shareOut.Denom) - s.Require().True(!balanceBeforeConvert.Amount.IsZero()) - - bondDenom := s.App.StakingKeeper.BondDenom(s.Ctx) - totalPoolLiquidityBeforeConvert, err := s.App.GAMMKeeper.GetTotalPoolLiquidity(s.Ctx, poolId) - s.Require().NoError(err) - bondDenomPoolAmtBeforeConvert := totalPoolLiquidityBeforeConvert.AmountOf(bondDenom) - - // system under test - totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.ConvertUnlockedToStake(s.Ctx, sender, valAddr.String(), shareOut, minAmtToStake) - if tc.expectedError { - s.Require().Error(err) - return - } - s.Require().NoError(err) - - // gamm check - totalPoolLiquidityAfterConvert, err := s.App.GAMMKeeper.GetTotalPoolLiquidity(s.Ctx, poolId) - s.Require().NoError(err) - // check that pool liquidity have reduced - bondDenomPoolAmtAfterConvert := totalPoolLiquidityAfterConvert.AmountOf(bondDenom) - s.Require().True(bondDenomPoolAmtAfterConvert.LT(bondDenomPoolAmtBeforeConvert)) - - // Staking & Delegation check - // check if delegation amount matches - delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) - s.Require().True(found) - s.Require().Equal(delegation.Shares, sharesDelegated) - s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) - - // Bank check - balanceAfterConvertLockToStake := s.App.BankKeeper.GetBalance(s.Ctx, sender, shareOut.Denom) - s.Require().True(balanceAfterConvertLockToStake.IsZero()) - }) - } -} - -func (s *KeeperTestSuite) TestValidateUnbondConvertAndStake() { - type tc struct { - sharesToMigrate sdk.Coin - expectedError bool - } - testCases := map[string]tc{ - "happy case": { - sharesToMigrate: sdk.NewInt64Coin("gamm/pool/1", 100), - }, - "non gamm token prefix": { - sharesToMigrate: sdk.NewInt64Coin("uosmo", 100), - expectedError: true, - }, - "invalid gamm token": { - sharesToMigrate: sdk.NewInt64Coin("gamm/pool/invalid", 100), - expectedError: true, - }, - } - - for name, tc := range testCases { - s.Run(name, func() { - s.SetupTest() - - poolIdLeaving, err := s.App.SuperfluidKeeper.ValidateUnbondConvertAndStake(s.Ctx, tc.sharesToMigrate) - if tc.expectedError { - s.Require().Error(err) - return - } - s.Require().NoError(err) - s.Require().Equal(poolIdLeaving, uint64(1)) - }) - } -} - func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { type tc struct { useInvalidValAddr bool diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index 601192b561f..c9727e0bfa2 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -1033,9 +1033,6 @@ type MsgUnbondConvertAndStake struct { ValAddr string `protobuf:"bytes,3,opt,name=val_addr,json=valAddr,proto3" json:"val_addr,omitempty"` // min_amt_to_stake indicates the minimum amount to stake after conversion MinAmtToStake github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=min_amt_to_stake,json=minAmtToStake,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_amt_to_stake" yaml:"min_amt_to_stake"` - // shares_to_convert_and_stake indicates amount of gamm shares to convert and - // stake. - SharesToConvertAndStake types.Coin `protobuf:"bytes,5,opt,name=shares_to_convert_and_stake,json=sharesToConvertAndStake,proto3" json:"shares_to_convert_and_stake" yaml:"shares_to_convert_and_stake"` } func (m *MsgUnbondConvertAndStake) Reset() { *m = MsgUnbondConvertAndStake{} } @@ -1092,13 +1089,6 @@ func (m *MsgUnbondConvertAndStake) GetValAddr() string { return "" } -func (m *MsgUnbondConvertAndStake) GetSharesToConvertAndStake() types.Coin { - if m != nil { - return m.SharesToConvertAndStake - } - return types.Coin{} -} - type MsgUnbondConvertAndStakeResponse struct { TotalAmtStaked github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=total_amt_staked,json=totalAmtStaked,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_amt_staked" yaml:"total_amt_staked"` TotalSharesDelegated github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=total_shares_delegated,json=totalSharesDelegated,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"total_shares_delegated"` @@ -1163,104 +1153,102 @@ func init() { func init() { proto.RegisterFile("osmosis/superfluid/tx.proto", fileDescriptor_55b645f187d22814) } var fileDescriptor_55b645f187d22814 = []byte{ - // 1538 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcf, 0x6f, 0x13, 0xc7, - 0x17, 0xcf, 0xda, 0x21, 0x81, 0x09, 0x09, 0xc9, 0x7e, 0x03, 0x31, 0x06, 0x6c, 0x33, 0xfc, 0xf8, - 0x86, 0x1f, 0xf6, 0xc6, 0xf0, 0xfd, 0x02, 0xca, 0x89, 0x38, 0x16, 0x95, 0x69, 0xac, 0xa2, 0x25, - 0xa8, 0x12, 0x17, 0x6b, 0xed, 0x99, 0x2c, 0xdb, 0xec, 0xee, 0x98, 0x9d, 0x71, 0x12, 0xd4, 0x53, - 0xcb, 0xa1, 0x12, 0x27, 0x8e, 0xbd, 0xf5, 0xdc, 0x1e, 0x10, 0x7f, 0x42, 0x0f, 0x3d, 0xa0, 0x9e, - 0x38, 0x56, 0xad, 0x14, 0x2a, 0x38, 0xf4, 0x9e, 0x4b, 0x4f, 0x95, 0xaa, 0xd9, 0x9d, 0x5d, 0xaf, - 0xed, 0xdd, 0x38, 0x6b, 0xd2, 0x43, 0x2f, 0xe0, 0x9d, 0x79, 0xf3, 0xde, 0xe7, 0xbd, 0x79, 0x9f, - 0xf7, 0xde, 0x04, 0x9c, 0x21, 0xd4, 0x22, 0xd4, 0xa0, 0x0a, 0xed, 0xb4, 0xb1, 0xb3, 0x61, 0x76, - 0x0c, 0xa4, 0xb0, 0x9d, 0x52, 0xdb, 0x21, 0x8c, 0xc8, 0xb2, 0xd8, 0x2c, 0x75, 0x37, 0xb3, 0xf3, - 0x3a, 0xd1, 0x89, 0xbb, 0xad, 0xf0, 0x5f, 0x9e, 0x64, 0x76, 0x4e, 0xb3, 0x0c, 0x9b, 0x28, 0xee, - 0xbf, 0x62, 0x29, 0xa7, 0x13, 0xa2, 0x9b, 0x58, 0x71, 0xbf, 0x9a, 0x9d, 0x0d, 0x05, 0x75, 0x1c, - 0x8d, 0x19, 0xc4, 0xf6, 0xf7, 0x5b, 0xae, 0x76, 0xa5, 0xa9, 0x51, 0xac, 0x6c, 0x95, 0x9b, 0x98, - 0x69, 0x65, 0xa5, 0x45, 0x0c, 0x7f, 0x3f, 0xdf, 0x7f, 0x9e, 0x19, 0x16, 0xa6, 0x4c, 0xb3, 0xda, - 0x42, 0xe0, 0x42, 0x04, 0xf4, 0xee, 0x4f, 0x4f, 0x08, 0x7e, 0x2b, 0x81, 0x93, 0x75, 0xaa, 0x3f, - 0x0c, 0xd6, 0xab, 0xd8, 0xc4, 0xba, 0xc6, 0xb0, 0x7c, 0x05, 0x4c, 0x50, 0x6c, 0x23, 0xec, 0x64, - 0xa4, 0x82, 0xb4, 0x78, 0xac, 0x32, 0xb7, 0xb7, 0x9b, 0x9f, 0x7e, 0xa6, 0x59, 0xe6, 0x32, 0xf4, - 0xd6, 0xa1, 0x2a, 0x04, 0xe4, 0x05, 0x30, 0x69, 0x92, 0xd6, 0x66, 0xc3, 0x40, 0x99, 0x54, 0x41, - 0x5a, 0x1c, 0x57, 0x27, 0xf8, 0x67, 0x0d, 0xc9, 0xa7, 0xc1, 0xd1, 0x2d, 0xcd, 0x6c, 0x68, 0x08, - 0x39, 0x99, 0x34, 0xd7, 0xa2, 0x4e, 0x6e, 0x69, 0xe6, 0x0a, 0x42, 0xce, 0x72, 0xe1, 0xc5, 0x1f, - 0xaf, 0xaf, 0x46, 0x44, 0xb7, 0x88, 0x04, 0x00, 0x98, 0x07, 0xe7, 0x22, 0x91, 0xa9, 0x98, 0xb6, - 0x89, 0x4d, 0x31, 0xfc, 0x4a, 0x02, 0x0b, 0x3d, 0x12, 0x8f, 0x6c, 0x74, 0x88, 0xe8, 0x97, 0x21, - 0x87, 0x78, 0x2e, 0x02, 0x62, 0x27, 0xb0, 0x03, 0xcf, 0x83, 0x7c, 0x0c, 0x84, 0x00, 0xe6, 0xd7, - 0x83, 0x30, 0x9b, 0xc4, 0x46, 0x6b, 0xa4, 0xb5, 0x79, 0x28, 0x30, 0x2f, 0x70, 0x98, 0xb9, 0x48, - 0x98, 0xdc, 0x4e, 0x91, 0x8b, 0x45, 0xe0, 0xf4, 0x31, 0x04, 0x38, 0x5f, 0x49, 0xe0, 0x62, 0x8c, - 0x2f, 0x2b, 0xf6, 0x21, 0x83, 0x96, 0x2b, 0x60, 0x9c, 0xe7, 0xb2, 0x9b, 0x15, 0x53, 0x37, 0x4e, - 0x97, 0xbc, 0x64, 0x2f, 0xf1, 0x64, 0x2f, 0x89, 0x64, 0x2f, 0xad, 0x12, 0xc3, 0xae, 0xfc, 0xe7, - 0xcd, 0x6e, 0x7e, 0x6c, 0x6f, 0x37, 0x3f, 0xe5, 0x19, 0xe0, 0x87, 0xa0, 0xea, 0x9e, 0x85, 0x9f, - 0x80, 0xeb, 0x07, 0xc1, 0xeb, 0x3b, 0x18, 0x06, 0x23, 0x85, 0xc1, 0xc0, 0x3d, 0x09, 0x9c, 0xad, - 0x53, 0x9d, 0x0b, 0xaf, 0xd8, 0xe8, 0xe3, 0xb8, 0xa0, 0x81, 0x23, 0x1c, 0x1c, 0xcd, 0xa4, 0x0a, - 0xe9, 0xfd, 0x3d, 0x5b, 0xe2, 0x9e, 0xfd, 0xf0, 0x2e, 0xbf, 0xa8, 0x1b, 0xec, 0x49, 0xa7, 0x59, - 0x6a, 0x11, 0x4b, 0x11, 0x9c, 0xf7, 0xfe, 0x2b, 0x52, 0xb4, 0xa9, 0xb0, 0x67, 0x6d, 0x4c, 0xdd, - 0x03, 0x54, 0xf5, 0x34, 0xef, 0xc7, 0xaa, 0x2b, 0x3c, 0x17, 0x2e, 0xfa, 0xb9, 0xc0, 0xdd, 0x2b, - 0x6a, 0x36, 0x2a, 0x46, 0xd1, 0xeb, 0x96, 0x7b, 0xdb, 0xb1, 0x3e, 0x07, 0x51, 0x9b, 0x01, 0xa9, - 0x5a, 0x55, 0x04, 0x2c, 0x55, 0xab, 0xc2, 0xd7, 0x29, 0xa0, 0xd4, 0xa9, 0xbe, 0xea, 0x60, 0x8d, - 0xe1, 0x7b, 0x1d, 0xd3, 0x54, 0x35, 0x5b, 0xc7, 0x0f, 0x08, 0x35, 0x78, 0xf1, 0xfa, 0x77, 0xc7, - 0x4f, 0xbe, 0x06, 0x26, 0xdb, 0x84, 0x98, 0x3c, 0x45, 0xc6, 0xb9, 0xc7, 0x15, 0x79, 0x6f, 0x37, - 0x3f, 0xe3, 0x21, 0x15, 0x1b, 0x50, 0x9d, 0xe0, 0xbf, 0x6a, 0x68, 0xf9, 0xbf, 0x3c, 0xd8, 0xd0, - 0x0f, 0xf6, 0x46, 0xc7, 0x34, 0x8b, 0x0e, 0x8f, 0x85, 0x17, 0xf2, 0x8d, 0x6e, 0xa8, 0x9f, 0x82, - 0xdb, 0x09, 0x23, 0x16, 0x44, 0xff, 0x14, 0xf0, 0x92, 0xb4, 0xda, 0x93, 0xb2, 0x55, 0x39, 0x07, - 0x40, 0x5b, 0x28, 0xa8, 0x55, 0x05, 0xb7, 0x42, 0x2b, 0xbc, 0xae, 0x67, 0xea, 0x54, 0x7f, 0x64, - 0x3f, 0x20, 0xc4, 0xfc, 0xfc, 0x89, 0xc1, 0xb0, 0x69, 0x50, 0x86, 0x11, 0xff, 0x4c, 0x72, 0x1d, - 0xa1, 0x80, 0xa4, 0x86, 0x06, 0xe4, 0x22, 0x0f, 0x48, 0xde, 0x0f, 0x48, 0xc7, 0xe6, 0xcb, 0xc5, - 0xed, 0xae, 0xf1, 0x22, 0x5f, 0x80, 0xf7, 0x41, 0x21, 0x0e, 0x59, 0xe0, 0xf6, 0x65, 0x70, 0x02, - 0xef, 0x18, 0x0c, 0xa3, 0x86, 0x60, 0x2c, 0xcd, 0x48, 0x85, 0xf4, 0xe2, 0xb8, 0x3a, 0xed, 0x2d, - 0xaf, 0xb9, 0xc4, 0xa5, 0xf0, 0xfb, 0x34, 0xb8, 0xe3, 0x2a, 0x33, 0xbd, 0x3c, 0xae, 0x1b, 0xba, - 0xa3, 0x31, 0xfc, 0xf0, 0x89, 0xe6, 0x60, 0xba, 0x4e, 0x82, 0x60, 0xaf, 0x12, 0xbb, 0x85, 0x6d, - 0xc6, 0xf7, 0x90, 0x1f, 0xf8, 0x84, 0x61, 0x08, 0xd7, 0xb1, 0x74, 0x38, 0x0c, 0x62, 0x03, 0x06, - 0xb5, 0x4d, 0x07, 0x73, 0xd4, 0x05, 0xd0, 0x60, 0xa4, 0x61, 0x79, 0x88, 0x86, 0x17, 0xba, 0x82, - 0x28, 0x74, 0x19, 0x81, 0xa0, 0x5f, 0x03, 0x54, 0x4f, 0x50, 0xe1, 0x96, 0xf0, 0x52, 0x7e, 0x21, - 0x81, 0x19, 0x46, 0x36, 0xb1, 0xdd, 0x20, 0x1d, 0xd6, 0xb0, 0x38, 0x6b, 0xc6, 0x87, 0xb1, 0xa6, - 0x26, 0xcc, 0x9c, 0xf4, 0xcc, 0xf4, 0x1e, 0x87, 0x89, 0xe8, 0x74, 0xdc, 0x3d, 0xfc, 0x59, 0x87, - 0xd5, 0x0d, 0x9b, 0x2e, 0xe7, 0xf9, 0xe5, 0x67, 0xbb, 0x97, 0x1f, 0x14, 0x1f, 0x1f, 0xff, 0xcf, - 0x69, 0x70, 0x77, 0xd4, 0xbb, 0x0a, 0x12, 0xe3, 0x31, 0x98, 0xd4, 0x2c, 0xd2, 0xb1, 0xd9, 0x92, - 0xb8, 0xb4, 0xbb, 0xdc, 0x9f, 0x5f, 0x77, 0xf3, 0x97, 0x0f, 0x00, 0xbb, 0x66, 0xb3, 0xee, 0xb5, - 0x09, 0x35, 0x50, 0xf5, 0x15, 0x76, 0x75, 0x97, 0xdd, 0x4b, 0xfe, 0x68, 0xdd, 0xe5, 0x40, 0x77, - 0x59, 0xde, 0x06, 0x73, 0xa6, 0xf1, 0xb4, 0x63, 0x20, 0x83, 0x3d, 0x6b, 0xb4, 0xdc, 0x4a, 0x80, - 0xbc, 0xe2, 0x53, 0xb9, 0x9f, 0xc0, 0x4a, 0x15, 0xb7, 0xba, 0x29, 0x32, 0xa0, 0x10, 0xaa, 0xb3, - 0xc1, 0x9a, 0x57, 0x6d, 0x90, 0xfc, 0x08, 0x1c, 0xfb, 0x82, 0x18, 0x76, 0x83, 0x4f, 0x87, 0x6e, - 0x4d, 0x9b, 0xba, 0x91, 0x2d, 0x79, 0xa3, 0x63, 0xc9, 0x1f, 0x1d, 0x4b, 0xeb, 0xfe, 0xe8, 0x58, - 0x39, 0x2b, 0xd2, 0x63, 0xd6, 0x33, 0x11, 0x1c, 0x85, 0x2f, 0xdf, 0xe5, 0x25, 0xf5, 0x28, 0xff, - 0xe6, 0xc2, 0xf0, 0x79, 0xda, 0xed, 0x02, 0x2b, 0x08, 0xad, 0x93, 0xf0, 0x85, 0xad, 0xf9, 0xf6, - 0xbb, 0x35, 0x2d, 0xe0, 0xdb, 0x6d, 0x30, 0xe5, 0x57, 0xa8, 0xa0, 0x07, 0x57, 0x4e, 0xed, 0xed, - 0xe6, 0x65, 0xbf, 0x9e, 0x04, 0x9b, 0x30, 0x54, 0xcc, 0x50, 0x88, 0xa8, 0xa9, 0x61, 0x44, 0x6d, - 0xf8, 0x8c, 0x40, 0x98, 0x1a, 0x0e, 0x46, 0x4b, 0xc3, 0x89, 0x77, 0x2e, 0x8a, 0x11, 0xfe, 0x71, - 0xa8, 0x4e, 0xbb, 0x0b, 0x55, 0xf1, 0x3d, 0x60, 0xa0, 0x2c, 0x82, 0x3a, 0xa2, 0x81, 0x72, 0x9f, - 0x81, 0xf2, 0xf2, 0x55, 0xce, 0xa3, 0x4b, 0x3e, 0x8f, 0x34, 0x84, 0x8a, 0x8c, 0x14, 0x5b, 0x66, - 0xb8, 0x87, 0xfb, 0xa1, 0x81, 0x3f, 0xa5, 0xdd, 0xce, 0x92, 0xe4, 0x16, 0x02, 0x26, 0x8d, 0x7c, - 0x1b, 0x21, 0x0a, 0xa6, 0xfe, 0x41, 0x0a, 0xa6, 0x0f, 0x9b, 0x82, 0x9b, 0x60, 0xda, 0xc6, 0xdb, - 0x8d, 0x80, 0x21, 0x99, 0x23, 0xae, 0x85, 0x7b, 0x89, 0xe9, 0x37, 0xef, 0x59, 0xe8, 0x51, 0x06, - 0xd5, 0xe3, 0x36, 0xde, 0x0e, 0xe2, 0x1e, 0x6e, 0x18, 0x03, 0x83, 0x44, 0x7f, 0xc3, 0x80, 0xaf, - 0xd2, 0xa2, 0x59, 0xf3, 0x91, 0x75, 0x95, 0xd8, 0x5b, 0xd8, 0x61, 0x7c, 0x2c, 0x60, 0xda, 0x26, - 0x0e, 0x6b, 0x92, 0x86, 0x69, 0x4a, 0xc2, 0x94, 0x7d, 0xa6, 0x20, 0x07, 0xcc, 0x5a, 0x86, 0xdd, - 0xd0, 0x2c, 0xc6, 0xfb, 0x0f, 0xe5, 0x30, 0x5c, 0x2f, 0x8e, 0x79, 0xdd, 0x23, 0xd1, 0x75, 0x2c, - 0x78, 0xd6, 0xfb, 0xf5, 0x41, 0x75, 0xda, 0x32, 0xec, 0x15, 0x8b, 0xad, 0x13, 0xcf, 0xcd, 0xe7, - 0x12, 0x38, 0xd3, 0xed, 0x79, 0x2d, 0x2f, 0x08, 0x0d, 0xcd, 0x46, 0xc2, 0xfe, 0x91, 0x61, 0x2c, - 0xbb, 0x2a, 0x58, 0x06, 0xfb, 0xfb, 0xe7, 0x80, 0x2e, 0xa8, 0x2e, 0xf8, 0x9d, 0xb4, 0x2f, 0xd8, - 0xcb, 0x97, 0x38, 0xf9, 0x0a, 0xdd, 0x26, 0xe6, 0x3e, 0xa0, 0x84, 0x02, 0x6f, 0xac, 0x73, 0x15, - 0xfc, 0x25, 0x89, 0x19, 0x26, 0xe2, 0xc2, 0x02, 0x82, 0x51, 0x30, 0xcb, 0x08, 0xe3, 0x21, 0xb6, - 0x98, 0x67, 0x18, 0x89, 0x9e, 0x35, 0x72, 0x14, 0xfb, 0xf5, 0x41, 0x75, 0xc6, 0x5d, 0x5a, 0xb1, - 0x98, 0x6b, 0x1b, 0xc9, 0x08, 0x9c, 0xf2, 0x84, 0x84, 0xff, 0xfe, 0x0c, 0x8a, 0x44, 0x42, 0x94, - 0x92, 0x65, 0xbb, 0x3a, 0xef, 0x6a, 0xf3, 0x7a, 0xb4, 0x3f, 0x9c, 0xa2, 0x1b, 0x7f, 0x4e, 0x81, - 0x74, 0x9d, 0xea, 0xb2, 0x03, 0xe4, 0xa8, 0x69, 0xbf, 0x34, 0xf8, 0x77, 0x91, 0x52, 0xe4, 0x53, - 0x3e, 0x5b, 0x3e, 0xb0, 0x68, 0x10, 0xd6, 0x1d, 0x30, 0x1f, 0xf9, 0xe2, 0xbf, 0x36, 0x54, 0x55, - 0x57, 0x38, 0x7b, 0x33, 0x81, 0x70, 0x9c, 0xe5, 0xe0, 0x3d, 0x7c, 0x10, 0xcb, 0xbe, 0xf0, 0x81, - 0x2c, 0x0f, 0xbc, 0x5c, 0xbf, 0x93, 0xc0, 0xf9, 0xe1, 0xef, 0xf2, 0x3b, 0x09, 0x9c, 0xea, 0x39, - 0x99, 0xbd, 0x3b, 0xea, 0xc9, 0x00, 0xe1, 0x37, 0x12, 0x38, 0x1d, 0xff, 0x7e, 0x5e, 0x8a, 0xd1, - 0x1f, 0x7b, 0x22, 0x7b, 0x27, 0xe9, 0x89, 0x00, 0xc9, 0x8f, 0x12, 0xb8, 0x9e, 0xe8, 0x71, 0xba, - 0x1a, 0x63, 0x2a, 0x89, 0x92, 0xec, 0xa7, 0x87, 0xa0, 0x24, 0x70, 0xe1, 0x4b, 0x70, 0x32, 0xfa, - 0xe1, 0x76, 0x3d, 0xc6, 0x4a, 0xa4, 0x74, 0xf6, 0x7f, 0x49, 0xa4, 0x03, 0xe3, 0xbf, 0x49, 0xe0, - 0xff, 0xa3, 0xbd, 0xa7, 0xd6, 0x62, 0xed, 0x8d, 0xa0, 0x2d, 0xbb, 0x7e, 0x98, 0xda, 0x7a, 0xb2, - 0x23, 0xd1, 0xd0, 0x1a, 0x97, 0x1d, 0x49, 0x94, 0xc4, 0x66, 0xc7, 0x48, 0x83, 0x9b, 0x9b, 0x1d, - 0x51, 0x93, 0x42, 0x7c, 0x76, 0x44, 0x48, 0xef, 0x93, 0x1d, 0xfb, 0x34, 0xb5, 0xca, 0x83, 0x37, - 0xef, 0x73, 0xd2, 0xdb, 0xf7, 0x39, 0xe9, 0xf7, 0xf7, 0x39, 0xe9, 0xe5, 0x87, 0xdc, 0xd8, 0xdb, - 0x0f, 0xb9, 0xb1, 0x5f, 0x3e, 0xe4, 0xc6, 0x1e, 0xdf, 0x0a, 0x75, 0x14, 0xa1, 0xb9, 0x68, 0x6a, - 0x4d, 0xea, 0x7f, 0x28, 0x5b, 0xe5, 0xdb, 0xca, 0x4e, 0xcf, 0xdf, 0xd1, 0x79, 0x97, 0x69, 0x4e, - 0xb8, 0xaf, 0x90, 0x9b, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x83, 0x52, 0xc3, 0x0d, 0x6a, 0x17, - 0x00, 0x00, + // 1505 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcd, 0x6f, 0x13, 0x47, + 0x1b, 0xcf, 0xda, 0x21, 0x81, 0x09, 0x09, 0xc9, 0xbe, 0x01, 0x8c, 0x5f, 0xb0, 0xcd, 0xf0, 0xf1, + 0x86, 0x0f, 0x7b, 0x63, 0x78, 0x0b, 0x28, 0x27, 0xe2, 0x58, 0x54, 0xa1, 0xb1, 0x8a, 0x96, 0xa0, + 0x4a, 0x5c, 0xac, 0xb5, 0x67, 0xb2, 0x6c, 0xb3, 0xbb, 0x63, 0x76, 0xc6, 0x49, 0x50, 0x4f, 0x6d, + 0x0f, 0x95, 0x38, 0x71, 0xec, 0xad, 0xd7, 0xb6, 0x87, 0x8a, 0x3f, 0xa1, 0x87, 0x1e, 0x50, 0x4f, + 0x1c, 0xab, 0x56, 0x0a, 0x15, 0x1c, 0x7a, 0xcf, 0xa5, 0xa7, 0x4a, 0xd5, 0xcc, 0xce, 0xae, 0xd7, + 0xc9, 0x6e, 0x9c, 0x35, 0xe9, 0xa1, 0x17, 0xf0, 0xce, 0x3c, 0xf3, 0x7b, 0x7e, 0xcf, 0x33, 0xcf, + 0xd7, 0x04, 0xfc, 0x97, 0x50, 0x87, 0x50, 0x8b, 0x6a, 0xb4, 0xdb, 0xc1, 0xde, 0x9a, 0xdd, 0xb5, + 0x90, 0xc6, 0xb6, 0x2a, 0x1d, 0x8f, 0x30, 0xa2, 0xaa, 0x72, 0xb3, 0xd2, 0xdb, 0xcc, 0xcf, 0x9a, + 0xc4, 0x24, 0x62, 0x5b, 0xe3, 0xbf, 0x7c, 0xc9, 0xfc, 0x8c, 0xe1, 0x58, 0x2e, 0xd1, 0xc4, 0xbf, + 0x72, 0xa9, 0x60, 0x12, 0x62, 0xda, 0x58, 0x13, 0x5f, 0xad, 0xee, 0x9a, 0x86, 0xba, 0x9e, 0xc1, + 0x2c, 0xe2, 0x06, 0xfb, 0x6d, 0x81, 0xae, 0xb5, 0x0c, 0x8a, 0xb5, 0x8d, 0x6a, 0x0b, 0x33, 0xa3, + 0xaa, 0xb5, 0x89, 0x15, 0xec, 0x17, 0x77, 0x9f, 0x67, 0x96, 0x83, 0x29, 0x33, 0x9c, 0x8e, 0x14, + 0xb8, 0x10, 0x43, 0xbd, 0xf7, 0xd3, 0x17, 0x82, 0x5f, 0x2b, 0xe0, 0x64, 0x83, 0x9a, 0x0f, 0xc3, + 0xf5, 0x3a, 0xb6, 0xb1, 0x69, 0x30, 0xac, 0x5e, 0x01, 0x63, 0x14, 0xbb, 0x08, 0x7b, 0x39, 0xa5, + 0xa4, 0xcc, 0x1d, 0xab, 0xcd, 0xec, 0x6c, 0x17, 0x27, 0x9f, 0x19, 0x8e, 0xbd, 0x00, 0xfd, 0x75, + 0xa8, 0x4b, 0x01, 0xf5, 0x34, 0x18, 0xb7, 0x49, 0x7b, 0xbd, 0x69, 0xa1, 0x5c, 0xa6, 0xa4, 0xcc, + 0x8d, 0xea, 0x63, 0xfc, 0x73, 0x19, 0xa9, 0x67, 0xc0, 0xd1, 0x0d, 0xc3, 0x6e, 0x1a, 0x08, 0x79, + 0xb9, 0x2c, 0x47, 0xd1, 0xc7, 0x37, 0x0c, 0x7b, 0x11, 0x21, 0x6f, 0xa1, 0xf4, 0xfc, 0x8f, 0x97, + 0x57, 0x63, 0xbc, 0x5b, 0x46, 0x92, 0x00, 0x2c, 0x82, 0x73, 0xb1, 0xcc, 0x74, 0x4c, 0x3b, 0xc4, + 0xa5, 0x18, 0x7e, 0xae, 0x80, 0xd3, 0x7d, 0x12, 0x8f, 0x5c, 0x74, 0x88, 0xec, 0x17, 0x20, 0xa7, + 0x78, 0x2e, 0x86, 0x62, 0x37, 0xd4, 0x03, 0xcf, 0x83, 0x62, 0x02, 0x85, 0x90, 0xe6, 0x17, 0x7b, + 0x69, 0xb6, 0x88, 0x8b, 0x56, 0x48, 0x7b, 0xfd, 0x50, 0x68, 0x5e, 0xe0, 0x34, 0x0b, 0xb1, 0x34, + 0xb9, 0x9e, 0x32, 0x17, 0x8b, 0xe1, 0x19, 0x70, 0x08, 0x79, 0xfe, 0xa0, 0x80, 0x8b, 0x09, 0xb6, + 0x2c, 0xba, 0x87, 0x4c, 0x5a, 0xad, 0x81, 0x51, 0x1e, 0xcb, 0x22, 0x2a, 0x26, 0x6e, 0x9c, 0xa9, + 0xf8, 0xc1, 0x5e, 0xe1, 0xc1, 0x5e, 0x91, 0xc1, 0x5e, 0x59, 0x22, 0x96, 0x5b, 0xfb, 0xcf, 0xab, + 0xed, 0xe2, 0xc8, 0xce, 0x76, 0x71, 0xc2, 0x57, 0xc0, 0x0f, 0x41, 0x5d, 0x9c, 0x85, 0x1f, 0x82, + 0xeb, 0x07, 0xe1, 0x1b, 0x18, 0x18, 0x25, 0xa3, 0x44, 0xc9, 0xc0, 0x1d, 0x05, 0x9c, 0x6d, 0x50, + 0x93, 0x0b, 0x2f, 0xba, 0xe8, 0xfd, 0x72, 0xc1, 0x00, 0x47, 0x38, 0x39, 0x9a, 0xcb, 0x94, 0xb2, + 0xfb, 0x5b, 0x36, 0xcf, 0x2d, 0xfb, 0xfe, 0x4d, 0x71, 0xce, 0xb4, 0xd8, 0x93, 0x6e, 0xab, 0xd2, + 0x26, 0x8e, 0x26, 0x73, 0xde, 0xff, 0xaf, 0x4c, 0xd1, 0xba, 0xc6, 0x9e, 0x75, 0x30, 0x15, 0x07, + 0xa8, 0xee, 0x23, 0xef, 0x97, 0x55, 0x57, 0x78, 0x2c, 0x5c, 0x0c, 0x62, 0x81, 0x9b, 0x57, 0x36, + 0x5c, 0x54, 0x8e, 0x4b, 0xaf, 0x5b, 0xe2, 0xb6, 0x13, 0x6d, 0x0e, 0xbd, 0x36, 0x05, 0x32, 0xcb, + 0x75, 0xe9, 0xb0, 0xcc, 0x72, 0x1d, 0xbe, 0xcc, 0x00, 0xad, 0x41, 0xcd, 0x25, 0x0f, 0x1b, 0x0c, + 0xdf, 0xeb, 0xda, 0xb6, 0x6e, 0xb8, 0x26, 0x7e, 0x40, 0xa8, 0xc5, 0x8b, 0xd7, 0xbf, 0xdb, 0x7f, + 0xea, 0x35, 0x30, 0xde, 0x21, 0xc4, 0xe6, 0x21, 0x32, 0xca, 0x2d, 0xae, 0xa9, 0x3b, 0xdb, 0xc5, + 0x29, 0x9f, 0xa9, 0xdc, 0x80, 0xfa, 0x18, 0xff, 0xb5, 0x8c, 0x16, 0xfe, 0xc7, 0x9d, 0x0d, 0x03, + 0x67, 0xaf, 0x75, 0x6d, 0xbb, 0xec, 0x71, 0x5f, 0xf8, 0x2e, 0x5f, 0xeb, 0xb9, 0xfa, 0x29, 0xb8, + 0x9d, 0xd2, 0x63, 0xa1, 0xf7, 0x4f, 0x01, 0x3f, 0x48, 0xeb, 0x7d, 0x21, 0x5b, 0x57, 0x0b, 0x00, + 0x74, 0x24, 0xc0, 0x72, 0x5d, 0xe6, 0x56, 0x64, 0x85, 0xd7, 0xf5, 0x5c, 0x83, 0x9a, 0x8f, 0xdc, + 0x07, 0x84, 0xd8, 0x9f, 0x3c, 0xb1, 0x18, 0xb6, 0x2d, 0xca, 0x30, 0xe2, 0x9f, 0x69, 0xae, 0x23, + 0xe2, 0x90, 0xcc, 0x40, 0x87, 0x5c, 0xe4, 0x0e, 0x29, 0x06, 0x0e, 0xe9, 0xba, 0x7c, 0xb9, 0xbc, + 0xd9, 0x53, 0x5e, 0xe6, 0x0b, 0xf0, 0x3e, 0x28, 0x25, 0x31, 0x0b, 0xcd, 0xbe, 0x0c, 0x4e, 0xe0, + 0x2d, 0x8b, 0x61, 0xd4, 0x94, 0x19, 0x4b, 0x73, 0x4a, 0x29, 0x3b, 0x37, 0xaa, 0x4f, 0xfa, 0xcb, + 0x2b, 0x22, 0x71, 0x29, 0xfc, 0x2e, 0x0b, 0xee, 0x08, 0x30, 0xdb, 0x8f, 0xe3, 0x86, 0x65, 0x7a, + 0x06, 0xc3, 0x0f, 0x9f, 0x18, 0x1e, 0xa6, 0xab, 0x24, 0x74, 0xf6, 0x12, 0x71, 0xdb, 0xd8, 0x65, + 0x7c, 0x0f, 0x05, 0x8e, 0x4f, 0xe9, 0x86, 0x68, 0x1d, 0xcb, 0x46, 0xdd, 0x20, 0x37, 0x60, 0x58, + 0xdb, 0x4c, 0x30, 0x43, 0x05, 0x81, 0x26, 0x23, 0x4d, 0xc7, 0x67, 0x34, 0xb8, 0xd0, 0x95, 0x64, + 0xa1, 0xcb, 0x49, 0x06, 0xbb, 0x11, 0xa0, 0x7e, 0x82, 0x4a, 0xb3, 0xa4, 0x95, 0xea, 0x73, 0x05, + 0x4c, 0x31, 0xb2, 0x8e, 0xdd, 0x26, 0xe9, 0xb2, 0xa6, 0xc3, 0xb3, 0x66, 0x74, 0x50, 0xd6, 0x2c, + 0x4b, 0x35, 0x27, 0x7d, 0x35, 0xfd, 0xc7, 0x61, 0xaa, 0x74, 0x3a, 0x2e, 0x0e, 0x7f, 0xdc, 0x65, + 0x0d, 0xcb, 0xa5, 0x0b, 0x45, 0x7e, 0xf9, 0xf9, 0xde, 0xe5, 0x87, 0xc5, 0x27, 0xe0, 0xff, 0x73, + 0x16, 0xdc, 0x1d, 0xf6, 0xae, 0xc2, 0xc0, 0x78, 0x0c, 0xc6, 0x0d, 0x87, 0x74, 0x5d, 0x36, 0x2f, + 0x2f, 0xed, 0x2e, 0xb7, 0xe7, 0xd7, 0xed, 0xe2, 0xe5, 0x03, 0xd0, 0x5e, 0x76, 0x59, 0xef, 0xda, + 0x24, 0x0c, 0xd4, 0x03, 0xc0, 0x1e, 0x76, 0x55, 0x5c, 0xf2, 0x7b, 0x63, 0x57, 0x43, 0xec, 0xaa, + 0xba, 0x09, 0x66, 0x6c, 0xeb, 0x69, 0xd7, 0x42, 0x16, 0x7b, 0xd6, 0x6c, 0x8b, 0x4a, 0x80, 0xfc, + 0xe2, 0x53, 0xbb, 0x9f, 0x42, 0x4b, 0x1d, 0xb7, 0x7b, 0x21, 0xb2, 0x07, 0x10, 0xea, 0xd3, 0xe1, + 0x9a, 0x5f, 0x6d, 0x90, 0xfa, 0x08, 0x1c, 0xfb, 0x94, 0x58, 0x6e, 0x93, 0x4f, 0x87, 0xa2, 0xa6, + 0x4d, 0xdc, 0xc8, 0x57, 0xfc, 0xd1, 0xb1, 0x12, 0x8c, 0x8e, 0x95, 0xd5, 0x60, 0x74, 0xac, 0x9d, + 0x95, 0xe1, 0x31, 0xed, 0xab, 0x08, 0x8f, 0xc2, 0x17, 0x6f, 0x8a, 0x8a, 0x7e, 0x94, 0x7f, 0x73, + 0x61, 0xf8, 0x65, 0x56, 0x74, 0x81, 0x45, 0x84, 0x56, 0x49, 0xf4, 0xc2, 0x56, 0x02, 0xfd, 0xbd, + 0x9a, 0x16, 0xe6, 0xdb, 0x6d, 0x30, 0x11, 0x54, 0xa8, 0xb0, 0x07, 0xd7, 0x4e, 0xed, 0x6c, 0x17, + 0xd5, 0xa0, 0x9e, 0x84, 0x9b, 0x30, 0x52, 0xcc, 0x50, 0x24, 0x51, 0x33, 0x83, 0x12, 0xb5, 0x19, + 0x64, 0x04, 0xc2, 0xd4, 0xf2, 0x30, 0x9a, 0x1f, 0x9c, 0x78, 0xe7, 0xe2, 0x32, 0x22, 0x38, 0x0e, + 0xf5, 0x49, 0xb1, 0x50, 0x97, 0xdf, 0x7b, 0x14, 0x54, 0xa5, 0x53, 0x87, 0x54, 0x50, 0xdd, 0xa5, + 0xa0, 0xba, 0x70, 0x95, 0xe7, 0xd1, 0xa5, 0x20, 0x8f, 0x0c, 0x84, 0xca, 0x8c, 0x94, 0xdb, 0x76, + 0xb4, 0x87, 0x07, 0xae, 0x81, 0x3f, 0x65, 0x45, 0x67, 0x49, 0x73, 0x0b, 0x61, 0x26, 0x0d, 0x7d, + 0x1b, 0x91, 0x14, 0xcc, 0xfc, 0x83, 0x29, 0x98, 0x3d, 0xec, 0x14, 0x5c, 0x07, 0x93, 0x2e, 0xde, + 0x6c, 0x86, 0x19, 0x92, 0x3b, 0x22, 0x34, 0xdc, 0x4b, 0x9d, 0x7e, 0xb3, 0xbe, 0x86, 0x3e, 0x30, + 0xa8, 0x1f, 0x77, 0xf1, 0x66, 0xe8, 0xf7, 0x68, 0xc3, 0xd8, 0x33, 0x48, 0xec, 0x6e, 0x18, 0xf0, + 0xdb, 0x8c, 0x6c, 0xd6, 0x7c, 0x64, 0x5d, 0x22, 0xee, 0x06, 0xf6, 0x18, 0x1f, 0x0b, 0x98, 0xb1, + 0x8e, 0xa3, 0x48, 0xca, 0x20, 0xa4, 0x34, 0x99, 0xb2, 0xcf, 0x14, 0xe4, 0x81, 0x69, 0xc7, 0x72, + 0x9b, 0x86, 0xc3, 0x78, 0xff, 0xa1, 0x9c, 0x86, 0xb0, 0xe2, 0x98, 0xdf, 0x3d, 0x52, 0x5d, 0xc7, + 0x69, 0x5f, 0xfb, 0x6e, 0x3c, 0xa8, 0x4f, 0x3a, 0x96, 0xbb, 0xe8, 0xb0, 0x55, 0x22, 0xcc, 0x5c, + 0xb8, 0xc4, 0xc3, 0xbe, 0xd4, 0x6b, 0x1f, 0xe2, 0xe9, 0xd2, 0xf6, 0x7d, 0xe1, 0x0f, 0x54, 0xe2, + 0xd8, 0x5f, 0x8a, 0x9c, 0x1e, 0x62, 0x5c, 0x15, 0x86, 0x36, 0x05, 0xd3, 0x8c, 0x30, 0x6e, 0x9c, + 0xc3, 0x7c, 0x75, 0x48, 0x76, 0x8b, 0xa1, 0xf9, 0xef, 0xc6, 0x83, 0xfa, 0x94, 0x58, 0x5a, 0x74, + 0x98, 0xd0, 0x8d, 0x54, 0x04, 0x4e, 0xf9, 0x42, 0xb2, 0x73, 0x07, 0xd3, 0x1f, 0x92, 0x57, 0x51, + 0x49, 0x17, 0x67, 0xfa, 0xac, 0x40, 0xf3, 0xbb, 0x63, 0x30, 0x16, 0xa2, 0x1b, 0x7f, 0x4e, 0x80, + 0x6c, 0x83, 0x9a, 0xaa, 0x07, 0xd4, 0xb8, 0x39, 0xbb, 0xb2, 0xf7, 0x2f, 0x12, 0x95, 0xd8, 0x47, + 0x74, 0xbe, 0x7a, 0x60, 0xd1, 0xd0, 0xad, 0x5b, 0x60, 0x36, 0xf6, 0xad, 0x7d, 0x6d, 0x20, 0x54, + 0x4f, 0x38, 0x7f, 0x33, 0x85, 0x70, 0x92, 0xe6, 0xf0, 0x25, 0x7a, 0x10, 0xcd, 0x81, 0xf0, 0x81, + 0x34, 0xef, 0x79, 0x33, 0x7e, 0xa3, 0x80, 0xf3, 0x83, 0x5f, 0xc4, 0x77, 0x52, 0x18, 0xd5, 0x77, + 0x32, 0x7f, 0x77, 0xd8, 0x93, 0x21, 0xc3, 0xaf, 0x14, 0x70, 0x26, 0xf9, 0xe5, 0x3a, 0x9f, 0x80, + 0x9f, 0x78, 0x22, 0x7f, 0x27, 0xed, 0x89, 0x90, 0xc9, 0x8f, 0x0a, 0xb8, 0x9e, 0xea, 0x59, 0xb8, + 0x94, 0xa0, 0x2a, 0x0d, 0x48, 0xfe, 0xa3, 0x43, 0x00, 0x09, 0x4d, 0xf8, 0x0c, 0x9c, 0x8c, 0x7f, + 0x32, 0x5d, 0x4f, 0xd0, 0x12, 0x2b, 0x9d, 0xff, 0x7f, 0x1a, 0xe9, 0x50, 0xf9, 0x6f, 0x0a, 0xf8, + 0x60, 0xb8, 0x97, 0xcc, 0x4a, 0xa2, 0xbe, 0x21, 0xd0, 0xf2, 0xab, 0x87, 0x89, 0xd6, 0x17, 0x1d, + 0xa9, 0xc6, 0xc5, 0xa4, 0xe8, 0x48, 0x03, 0x92, 0x18, 0x1d, 0x43, 0x8d, 0x4c, 0x22, 0x3a, 0xe2, + 0x7a, 0x74, 0x72, 0x74, 0xc4, 0x48, 0xef, 0x13, 0x1d, 0xfb, 0x34, 0xb5, 0xda, 0x83, 0x57, 0x6f, + 0x0b, 0xca, 0xeb, 0xb7, 0x05, 0xe5, 0xf7, 0xb7, 0x05, 0xe5, 0xc5, 0xbb, 0xc2, 0xc8, 0xeb, 0x77, + 0x85, 0x91, 0x5f, 0xde, 0x15, 0x46, 0x1e, 0xdf, 0x8a, 0x74, 0x14, 0x89, 0x5c, 0xb6, 0x8d, 0x16, + 0x0d, 0x3e, 0xb4, 0x8d, 0xea, 0x6d, 0x6d, 0xab, 0xef, 0x2f, 0xd8, 0xbc, 0xcb, 0xb4, 0xc6, 0xc4, + 0xfc, 0x7f, 0xf3, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x81, 0xc5, 0xc4, 0x97, 0xe4, 0x16, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2439,16 +2427,6 @@ func (m *MsgUnbondConvertAndStake) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l - { - size, err := m.SharesToConvertAndStake.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x2a { size := m.MinAmtToStake.Size() i -= size @@ -2853,8 +2831,6 @@ func (m *MsgUnbondConvertAndStake) Size() (n int) { } l = m.MinAmtToStake.Size() n += 1 + l + sovTx(uint64(l)) - l = m.SharesToConvertAndStake.Size() - n += 1 + l + sovTx(uint64(l)) return n } @@ -5120,39 +5096,6 @@ func (m *MsgUnbondConvertAndStake) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SharesToConvertAndStake", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.SharesToConvertAndStake.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From 0898bdc25c91d561e5f85dac87b4e3dca69431f4 Mon Sep 17 00:00:00 2001 From: mattverse Date: Tue, 8 Aug 2023 20:32:52 +0900 Subject: [PATCH 09/27] Use val set delegation and edit test cases --- proto/osmosis/superfluid/tx.proto | 4 - x/superfluid/keeper/export_test.go | 12 +- x/superfluid/keeper/migrate.go | 28 +-- x/superfluid/keeper/migrate_test.go | 24 ++- x/superfluid/keeper/msg_server.go | 4 +- x/superfluid/keeper/stake.go | 109 ++++++----- x/superfluid/keeper/stake_test.go | 44 +++-- x/superfluid/types/errors.go | 6 +- x/superfluid/types/expected_keepers.go | 3 +- x/superfluid/types/tx.pb.go | 239 ++++++++++--------------- x/valset-pref/keeper.go | 3 +- x/valset-pref/types/errors.go | 7 + x/valset-pref/validator_set.go | 2 +- 13 files changed, 238 insertions(+), 247 deletions(-) create mode 100644 x/valset-pref/types/errors.go diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index 25fe4f501e2..03b4904d6b6 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -264,8 +264,4 @@ message MsgUnbondConvertAndStakeResponse { (gogoproto.moretags) = "yaml:\"total_amt_staked\"", (gogoproto.nullable) = false ]; - string total_shares_delegated = 2 [ - (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", - (gogoproto.nullable) = false - ]; } \ No newline at end of file diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index b05e4baf389..77136372da2 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -33,8 +33,8 @@ func (k Keeper) MigrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, return k.migrateNonSuperfluidLockBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, tokenOutMins) } -func (k Keeper) ForceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { - return k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins) +func (k Keeper) ForceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins, exitCoinsLengthIsTwo bool) (exitCoins sdk.Coins, err error) { + return k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins, exitCoinsLengthIsTwo) } func (k Keeper) RouteMigration(ctx sdk.Context, sender sdk.AccAddress, lockId int64) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { @@ -62,14 +62,14 @@ func (k Keeper) DistributeSuperfluidGauges(ctx sdk.Context) { } func (k Keeper) ConvertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, lockId uint64, - minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { return k.convertLockToStake(ctx, sender, valAddr, lockId, minAmtToStake) } func (k Keeper) ConvertGammSharesToOsmoAndStake( ctx sdk.Context, sender sdk.AccAddress, valAddr string, - poolIdLeaving uint64, exitCoins sdk.Coins, minAmtToStake sdk.Int, -) (totalAmtCoverted sdk.Int, shares sdk.Dec, err error) { - return k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake) + poolIdLeaving uint64, exitCoins sdk.Coins, minAmtToStake sdk.Int, originalSuperfluidValAddr string, +) (totalAmtCoverted sdk.Int, err error) { + return k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake, originalSuperfluidValAddr) } diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index a6d5016ae99..1b1c6d83ca7 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -105,16 +105,11 @@ func (k Keeper) migrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins, true) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } - // Defense in depth, ensuring we are returning exactly two coins. - if len(exitCoins) != 2 { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, types.TwoTokenBalancerPoolError{NumberOfTokens: len(exitCoins)} - } - // Create a full range (min to max tick) concentrated liquidity position, lock it, and superfluid delegate it. positionId, amount0, amount1, liquidity, concentratedLockId, err = k.clk.CreateFullRangePositionLocked(ctx, poolIdEntering, sender, exitCoins, remainingLockTime) if err != nil { @@ -156,16 +151,11 @@ func (k Keeper) migrateSuperfluidUnbondingBalancerToConcentrated(ctx sdk.Context // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins, true) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } - // Defense in depth, ensuring we are returning exactly two coins. - if len(exitCoins) != 2 { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, types.TwoTokenBalancerPoolError{NumberOfTokens: len(exitCoins)} - } - // Create a full range (min to max tick) concentrated liquidity position. positionId, amount0, amount1, liquidity, concentratedLockId, err = k.clk.CreateFullRangePositionUnlocking(ctx, poolIdEntering, sender, exitCoins, remainingLockTime) if err != nil { @@ -209,16 +199,11 @@ func (k Keeper) migrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, // Force unlock, validate the provided sharesToMigrate, and exit the balancer pool. // This will return the coins that will be used to create the concentrated liquidity position. // It also returns the lock object that contains the remaining shares that were not used in this migration. - exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins, true) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } - // Defense in depth, ensuring we are returning exactly two coins. - if len(exitCoins) != 2 { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, types.TwoTokenBalancerPoolError{NumberOfTokens: len(exitCoins)} - } - // Create a new lock that is unlocking for the remaining time of the old lock. // Regardless of the previous lock's status, we create a new lock that is unlocking. // This is because locking without superfluid is pointless in the context of concentrated liquidity. @@ -314,7 +299,7 @@ func (k Keeper) validateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId // 4. Exits the position in the Balancer pool. // 5. Ensures that exactly two coins are returned. // 6. Any remaining shares that were not migrated are re-locked as a new lock for the remaining time on the lock. -func (k Keeper) forceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (exitCoins sdk.Coins, err error) { +func (k Keeper) forceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins, exitCoinsLengthIsTwo bool) (exitCoins sdk.Coins, err error) { // validateMigration ensures that the preMigrationLock contains coins of length 1. gammSharesInLock := lock.Coins[0] @@ -345,5 +330,10 @@ func (k Keeper) forceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAd return sdk.Coins{}, err } + // if exit coins length should be two, check exitCoins length + if exitCoinsLengthIsTwo && len(exitCoins) != 2 { + return sdk.Coins{}, types.TwoTokenBalancerPoolError{NumberOfTokens: len(exitCoins)} + } + return exitCoins, nil } diff --git a/x/superfluid/keeper/migrate_test.go b/x/superfluid/keeper/migrate_test.go index bf5f3eb325a..bd9f3fdc4f2 100644 --- a/x/superfluid/keeper/migrate_test.go +++ b/x/superfluid/keeper/migrate_test.go @@ -797,13 +797,14 @@ func (s *KeeperTestSuite) TestValidateMigration() { } } -func (s *KeeperTestSuite) TestforceUnlockAndExitBalancerPool() { +func (s *KeeperTestSuite) TestForceUnlockAndExitBalancerPool() { defaultJoinTime := s.Ctx.BlockTime() type sendTest struct { overwritePreMigrationLock bool overwriteShares bool overwritePool bool overwritePoolId bool + exitCoinsLengthIsTwo bool percentOfSharesToMigrate sdk.Dec tokenOutMins sdk.Coins expectedError error @@ -816,6 +817,11 @@ func (s *KeeperTestSuite) TestforceUnlockAndExitBalancerPool() { percentOfSharesToMigrate: sdk.MustNewDecFromStr("0.4"), expectedError: types.MigratePartialSharesError{SharesToMigrate: "20000000000000000000", SharesInLock: "50000000000000000000"}, }, + "attempt to leave a pool that has more than two denoms": { + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + overwritePool: true, + exitCoinsLengthIsTwo: false, + }, "error: lock does not exist": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), overwritePreMigrationLock: true, @@ -831,6 +837,12 @@ func (s *KeeperTestSuite) TestforceUnlockAndExitBalancerPool() { overwritePoolId: true, expectedError: fmt.Errorf("pool with ID %d does not exist", 2), }, + "error: attempt to leave a pool that has more than two denoms with exitCoinsLengthIsTwo true": { + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + overwritePool: true, + exitCoinsLengthIsTwo: true, + expectedError: types.TwoTokenBalancerPoolError{NumberOfTokens: 4}, + }, "error: happy path (full shares), token out mins is more than exit coins": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), tokenOutMins: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(100000))), @@ -912,7 +924,7 @@ func (s *KeeperTestSuite) TestforceUnlockAndExitBalancerPool() { } // System under test - exitCoins, err := superfluidKeeper.ForceUnlockAndExitBalancerPool(ctx, poolJoinAcc, balancerPooId, lock, coinsToMigrate, tc.tokenOutMins) + exitCoins, err := superfluidKeeper.ForceUnlockAndExitBalancerPool(ctx, poolJoinAcc, balancerPooId, lock, coinsToMigrate, tc.tokenOutMins, tc.exitCoinsLengthIsTwo) if tc.expectedError != nil { s.Require().Error(err) s.Require().ErrorContains(err, tc.expectedError.Error()) @@ -937,9 +949,11 @@ func (s *KeeperTestSuite) TestforceUnlockAndExitBalancerPool() { s.Require().Equal(expectedSharesStillInOldLock.String(), lock.Coins[0].Amount.String()) } - for _, coin := range exitCoins { - // Check that the exit coin is the same amount that we joined with (with one unit rounding down) - s.Require().Equal(0, defaultErrorTolerance.Compare(tokensIn.AmountOf(coin.Denom).ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt(), coin.Amount)) + if tc.exitCoinsLengthIsTwo { + for _, coin := range exitCoins { + // Check that the exit coin is the same amount that we joined with (with one unit rounding down) + s.Require().Equal(0, defaultErrorTolerance.Compare(tokensIn.AmountOf(coin.Denom).ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt(), coin.Amount)) + } } }) } diff --git a/x/superfluid/keeper/msg_server.go b/x/superfluid/keeper/msg_server.go index 7155949a81a..5b635824818 100644 --- a/x/superfluid/keeper/msg_server.go +++ b/x/superfluid/keeper/msg_server.go @@ -246,10 +246,10 @@ func (server msgServer) AddToConcentratedLiquiditySuperfluidPosition(goCtx conte func (server msgServer) UnbondConvertAndStake(goCtx context.Context, msg *types.MsgUnbondConvertAndStake) (*types.MsgUnbondConvertAndStakeResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - totalAmtConverted, totalSharesDelegated, err := server.keeper.UnbondConvertAndStake(ctx, msg.LockId, msg.Sender, msg.ValAddr, msg.MinAmtToStake) + totalAmtConverted, err := server.keeper.UnbondConvertAndStake(ctx, msg.LockId, msg.Sender, msg.ValAddr, msg.MinAmtToStake) if err != nil { return nil, err } - return &types.MsgUnbondConvertAndStakeResponse{TotalAmtStaked: totalAmtConverted, TotalSharesDelegated: totalSharesDelegated}, nil + return &types.MsgUnbondConvertAndStakeResponse{TotalAmtStaked: totalAmtConverted}, nil } diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index c88df045464..b3170629345 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -9,6 +9,7 @@ import ( gammtypes "github.com/osmosis-labs/osmosis/v17/x/gamm/types" lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" + valsettypes "github.com/osmosis-labs/osmosis/v17/x/valset-pref/types" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -660,80 +661,96 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn } // UnbondConvertAndStake converts given lock to osmo and stakes it to given validator. -// Supports conversion of 1)superfluid bonded 2)superfluid undelegating 3)vanilla unlocking 4) unlocked locks. -// If valAddr is empty, we attempt to get staking preference from valset-pref module and stake to the given validator. +// Supports conversion of 1)superfluid bonded 2)superfluid undelegating 3)vanilla unlocking. +// Liquid gamm shares will not be supported for conversion. +// Delegation is done in the following logic: +// - If valAddr provided, single delegate. +// - If valAddr not provided and valset exists, valsetpref.Delegate +// - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator +// - Else: error func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, - minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, totalSharesDelegated sdk.Dec, err error) { + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { senderAddr, err := sdk.AccAddressFromBech32(sender) if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + return sdk.ZeroInt(), err } // use routeMigration method to check status of lock(either superfluid staked, superfluid unbonding, vanialla locked, unlocked) _, migrationType, err := k.routeMigration(ctx, senderAddr, int64(lockID)) if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + return sdk.ZeroInt(), err } // if superfluid bonded, first change it into superfluid undelegate to burn minted osmo and instantly undelegate. if migrationType == SuperfluidBonded { _, err = k.undelegateCommon(ctx, sender, lockID) if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + return sdk.ZeroInt(), err } } if migrationType == SuperfluidBonded || migrationType == SuperfluidUnbonding || migrationType == NonSuperfluid { - totalAmtConverted, totalSharesDelegated, err = k.convertLockToStake(ctx, senderAddr, valAddr, lockID, minAmtToStake) + totalAmtConverted, err = k.convertLockToStake(ctx, senderAddr, valAddr, lockID, minAmtToStake) if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + return sdk.ZeroInt(), err } } else { // liquid gamm shares without locks are not supported - return sdk.ZeroInt(), sdk.ZeroDec(), fmt.Errorf("unsupported staking conversion type") + return sdk.ZeroInt(), fmt.Errorf("unsupported staking conversion type") } - return totalAmtConverted, totalSharesDelegated, nil + return totalAmtConverted, nil } // convertLockToStake handles locks that are superfluid bonded, superfluid unbonding, vanilla locked(unlocking) locks. // Deletes all associated state, converts the lock itself to staking delegation by going through exit pool and swap. func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, lockId uint64, - minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, shares sdk.Dec, err error) { + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { lock, err := k.lk.GetLockByID(ctx, lockId) if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + return sdk.ZeroInt(), err } lockCoin := lock.Coins[0] poolIdLeaving, err := gammtypes.GetPoolIdFromShareDenom(lockCoin.Denom) if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + return sdk.ZeroInt(), err + } + + var superfluidValAddr string + interAcc, found := k.GetIntermediaryAccountFromLockId(ctx, lockId) + if found { + superfluidValAddr = interAcc.ValAddr } // Force unlock, validate the provided sharesToStake, and exit the balancer pool. // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. - exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, lockCoin, sdk.NewCoins()) + exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, lockCoin, sdk.NewCoins(), false) if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + return sdk.ZeroInt(), err } - totalAmtConverted, shares, err = k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake) + totalAmtConverted, err = k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake, superfluidValAddr) if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + return sdk.ZeroInt(), err } - return totalAmtConverted, shares, nil + return totalAmtConverted, nil } // convertGammSharesToOsmoAndStake converts given gamm shares to osmo by swapping in the given pool // then stakes it to the designated validator. // minAmtToStake works as slippage bound, and would error if total amount being staked is less than min amount to stake. // If valAddr is empty, we attempt to get staking preference from valset-pref module and stake to the given validator. +// Delegation is done in the following logic: +// - If valAddr provided, single delegate. +// - If valAddr not provided and valset exists, valsetpref.Delegate +// - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator +// - Else: error func (k Keeper) convertGammSharesToOsmoAndStake( ctx sdk.Context, sender sdk.AccAddress, valAddr string, - poolIdLeaving uint64, exitCoins sdk.Coins, minAmtToStake sdk.Int, -) (totalAmtCoverted sdk.Int, shares sdk.Dec, err error) { + poolIdLeaving uint64, exitCoins sdk.Coins, minAmtToStake sdk.Int, originalSuperfluidValAddr string, +) (totalAmtCoverted sdk.Int, err error) { var nonOsmoCoins sdk.Coins bondDenom := k.sk.BondDenom(ctx) @@ -753,7 +770,7 @@ func (k Keeper) convertGammSharesToOsmoAndStake( for _, coinToConvert := range nonOsmoCoins { tokenOutAmt, err := k.pmk.SwapExactAmountIn(ctx, sender, poolIdLeaving, coinToConvert, bondDenom, sdk.ZeroInt()) if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + return sdk.ZeroInt(), err } totalAmtCoverted = totalAmtCoverted.Add(tokenOutAmt) @@ -764,44 +781,46 @@ func (k Keeper) convertGammSharesToOsmoAndStake( // check if the total amount to stake after all conversion is greater than provided min amount to stake if totalAmtToStake.LT(minAmtToStake) { - return sdk.ZeroInt(), sdk.ZeroDec(), types.TokenConvertedLessThenDesiredStakeError{ + return sdk.ZeroInt(), types.TokenConvertedLessThenDesiredStakeError{ ActualTotalAmtToStake: totalAmtToStake, ExpectedTotalAmtToStake: minAmtToStake, } } var val stakingtypes.Validator - // if given valAddr is empty, we use delegation preference given from valset-pref module. + // if given valAddr is empty, we use delegation preference given from valset-pref module or reference from superfluid staking if valAddr == "" { - delegationPref, err := k.vspk.GetDelegationPreferences(ctx, sender.String()) - if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err - } - if len(delegationPref.Preferences) != 1 { - return sdk.ZeroInt(), sdk.ZeroDec(), types.MultipleValFromValsetError{} + err := k.vspk.DelegateToValidatorSet(ctx, sender.String(), sdk.NewCoin(bondDenom, totalAmtToStake)) + + // if valset-pref delegation errored due to no existing delegation existing, fall back and try using superfluid staked validator + if err == valsettypes.ErrNoDelegation { + val, err = k.validateValAddrForDelegate(ctx, originalSuperfluidValAddr) + if err != nil { + return sdk.ZeroInt(), err + } + + // delegate now! + _, err = k.sk.Delegate(ctx, sender, totalAmtToStake, stakingtypes.Unbonded, val, true) + if err != nil { + return sdk.ZeroInt(), err + } } - valOperAddr := delegationPref.Preferences[0].ValOperAddress - valAddr, err := sdk.ValAddressFromBech32(valOperAddr) + + // for other errors, handle error if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), fmt.Errorf("validator address not formatted") - } - validator, found := k.sk.GetValidator(ctx, valAddr) - if !found { - return sdk.ZeroInt(), sdk.ZeroDec(), fmt.Errorf("validator not found %s", validator) + return sdk.ZeroInt(), err } - val = validator } else { val, err = k.validateValAddrForDelegate(ctx, valAddr) if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err + return sdk.ZeroInt(), err + } + // delegate now! + _, err = k.sk.Delegate(ctx, sender, totalAmtToStake, stakingtypes.Unbonded, val, true) + if err != nil { + return sdk.ZeroInt(), err } } - // delegate now! - shares, err = k.sk.Delegate(ctx, sender, totalAmtToStake, stakingtypes.Unbonded, val, true) - if err != nil { - return sdk.ZeroInt(), sdk.ZeroDec(), err - } - - return totalAmtToStake, shares, nil + return totalAmtToStake, nil } diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 20a599583b2..010e2a49eea 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1071,7 +1071,7 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) // system under test - totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lock.ID, sender.String(), valAddr.String(), minAmountToStake) + totalAmtConverted, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lock.ID, sender.String(), valAddr.String(), minAmountToStake) if tc.expectedError { s.Require().Error(err) return @@ -1085,7 +1085,6 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { // check if delegation amount matches delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) s.Require().True(found) - s.Require().Equal(delegation.Shares, sharesDelegated) s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) // Superfluid check @@ -1163,7 +1162,7 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) // system under test - totalAmtConverted, sharesDelegated, err := s.App.SuperfluidKeeper.ConvertLockToStake(s.Ctx, sender, valAddr.String(), lock.ID, minAmountToStake) + totalAmtConverted, err := s.App.SuperfluidKeeper.ConvertLockToStake(s.Ctx, sender, valAddr.String(), lock.ID, minAmountToStake) if tc.expectedError { s.Require().Error(err) return @@ -1177,7 +1176,6 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { // check if delegation amount matches delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) s.Require().True(found) - s.Require().Equal(delegation.Shares, sharesDelegated) s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) // Superfluid check @@ -1202,6 +1200,7 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { useMinAmtToStake bool useValSetPrefSingleVal bool useValSetPrefMultipleVal bool + useSuperfluid bool expectedError bool } @@ -1210,9 +1209,13 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { "use val set preference (single validator)": { useValSetPrefSingleVal: true, }, - "error: multiple validator returned from valset pref": { + "multiple validator returned from valset pref": { useValSetPrefMultipleVal: true, - expectedError: true, + expectedError: false, + }, + "No validator returned from valset, fall back to superfluid delegation": { + useSuperfluid: true, + expectedError: false, }, "error: invalid val address": { useInvalidValAddr: true, @@ -1237,6 +1240,7 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { s.Require().NoError(err) // test params + originalSuperfluidValAddr := "" valAddr := s.SetupValidator(stakingtypes.Bonded) valAddrString := valAddr.String() if tc.useInvalidValAddr { @@ -1254,6 +1258,10 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { _, err = s.App.StakingKeeper.Delegate(s.Ctx, sender, stakeCoin.Amount, stakingtypes.Unbonded, validator, true) s.Require().NoError(err) } + if tc.useSuperfluid { + originalSuperfluidValAddr = valAddrString + valAddrString = "" + } // if test case is setting multiple validator, stake one more time to a different validator if tc.useValSetPrefMultipleVal { @@ -1288,7 +1296,7 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { poolBeforeNonBondDenomAmt := poolLiquidityBeforeSwap.AmountOf("foo") // system under test. - totalAmtConverted, shares, err := s.App.SuperfluidKeeper.ConvertGammSharesToOsmoAndStake(s.Ctx, sender, valAddrString, poolId, exitCoins, minAmtToStake) + totalAmtConverted, err := s.App.SuperfluidKeeper.ConvertGammSharesToOsmoAndStake(s.Ctx, sender, valAddrString, poolId, exitCoins, minAmtToStake, originalSuperfluidValAddr) if tc.expectedError { s.Require().Error(err) return @@ -1297,16 +1305,24 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { // check that total Amount converted is equal to (swap result + original stake denom amount) s.Require().True(expectedTotalAmtStaked.Equal(totalAmtConverted)) - s.Require().True(expectedTotalAmtStaked.Equal(shares.RoundInt())) // check staking - delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) - s.Require().True(found) - // if we have used val set pref, we also need to count extra amount we have delegated - if tc.useValSetPrefSingleVal { - s.Require().True(delegation.Shares.Sub(stakeCoin.Amount.ToDec()).Equal(shares)) + if tc.useValSetPrefMultipleVal { + delegations := s.App.StakingKeeper.GetAllDelegatorDelegations(s.Ctx, sender) + // we used two validators + s.Require().True(len(delegations) == 2) + + delegation0Shares := delegations[0].Shares + delegation1Shares := delegations[1].Shares + + shareDiff := delegation0Shares.Sub(delegation1Shares).Abs() + + // in practice, the share amount between two validators should be equal, + // but due to how we handle truncation and rounding in valset pref, we expect the diff to be under one dec. + s.Require().True(shareDiff.LTE(sdk.OneDec())) } else { - s.Require().True(delegation.Shares.Equal(shares)) + _, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) + s.Require().True(found) } // check pool diff --git a/x/superfluid/types/errors.go b/x/superfluid/types/errors.go index 3038e54345a..bc8ded5d8f4 100644 --- a/x/superfluid/types/errors.go +++ b/x/superfluid/types/errors.go @@ -127,9 +127,9 @@ func (e TokenConvertedLessThenDesiredStakeError) Error() string { return fmt.Sprintf("actual amount converted to stake (%s) is less then minimum amount expected to be staked (%s)", e.ActualTotalAmtToStake, e.ExpectedTotalAmtToStake) } -type MultipleValFromValsetError struct { +type NoValsetNoSuperfluidDelegationError struct { } -func (e MultipleValFromValsetError) Error() string { - return fmt.Sprintf("Multiple validator from valset-preference exists") +func (e NoValsetNoSuperfluidDelegationError) Error() string { + return fmt.Sprintf("No val-set for delegation nor existing superfluid delegation") } diff --git a/x/superfluid/types/expected_keepers.go b/x/superfluid/types/expected_keepers.go index cb9ad0db2a8..bb8ed5c59a0 100644 --- a/x/superfluid/types/expected_keepers.go +++ b/x/superfluid/types/expected_keepers.go @@ -13,7 +13,6 @@ import ( gammmigration "github.com/osmosis-labs/osmosis/v17/x/gamm/types/migration" incentivestypes "github.com/osmosis-labs/osmosis/v17/x/incentives/types" lockuptypes "github.com/osmosis-labs/osmosis/v17/x/lockup/types" - valsetpreftypes "github.com/osmosis-labs/osmosis/v17/x/valset-pref/types" epochstypes "github.com/osmosis-labs/osmosis/x/epochs/types" ) @@ -135,5 +134,5 @@ type PoolManagerKeeper interface { } type ValSetPreferenceKeeper interface { - GetDelegationPreferences(ctx sdk.Context, delegator string) (valsetpreftypes.ValidatorSetPreferences, error) + DelegateToValidatorSet(ctx sdk.Context, delegatorAddr string, coin sdk.Coin) error } diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index c9727e0bfa2..6b9a99cb2f1 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -1090,8 +1090,7 @@ func (m *MsgUnbondConvertAndStake) GetValAddr() string { } type MsgUnbondConvertAndStakeResponse struct { - TotalAmtStaked github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=total_amt_staked,json=totalAmtStaked,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_amt_staked" yaml:"total_amt_staked"` - TotalSharesDelegated github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=total_shares_delegated,json=totalSharesDelegated,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"total_shares_delegated"` + TotalAmtStaked github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=total_amt_staked,json=totalAmtStaked,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_amt_staked" yaml:"total_amt_staked"` } func (m *MsgUnbondConvertAndStakeResponse) Reset() { *m = MsgUnbondConvertAndStakeResponse{} } @@ -1153,102 +1152,100 @@ func init() { func init() { proto.RegisterFile("osmosis/superfluid/tx.proto", fileDescriptor_55b645f187d22814) } var fileDescriptor_55b645f187d22814 = []byte{ - // 1505 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcd, 0x6f, 0x13, 0x47, - 0x1b, 0xcf, 0xda, 0x21, 0x81, 0x09, 0x09, 0xc9, 0xbe, 0x01, 0x8c, 0x5f, 0xb0, 0xcd, 0xf0, 0xf1, - 0x86, 0x0f, 0x7b, 0x63, 0x78, 0x0b, 0x28, 0x27, 0xe2, 0x58, 0x54, 0xa1, 0xb1, 0x8a, 0x96, 0xa0, - 0x4a, 0x5c, 0xac, 0xb5, 0x67, 0xb2, 0x6c, 0xb3, 0xbb, 0x63, 0x76, 0xc6, 0x49, 0x50, 0x4f, 0x6d, - 0x0f, 0x95, 0x38, 0x71, 0xec, 0xad, 0xd7, 0xb6, 0x87, 0x8a, 0x3f, 0xa1, 0x87, 0x1e, 0x50, 0x4f, - 0x1c, 0xab, 0x56, 0x0a, 0x15, 0x1c, 0x7a, 0xcf, 0xa5, 0xa7, 0x4a, 0xd5, 0xcc, 0xce, 0xae, 0xd7, - 0xc9, 0x6e, 0x9c, 0x35, 0xe9, 0xa1, 0x17, 0xf0, 0xce, 0x3c, 0xf3, 0x7b, 0x7e, 0xcf, 0x33, 0xcf, - 0xd7, 0x04, 0xfc, 0x97, 0x50, 0x87, 0x50, 0x8b, 0x6a, 0xb4, 0xdb, 0xc1, 0xde, 0x9a, 0xdd, 0xb5, - 0x90, 0xc6, 0xb6, 0x2a, 0x1d, 0x8f, 0x30, 0xa2, 0xaa, 0x72, 0xb3, 0xd2, 0xdb, 0xcc, 0xcf, 0x9a, - 0xc4, 0x24, 0x62, 0x5b, 0xe3, 0xbf, 0x7c, 0xc9, 0xfc, 0x8c, 0xe1, 0x58, 0x2e, 0xd1, 0xc4, 0xbf, - 0x72, 0xa9, 0x60, 0x12, 0x62, 0xda, 0x58, 0x13, 0x5f, 0xad, 0xee, 0x9a, 0x86, 0xba, 0x9e, 0xc1, - 0x2c, 0xe2, 0x06, 0xfb, 0x6d, 0x81, 0xae, 0xb5, 0x0c, 0x8a, 0xb5, 0x8d, 0x6a, 0x0b, 0x33, 0xa3, - 0xaa, 0xb5, 0x89, 0x15, 0xec, 0x17, 0x77, 0x9f, 0x67, 0x96, 0x83, 0x29, 0x33, 0x9c, 0x8e, 0x14, - 0xb8, 0x10, 0x43, 0xbd, 0xf7, 0xd3, 0x17, 0x82, 0x5f, 0x2b, 0xe0, 0x64, 0x83, 0x9a, 0x0f, 0xc3, - 0xf5, 0x3a, 0xb6, 0xb1, 0x69, 0x30, 0xac, 0x5e, 0x01, 0x63, 0x14, 0xbb, 0x08, 0x7b, 0x39, 0xa5, - 0xa4, 0xcc, 0x1d, 0xab, 0xcd, 0xec, 0x6c, 0x17, 0x27, 0x9f, 0x19, 0x8e, 0xbd, 0x00, 0xfd, 0x75, - 0xa8, 0x4b, 0x01, 0xf5, 0x34, 0x18, 0xb7, 0x49, 0x7b, 0xbd, 0x69, 0xa1, 0x5c, 0xa6, 0xa4, 0xcc, - 0x8d, 0xea, 0x63, 0xfc, 0x73, 0x19, 0xa9, 0x67, 0xc0, 0xd1, 0x0d, 0xc3, 0x6e, 0x1a, 0x08, 0x79, - 0xb9, 0x2c, 0x47, 0xd1, 0xc7, 0x37, 0x0c, 0x7b, 0x11, 0x21, 0x6f, 0xa1, 0xf4, 0xfc, 0x8f, 0x97, - 0x57, 0x63, 0xbc, 0x5b, 0x46, 0x92, 0x00, 0x2c, 0x82, 0x73, 0xb1, 0xcc, 0x74, 0x4c, 0x3b, 0xc4, - 0xa5, 0x18, 0x7e, 0xae, 0x80, 0xd3, 0x7d, 0x12, 0x8f, 0x5c, 0x74, 0x88, 0xec, 0x17, 0x20, 0xa7, - 0x78, 0x2e, 0x86, 0x62, 0x37, 0xd4, 0x03, 0xcf, 0x83, 0x62, 0x02, 0x85, 0x90, 0xe6, 0x17, 0x7b, - 0x69, 0xb6, 0x88, 0x8b, 0x56, 0x48, 0x7b, 0xfd, 0x50, 0x68, 0x5e, 0xe0, 0x34, 0x0b, 0xb1, 0x34, - 0xb9, 0x9e, 0x32, 0x17, 0x8b, 0xe1, 0x19, 0x70, 0x08, 0x79, 0xfe, 0xa0, 0x80, 0x8b, 0x09, 0xb6, - 0x2c, 0xba, 0x87, 0x4c, 0x5a, 0xad, 0x81, 0x51, 0x1e, 0xcb, 0x22, 0x2a, 0x26, 0x6e, 0x9c, 0xa9, - 0xf8, 0xc1, 0x5e, 0xe1, 0xc1, 0x5e, 0x91, 0xc1, 0x5e, 0x59, 0x22, 0x96, 0x5b, 0xfb, 0xcf, 0xab, - 0xed, 0xe2, 0xc8, 0xce, 0x76, 0x71, 0xc2, 0x57, 0xc0, 0x0f, 0x41, 0x5d, 0x9c, 0x85, 0x1f, 0x82, - 0xeb, 0x07, 0xe1, 0x1b, 0x18, 0x18, 0x25, 0xa3, 0x44, 0xc9, 0xc0, 0x1d, 0x05, 0x9c, 0x6d, 0x50, - 0x93, 0x0b, 0x2f, 0xba, 0xe8, 0xfd, 0x72, 0xc1, 0x00, 0x47, 0x38, 0x39, 0x9a, 0xcb, 0x94, 0xb2, - 0xfb, 0x5b, 0x36, 0xcf, 0x2d, 0xfb, 0xfe, 0x4d, 0x71, 0xce, 0xb4, 0xd8, 0x93, 0x6e, 0xab, 0xd2, - 0x26, 0x8e, 0x26, 0x73, 0xde, 0xff, 0xaf, 0x4c, 0xd1, 0xba, 0xc6, 0x9e, 0x75, 0x30, 0x15, 0x07, - 0xa8, 0xee, 0x23, 0xef, 0x97, 0x55, 0x57, 0x78, 0x2c, 0x5c, 0x0c, 0x62, 0x81, 0x9b, 0x57, 0x36, - 0x5c, 0x54, 0x8e, 0x4b, 0xaf, 0x5b, 0xe2, 0xb6, 0x13, 0x6d, 0x0e, 0xbd, 0x36, 0x05, 0x32, 0xcb, - 0x75, 0xe9, 0xb0, 0xcc, 0x72, 0x1d, 0xbe, 0xcc, 0x00, 0xad, 0x41, 0xcd, 0x25, 0x0f, 0x1b, 0x0c, - 0xdf, 0xeb, 0xda, 0xb6, 0x6e, 0xb8, 0x26, 0x7e, 0x40, 0xa8, 0xc5, 0x8b, 0xd7, 0xbf, 0xdb, 0x7f, - 0xea, 0x35, 0x30, 0xde, 0x21, 0xc4, 0xe6, 0x21, 0x32, 0xca, 0x2d, 0xae, 0xa9, 0x3b, 0xdb, 0xc5, - 0x29, 0x9f, 0xa9, 0xdc, 0x80, 0xfa, 0x18, 0xff, 0xb5, 0x8c, 0x16, 0xfe, 0xc7, 0x9d, 0x0d, 0x03, - 0x67, 0xaf, 0x75, 0x6d, 0xbb, 0xec, 0x71, 0x5f, 0xf8, 0x2e, 0x5f, 0xeb, 0xb9, 0xfa, 0x29, 0xb8, - 0x9d, 0xd2, 0x63, 0xa1, 0xf7, 0x4f, 0x01, 0x3f, 0x48, 0xeb, 0x7d, 0x21, 0x5b, 0x57, 0x0b, 0x00, - 0x74, 0x24, 0xc0, 0x72, 0x5d, 0xe6, 0x56, 0x64, 0x85, 0xd7, 0xf5, 0x5c, 0x83, 0x9a, 0x8f, 0xdc, - 0x07, 0x84, 0xd8, 0x9f, 0x3c, 0xb1, 0x18, 0xb6, 0x2d, 0xca, 0x30, 0xe2, 0x9f, 0x69, 0xae, 0x23, - 0xe2, 0x90, 0xcc, 0x40, 0x87, 0x5c, 0xe4, 0x0e, 0x29, 0x06, 0x0e, 0xe9, 0xba, 0x7c, 0xb9, 0xbc, - 0xd9, 0x53, 0x5e, 0xe6, 0x0b, 0xf0, 0x3e, 0x28, 0x25, 0x31, 0x0b, 0xcd, 0xbe, 0x0c, 0x4e, 0xe0, - 0x2d, 0x8b, 0x61, 0xd4, 0x94, 0x19, 0x4b, 0x73, 0x4a, 0x29, 0x3b, 0x37, 0xaa, 0x4f, 0xfa, 0xcb, - 0x2b, 0x22, 0x71, 0x29, 0xfc, 0x2e, 0x0b, 0xee, 0x08, 0x30, 0xdb, 0x8f, 0xe3, 0x86, 0x65, 0x7a, - 0x06, 0xc3, 0x0f, 0x9f, 0x18, 0x1e, 0xa6, 0xab, 0x24, 0x74, 0xf6, 0x12, 0x71, 0xdb, 0xd8, 0x65, - 0x7c, 0x0f, 0x05, 0x8e, 0x4f, 0xe9, 0x86, 0x68, 0x1d, 0xcb, 0x46, 0xdd, 0x20, 0x37, 0x60, 0x58, - 0xdb, 0x4c, 0x30, 0x43, 0x05, 0x81, 0x26, 0x23, 0x4d, 0xc7, 0x67, 0x34, 0xb8, 0xd0, 0x95, 0x64, - 0xa1, 0xcb, 0x49, 0x06, 0xbb, 0x11, 0xa0, 0x7e, 0x82, 0x4a, 0xb3, 0xa4, 0x95, 0xea, 0x73, 0x05, - 0x4c, 0x31, 0xb2, 0x8e, 0xdd, 0x26, 0xe9, 0xb2, 0xa6, 0xc3, 0xb3, 0x66, 0x74, 0x50, 0xd6, 0x2c, - 0x4b, 0x35, 0x27, 0x7d, 0x35, 0xfd, 0xc7, 0x61, 0xaa, 0x74, 0x3a, 0x2e, 0x0e, 0x7f, 0xdc, 0x65, - 0x0d, 0xcb, 0xa5, 0x0b, 0x45, 0x7e, 0xf9, 0xf9, 0xde, 0xe5, 0x87, 0xc5, 0x27, 0xe0, 0xff, 0x73, - 0x16, 0xdc, 0x1d, 0xf6, 0xae, 0xc2, 0xc0, 0x78, 0x0c, 0xc6, 0x0d, 0x87, 0x74, 0x5d, 0x36, 0x2f, - 0x2f, 0xed, 0x2e, 0xb7, 0xe7, 0xd7, 0xed, 0xe2, 0xe5, 0x03, 0xd0, 0x5e, 0x76, 0x59, 0xef, 0xda, - 0x24, 0x0c, 0xd4, 0x03, 0xc0, 0x1e, 0x76, 0x55, 0x5c, 0xf2, 0x7b, 0x63, 0x57, 0x43, 0xec, 0xaa, - 0xba, 0x09, 0x66, 0x6c, 0xeb, 0x69, 0xd7, 0x42, 0x16, 0x7b, 0xd6, 0x6c, 0x8b, 0x4a, 0x80, 0xfc, - 0xe2, 0x53, 0xbb, 0x9f, 0x42, 0x4b, 0x1d, 0xb7, 0x7b, 0x21, 0xb2, 0x07, 0x10, 0xea, 0xd3, 0xe1, - 0x9a, 0x5f, 0x6d, 0x90, 0xfa, 0x08, 0x1c, 0xfb, 0x94, 0x58, 0x6e, 0x93, 0x4f, 0x87, 0xa2, 0xa6, - 0x4d, 0xdc, 0xc8, 0x57, 0xfc, 0xd1, 0xb1, 0x12, 0x8c, 0x8e, 0x95, 0xd5, 0x60, 0x74, 0xac, 0x9d, - 0x95, 0xe1, 0x31, 0xed, 0xab, 0x08, 0x8f, 0xc2, 0x17, 0x6f, 0x8a, 0x8a, 0x7e, 0x94, 0x7f, 0x73, - 0x61, 0xf8, 0x65, 0x56, 0x74, 0x81, 0x45, 0x84, 0x56, 0x49, 0xf4, 0xc2, 0x56, 0x02, 0xfd, 0xbd, - 0x9a, 0x16, 0xe6, 0xdb, 0x6d, 0x30, 0x11, 0x54, 0xa8, 0xb0, 0x07, 0xd7, 0x4e, 0xed, 0x6c, 0x17, - 0xd5, 0xa0, 0x9e, 0x84, 0x9b, 0x30, 0x52, 0xcc, 0x50, 0x24, 0x51, 0x33, 0x83, 0x12, 0xb5, 0x19, - 0x64, 0x04, 0xc2, 0xd4, 0xf2, 0x30, 0x9a, 0x1f, 0x9c, 0x78, 0xe7, 0xe2, 0x32, 0x22, 0x38, 0x0e, - 0xf5, 0x49, 0xb1, 0x50, 0x97, 0xdf, 0x7b, 0x14, 0x54, 0xa5, 0x53, 0x87, 0x54, 0x50, 0xdd, 0xa5, - 0xa0, 0xba, 0x70, 0x95, 0xe7, 0xd1, 0xa5, 0x20, 0x8f, 0x0c, 0x84, 0xca, 0x8c, 0x94, 0xdb, 0x76, - 0xb4, 0x87, 0x07, 0xae, 0x81, 0x3f, 0x65, 0x45, 0x67, 0x49, 0x73, 0x0b, 0x61, 0x26, 0x0d, 0x7d, - 0x1b, 0x91, 0x14, 0xcc, 0xfc, 0x83, 0x29, 0x98, 0x3d, 0xec, 0x14, 0x5c, 0x07, 0x93, 0x2e, 0xde, - 0x6c, 0x86, 0x19, 0x92, 0x3b, 0x22, 0x34, 0xdc, 0x4b, 0x9d, 0x7e, 0xb3, 0xbe, 0x86, 0x3e, 0x30, - 0xa8, 0x1f, 0x77, 0xf1, 0x66, 0xe8, 0xf7, 0x68, 0xc3, 0xd8, 0x33, 0x48, 0xec, 0x6e, 0x18, 0xf0, - 0xdb, 0x8c, 0x6c, 0xd6, 0x7c, 0x64, 0x5d, 0x22, 0xee, 0x06, 0xf6, 0x18, 0x1f, 0x0b, 0x98, 0xb1, - 0x8e, 0xa3, 0x48, 0xca, 0x20, 0xa4, 0x34, 0x99, 0xb2, 0xcf, 0x14, 0xe4, 0x81, 0x69, 0xc7, 0x72, - 0x9b, 0x86, 0xc3, 0x78, 0xff, 0xa1, 0x9c, 0x86, 0xb0, 0xe2, 0x98, 0xdf, 0x3d, 0x52, 0x5d, 0xc7, - 0x69, 0x5f, 0xfb, 0x6e, 0x3c, 0xa8, 0x4f, 0x3a, 0x96, 0xbb, 0xe8, 0xb0, 0x55, 0x22, 0xcc, 0x5c, - 0xb8, 0xc4, 0xc3, 0xbe, 0xd4, 0x6b, 0x1f, 0xe2, 0xe9, 0xd2, 0xf6, 0x7d, 0xe1, 0x0f, 0x54, 0xe2, - 0xd8, 0x5f, 0x8a, 0x9c, 0x1e, 0x62, 0x5c, 0x15, 0x86, 0x36, 0x05, 0xd3, 0x8c, 0x30, 0x6e, 0x9c, - 0xc3, 0x7c, 0x75, 0x48, 0x76, 0x8b, 0xa1, 0xf9, 0xef, 0xc6, 0x83, 0xfa, 0x94, 0x58, 0x5a, 0x74, - 0x98, 0xd0, 0x8d, 0x54, 0x04, 0x4e, 0xf9, 0x42, 0xb2, 0x73, 0x07, 0xd3, 0x1f, 0x92, 0x57, 0x51, - 0x49, 0x17, 0x67, 0xfa, 0xac, 0x40, 0xf3, 0xbb, 0x63, 0x30, 0x16, 0xa2, 0x1b, 0x7f, 0x4e, 0x80, - 0x6c, 0x83, 0x9a, 0xaa, 0x07, 0xd4, 0xb8, 0x39, 0xbb, 0xb2, 0xf7, 0x2f, 0x12, 0x95, 0xd8, 0x47, - 0x74, 0xbe, 0x7a, 0x60, 0xd1, 0xd0, 0xad, 0x5b, 0x60, 0x36, 0xf6, 0xad, 0x7d, 0x6d, 0x20, 0x54, - 0x4f, 0x38, 0x7f, 0x33, 0x85, 0x70, 0x92, 0xe6, 0xf0, 0x25, 0x7a, 0x10, 0xcd, 0x81, 0xf0, 0x81, - 0x34, 0xef, 0x79, 0x33, 0x7e, 0xa3, 0x80, 0xf3, 0x83, 0x5f, 0xc4, 0x77, 0x52, 0x18, 0xd5, 0x77, - 0x32, 0x7f, 0x77, 0xd8, 0x93, 0x21, 0xc3, 0xaf, 0x14, 0x70, 0x26, 0xf9, 0xe5, 0x3a, 0x9f, 0x80, - 0x9f, 0x78, 0x22, 0x7f, 0x27, 0xed, 0x89, 0x90, 0xc9, 0x8f, 0x0a, 0xb8, 0x9e, 0xea, 0x59, 0xb8, - 0x94, 0xa0, 0x2a, 0x0d, 0x48, 0xfe, 0xa3, 0x43, 0x00, 0x09, 0x4d, 0xf8, 0x0c, 0x9c, 0x8c, 0x7f, - 0x32, 0x5d, 0x4f, 0xd0, 0x12, 0x2b, 0x9d, 0xff, 0x7f, 0x1a, 0xe9, 0x50, 0xf9, 0x6f, 0x0a, 0xf8, - 0x60, 0xb8, 0x97, 0xcc, 0x4a, 0xa2, 0xbe, 0x21, 0xd0, 0xf2, 0xab, 0x87, 0x89, 0xd6, 0x17, 0x1d, - 0xa9, 0xc6, 0xc5, 0xa4, 0xe8, 0x48, 0x03, 0x92, 0x18, 0x1d, 0x43, 0x8d, 0x4c, 0x22, 0x3a, 0xe2, - 0x7a, 0x74, 0x72, 0x74, 0xc4, 0x48, 0xef, 0x13, 0x1d, 0xfb, 0x34, 0xb5, 0xda, 0x83, 0x57, 0x6f, - 0x0b, 0xca, 0xeb, 0xb7, 0x05, 0xe5, 0xf7, 0xb7, 0x05, 0xe5, 0xc5, 0xbb, 0xc2, 0xc8, 0xeb, 0x77, - 0x85, 0x91, 0x5f, 0xde, 0x15, 0x46, 0x1e, 0xdf, 0x8a, 0x74, 0x14, 0x89, 0x5c, 0xb6, 0x8d, 0x16, - 0x0d, 0x3e, 0xb4, 0x8d, 0xea, 0x6d, 0x6d, 0xab, 0xef, 0x2f, 0xd8, 0xbc, 0xcb, 0xb4, 0xc6, 0xc4, - 0xfc, 0x7f, 0xf3, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x81, 0xc5, 0xc4, 0x97, 0xe4, 0x16, 0x00, - 0x00, + // 1476 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xbb, 0x6f, 0xdb, 0x46, + 0x18, 0x37, 0x25, 0xc7, 0x4e, 0xce, 0xb1, 0x63, 0xb3, 0x79, 0x28, 0x6a, 0x22, 0x29, 0x97, 0x47, + 0x9d, 0x87, 0x44, 0x2b, 0x69, 0x93, 0xc0, 0x53, 0x2c, 0x0b, 0x29, 0x9c, 0x5a, 0x68, 0xc0, 0x38, + 0x28, 0x90, 0x45, 0xa0, 0x74, 0x67, 0x86, 0x35, 0xc9, 0x53, 0x78, 0x27, 0xdb, 0x41, 0xa7, 0xb6, + 0x43, 0x81, 0x4c, 0xd9, 0xda, 0xad, 0x6b, 0xdb, 0xa1, 0xc8, 0x9f, 0xd0, 0xa1, 0x43, 0xd0, 0x29, + 0x63, 0xd1, 0x02, 0x4e, 0x91, 0x0c, 0xdd, 0xbd, 0x74, 0x2d, 0x8e, 0x3c, 0x9e, 0x28, 0x9b, 0xb4, + 0x4c, 0xc5, 0x1d, 0xba, 0xd8, 0xbc, 0xbb, 0xef, 0xf1, 0xfb, 0xbe, 0xfb, 0x5e, 0x27, 0xf0, 0x3e, + 0xa1, 0x0e, 0xa1, 0x16, 0xd5, 0x68, 0xb7, 0x83, 0xbd, 0x55, 0xbb, 0x6b, 0x21, 0x8d, 0x6d, 0x56, + 0x3a, 0x1e, 0x61, 0x44, 0x55, 0xc5, 0x61, 0xa5, 0x77, 0x98, 0x3f, 0x6e, 0x12, 0x93, 0xf8, 0xc7, + 0x1a, 0xff, 0x0a, 0x28, 0xf3, 0x33, 0x86, 0x63, 0xb9, 0x44, 0xf3, 0xff, 0x8a, 0xad, 0x82, 0x49, + 0x88, 0x69, 0x63, 0xcd, 0x5f, 0xb5, 0xba, 0xab, 0x1a, 0xea, 0x7a, 0x06, 0xb3, 0x88, 0x1b, 0x9e, + 0xb7, 0x7d, 0xe9, 0x5a, 0xcb, 0xa0, 0x58, 0x5b, 0xaf, 0xb6, 0x30, 0x33, 0xaa, 0x5a, 0x9b, 0x58, + 0xe1, 0x79, 0x71, 0x27, 0x3f, 0xb3, 0x1c, 0x4c, 0x99, 0xe1, 0x74, 0x04, 0xc1, 0xf9, 0x18, 0xe8, + 0xbd, 0xcf, 0x80, 0x08, 0x7e, 0xa7, 0x80, 0x13, 0x0d, 0x6a, 0x3e, 0x90, 0xfb, 0x75, 0x6c, 0x63, + 0xd3, 0x60, 0x58, 0xbd, 0x0c, 0xc6, 0x28, 0x76, 0x11, 0xf6, 0x72, 0x4a, 0x49, 0x99, 0x3d, 0x52, + 0x9b, 0xd9, 0xde, 0x2a, 0x4e, 0x3e, 0x35, 0x1c, 0x7b, 0x1e, 0x06, 0xfb, 0x50, 0x17, 0x04, 0xea, + 0x29, 0x30, 0x6e, 0x93, 0xf6, 0x5a, 0xd3, 0x42, 0xb9, 0x4c, 0x49, 0x99, 0x1d, 0xd5, 0xc7, 0xf8, + 0x72, 0x09, 0xa9, 0xa7, 0xc1, 0xe1, 0x75, 0xc3, 0x6e, 0x1a, 0x08, 0x79, 0xb9, 0x2c, 0x97, 0xa2, + 0x8f, 0xaf, 0x1b, 0xf6, 0x02, 0x42, 0xde, 0x7c, 0xe9, 0xd9, 0xdf, 0x2f, 0xae, 0xc4, 0x78, 0xb7, + 0x8c, 0x04, 0x00, 0x58, 0x04, 0x67, 0x63, 0x91, 0xe9, 0x98, 0x76, 0x88, 0x4b, 0x31, 0xfc, 0x52, + 0x01, 0xa7, 0xfa, 0x28, 0x1e, 0xba, 0xe8, 0x00, 0xd1, 0xcf, 0x43, 0x0e, 0xf1, 0x6c, 0x0c, 0xc4, + 0xae, 0xd4, 0x03, 0xcf, 0x81, 0x62, 0x02, 0x04, 0x09, 0xf3, 0xab, 0xdd, 0x30, 0x5b, 0xc4, 0x45, + 0xcb, 0xa4, 0xbd, 0x76, 0x20, 0x30, 0xcf, 0x73, 0x98, 0x85, 0x58, 0x98, 0x5c, 0x4f, 0x99, 0x93, + 0xc5, 0xe0, 0x0c, 0x31, 0x48, 0x9c, 0x3f, 0x2b, 0xe0, 0x42, 0x82, 0x2d, 0x0b, 0xee, 0x01, 0x83, + 0x56, 0x6b, 0x60, 0x94, 0xc7, 0xb2, 0x1f, 0x15, 0x13, 0xd7, 0x4f, 0x57, 0x82, 0x60, 0xaf, 0xf0, + 0x60, 0xaf, 0x88, 0x60, 0xaf, 0x2c, 0x12, 0xcb, 0xad, 0xbd, 0xf7, 0x72, 0xab, 0x38, 0xb2, 0xbd, + 0x55, 0x9c, 0x08, 0x14, 0x70, 0x26, 0xa8, 0xfb, 0xbc, 0xf0, 0x63, 0x70, 0x6d, 0x3f, 0x78, 0x43, + 0x03, 0xa3, 0x60, 0x94, 0x28, 0x18, 0xb8, 0xad, 0x80, 0x33, 0x0d, 0x6a, 0x72, 0xe2, 0x05, 0x17, + 0xbd, 0x5b, 0x2e, 0x18, 0xe0, 0x10, 0x07, 0x47, 0x73, 0x99, 0x52, 0x76, 0x6f, 0xcb, 0xe6, 0xb8, + 0x65, 0x3f, 0xbd, 0x2e, 0xce, 0x9a, 0x16, 0x7b, 0xdc, 0x6d, 0x55, 0xda, 0xc4, 0xd1, 0x44, 0xce, + 0x07, 0xff, 0xca, 0x14, 0xad, 0x69, 0xec, 0x69, 0x07, 0x53, 0x9f, 0x81, 0xea, 0x81, 0xe4, 0xbd, + 0xb2, 0xea, 0x32, 0x8f, 0x85, 0x0b, 0x61, 0x2c, 0x70, 0xf3, 0xca, 0x86, 0x8b, 0xca, 0x71, 0xe9, + 0x75, 0xd3, 0xbf, 0xed, 0x44, 0x9b, 0xa5, 0xd7, 0xa6, 0x40, 0x66, 0xa9, 0x2e, 0x1c, 0x96, 0x59, + 0xaa, 0xc3, 0x17, 0x19, 0xa0, 0x35, 0xa8, 0xb9, 0xe8, 0x61, 0x83, 0xe1, 0xbb, 0x5d, 0xdb, 0xd6, + 0x0d, 0xd7, 0xc4, 0xf7, 0x09, 0xb5, 0x78, 0xf1, 0xfa, 0x7f, 0xfb, 0x4f, 0xbd, 0x0a, 0xc6, 0x3b, + 0x84, 0xd8, 0x3c, 0x44, 0x46, 0xb9, 0xc5, 0x35, 0x75, 0x7b, 0xab, 0x38, 0x15, 0x20, 0x15, 0x07, + 0x50, 0x1f, 0xe3, 0x5f, 0x4b, 0x68, 0xfe, 0x03, 0xee, 0x6c, 0x18, 0x3a, 0x7b, 0xb5, 0x6b, 0xdb, + 0x65, 0x8f, 0xfb, 0x22, 0x70, 0xf9, 0x6a, 0xcf, 0xd5, 0x4f, 0xc0, 0xad, 0x94, 0x1e, 0x93, 0xde, + 0x3f, 0x09, 0x82, 0x20, 0xad, 0xf7, 0x85, 0x6c, 0x5d, 0x2d, 0x00, 0xd0, 0x11, 0x02, 0x96, 0xea, + 0x22, 0xb7, 0x22, 0x3b, 0xbc, 0xae, 0xe7, 0x1a, 0xd4, 0x7c, 0xe8, 0xde, 0x27, 0xc4, 0xfe, 0xec, + 0xb1, 0xc5, 0xb0, 0x6d, 0x51, 0x86, 0x11, 0x5f, 0xa6, 0xb9, 0x8e, 0x88, 0x43, 0x32, 0x03, 0x1d, + 0x72, 0x81, 0x3b, 0xa4, 0x18, 0x3a, 0xa4, 0xeb, 0xf2, 0xed, 0xf2, 0x46, 0x4f, 0x79, 0x99, 0x6f, + 0xc0, 0x7b, 0xa0, 0x94, 0x84, 0x4c, 0x9a, 0x7d, 0x09, 0x1c, 0xc3, 0x9b, 0x16, 0xc3, 0xa8, 0x29, + 0x32, 0x96, 0xe6, 0x94, 0x52, 0x76, 0x76, 0x54, 0x9f, 0x0c, 0xb6, 0x97, 0xfd, 0xc4, 0xa5, 0xf0, + 0xc7, 0x2c, 0xb8, 0xed, 0x0b, 0xb3, 0x83, 0x38, 0x6e, 0x58, 0xa6, 0x67, 0x30, 0xfc, 0xe0, 0xb1, + 0xe1, 0x61, 0xba, 0x42, 0xa4, 0xb3, 0x17, 0x89, 0xdb, 0xc6, 0x2e, 0xe3, 0x67, 0x28, 0x74, 0x7c, + 0x4a, 0x37, 0x44, 0xeb, 0x58, 0x36, 0xea, 0x06, 0x71, 0x00, 0x65, 0x6d, 0x33, 0xc1, 0x0c, 0xf5, + 0x01, 0x34, 0x19, 0x69, 0x3a, 0x01, 0xa2, 0xc1, 0x85, 0xae, 0x24, 0x0a, 0x5d, 0x4e, 0x20, 0xd8, + 0x29, 0x01, 0xea, 0xc7, 0xa8, 0x30, 0x4b, 0x58, 0xa9, 0x3e, 0x53, 0xc0, 0x14, 0x23, 0x6b, 0xd8, + 0x6d, 0x92, 0x2e, 0x6b, 0x3a, 0x3c, 0x6b, 0x46, 0x07, 0x65, 0xcd, 0x92, 0x50, 0x73, 0x22, 0x50, + 0xd3, 0xcf, 0x0e, 0x53, 0xa5, 0xd3, 0x51, 0x9f, 0xf9, 0xd3, 0x2e, 0x6b, 0x58, 0x2e, 0x9d, 0x2f, + 0xf2, 0xcb, 0xcf, 0xf7, 0x2e, 0x5f, 0x16, 0x9f, 0x10, 0xff, 0x6f, 0x59, 0x70, 0x67, 0xd8, 0xbb, + 0x92, 0x81, 0xf1, 0x08, 0x8c, 0x1b, 0x0e, 0xe9, 0xba, 0x6c, 0x4e, 0x5c, 0xda, 0x1d, 0x6e, 0xcf, + 0x1f, 0x5b, 0xc5, 0x4b, 0xfb, 0x80, 0xbd, 0xe4, 0xb2, 0xde, 0xb5, 0x09, 0x31, 0x50, 0x0f, 0x05, + 0xf6, 0x64, 0x57, 0xfd, 0x4b, 0x7e, 0x67, 0xd9, 0x55, 0x29, 0xbb, 0xaa, 0x6e, 0x80, 0x19, 0xdb, + 0x7a, 0xd2, 0xb5, 0x90, 0xc5, 0x9e, 0x36, 0xdb, 0x7e, 0x25, 0x40, 0x41, 0xf1, 0xa9, 0xdd, 0x4b, + 0xa1, 0xa5, 0x8e, 0xdb, 0xbd, 0x10, 0xd9, 0x25, 0x10, 0xea, 0xd3, 0x72, 0x2f, 0xa8, 0x36, 0x48, + 0x7d, 0x08, 0x8e, 0x7c, 0x4e, 0x2c, 0xb7, 0xc9, 0xa7, 0x43, 0xbf, 0xa6, 0x4d, 0x5c, 0xcf, 0x57, + 0x82, 0xd1, 0xb1, 0x12, 0x8e, 0x8e, 0x95, 0x95, 0x70, 0x74, 0xac, 0x9d, 0x11, 0xe1, 0x31, 0x1d, + 0xa8, 0x90, 0xac, 0xf0, 0xf9, 0xeb, 0xa2, 0xa2, 0x1f, 0xe6, 0x6b, 0x4e, 0x0c, 0xbf, 0xce, 0xfa, + 0x5d, 0x60, 0x01, 0xa1, 0x15, 0x12, 0xbd, 0xb0, 0xe5, 0x50, 0x7f, 0xaf, 0xa6, 0xc9, 0x7c, 0xbb, + 0x05, 0x26, 0xc2, 0x0a, 0x25, 0x7b, 0x70, 0xed, 0xe4, 0xf6, 0x56, 0x51, 0x0d, 0xeb, 0x89, 0x3c, + 0x84, 0x91, 0x62, 0x86, 0x22, 0x89, 0x9a, 0x19, 0x94, 0xa8, 0xcd, 0x30, 0x23, 0x10, 0xa6, 0x96, + 0x87, 0xd1, 0xdc, 0xe0, 0xc4, 0x3b, 0x1b, 0x97, 0x11, 0x21, 0x3b, 0xd4, 0x27, 0xfd, 0x8d, 0xba, + 0x58, 0xef, 0x52, 0x50, 0x15, 0x4e, 0x1d, 0x52, 0x41, 0x75, 0x87, 0x82, 0xea, 0xfc, 0x15, 0x9e, + 0x47, 0x17, 0xc3, 0x3c, 0x32, 0x10, 0x2a, 0x33, 0x52, 0x6e, 0xdb, 0xd1, 0x1e, 0x1e, 0xba, 0x06, + 0xfe, 0x9a, 0xf5, 0x3b, 0x4b, 0x9a, 0x5b, 0x90, 0x99, 0x34, 0xf4, 0x6d, 0x44, 0x52, 0x30, 0xf3, + 0x1f, 0xa6, 0x60, 0xf6, 0xa0, 0x53, 0x70, 0x0d, 0x4c, 0xba, 0x78, 0xa3, 0x29, 0x33, 0x24, 0x77, + 0xc8, 0xd7, 0x70, 0x37, 0x75, 0xfa, 0x1d, 0x0f, 0x34, 0xf4, 0x09, 0x83, 0xfa, 0x51, 0x17, 0x6f, + 0x48, 0xbf, 0x47, 0x1b, 0xc6, 0xae, 0x41, 0x62, 0x67, 0xc3, 0x80, 0x3f, 0x64, 0x44, 0xb3, 0xe6, + 0x23, 0xeb, 0x22, 0x71, 0xd7, 0xb1, 0xc7, 0xf8, 0x58, 0xc0, 0x8c, 0x35, 0x1c, 0x95, 0xa4, 0x0c, + 0x92, 0x94, 0x26, 0x53, 0xf6, 0x98, 0x82, 0x3c, 0x30, 0xed, 0x58, 0x6e, 0xd3, 0x70, 0x18, 0xef, + 0x3f, 0x94, 0xc3, 0xf0, 0xad, 0x38, 0x12, 0x74, 0x8f, 0x54, 0xd7, 0x71, 0x2a, 0xd0, 0xbe, 0x53, + 0x1e, 0xd4, 0x27, 0x1d, 0xcb, 0x5d, 0x70, 0xd8, 0x0a, 0xf1, 0xcd, 0x9c, 0xbf, 0xc8, 0xc3, 0xbe, + 0xd4, 0x6b, 0x1f, 0xfe, 0xd3, 0xa5, 0x1d, 0xf8, 0x22, 0x18, 0xa8, 0x7c, 0xb6, 0x6f, 0x15, 0x31, + 0x3d, 0xc4, 0xb8, 0x4a, 0x86, 0x36, 0x05, 0xd3, 0x8c, 0x30, 0x6e, 0x9c, 0xc3, 0x02, 0x75, 0x48, + 0x74, 0x8b, 0xa1, 0xf1, 0xef, 0x94, 0x07, 0xf5, 0x29, 0x7f, 0x6b, 0xc1, 0x61, 0xbe, 0x6e, 0x74, + 0xfd, 0x9f, 0x09, 0x90, 0x6d, 0x50, 0x53, 0xf5, 0x80, 0x1a, 0x37, 0x01, 0x57, 0x76, 0xff, 0x56, + 0x50, 0x89, 0x7d, 0xde, 0xe6, 0xab, 0xfb, 0x26, 0x95, 0x06, 0x6f, 0x82, 0xe3, 0xb1, 0xaf, 0xe0, + 0xab, 0x03, 0x45, 0xf5, 0x88, 0xf3, 0x37, 0x52, 0x10, 0x27, 0x69, 0x96, 0x6f, 0xc4, 0xfd, 0x68, + 0x0e, 0x89, 0xf7, 0xa5, 0x79, 0xd7, 0x6b, 0xee, 0x7b, 0x05, 0x9c, 0x1b, 0xfc, 0x56, 0xbd, 0x9d, + 0xc2, 0xa8, 0x3e, 0xce, 0xfc, 0x9d, 0x61, 0x39, 0x25, 0xc2, 0x6f, 0x14, 0x70, 0x3a, 0xf9, 0x4d, + 0x39, 0x97, 0x20, 0x3f, 0x91, 0x23, 0x7f, 0x3b, 0x2d, 0x87, 0x44, 0xf2, 0x8b, 0x02, 0xae, 0xa5, + 0x7a, 0xb0, 0x2d, 0x26, 0xa8, 0x4a, 0x23, 0x24, 0xff, 0xc9, 0x01, 0x08, 0x91, 0x26, 0x7c, 0x01, + 0x4e, 0xc4, 0x3f, 0x66, 0xae, 0x25, 0x68, 0x89, 0xa5, 0xce, 0x7f, 0x98, 0x86, 0x5a, 0x2a, 0xff, + 0x53, 0x01, 0x1f, 0x0d, 0xf7, 0xc6, 0x58, 0x4e, 0xd4, 0x37, 0x84, 0xb4, 0xfc, 0xca, 0x41, 0x4a, + 0xeb, 0x8b, 0x8e, 0x54, 0x83, 0x5c, 0x52, 0x74, 0xa4, 0x11, 0x92, 0x18, 0x1d, 0x43, 0x0d, 0x33, + 0x7e, 0x74, 0xc4, 0x75, 0xcf, 0xe4, 0xe8, 0x88, 0xa1, 0xde, 0x23, 0x3a, 0xf6, 0x68, 0x37, 0xb5, + 0xfb, 0x2f, 0xdf, 0x14, 0x94, 0x57, 0x6f, 0x0a, 0xca, 0x5f, 0x6f, 0x0a, 0xca, 0xf3, 0xb7, 0x85, + 0x91, 0x57, 0x6f, 0x0b, 0x23, 0xbf, 0xbf, 0x2d, 0x8c, 0x3c, 0xba, 0x19, 0x69, 0x33, 0x42, 0x72, + 0xd9, 0x36, 0x5a, 0x34, 0x5c, 0x68, 0xeb, 0xd5, 0x5b, 0xda, 0x66, 0xdf, 0x6f, 0xcb, 0xbc, 0xf5, + 0xb4, 0xc6, 0xfc, 0xc9, 0xfc, 0xc6, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x76, 0x62, 0x15, + 0x7e, 0x16, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2479,16 +2476,6 @@ func (m *MsgUnbondConvertAndStakeResponse) MarshalToSizedBuffer(dAtA []byte) (in _ = i var l int _ = l - { - size := m.TotalSharesDelegated.Size() - i -= size - if _, err := m.TotalSharesDelegated.MarshalTo(dAtA[i:]); err != nil { - return 0, err - } - i = encodeVarintTx(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 { size := m.TotalAmtStaked.Size() i -= size @@ -2842,8 +2829,6 @@ func (m *MsgUnbondConvertAndStakeResponse) Size() (n int) { _ = l l = m.TotalAmtStaked.Size() n += 1 + l + sovTx(uint64(l)) - l = m.TotalSharesDelegated.Size() - n += 1 + l + sovTx(uint64(l)) return n } @@ -5180,40 +5165,6 @@ func (m *MsgUnbondConvertAndStakeResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalSharesDelegated", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.TotalSharesDelegated.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/x/valset-pref/keeper.go b/x/valset-pref/keeper.go index bbdfff2847e..b511a329971 100644 --- a/x/valset-pref/keeper.go +++ b/x/valset-pref/keeper.go @@ -48,7 +48,6 @@ func (k Keeper) GetDelegationPreferences(ctx sdk.Context, delegator string) (typ if err != nil { return types.ValidatorSetPreferences{}, err } - existingDelsValSetFormatted, err := k.GetExistingStakingDelegations(ctx, delAddr) if err != nil { return types.ValidatorSetPreferences{}, err @@ -69,7 +68,7 @@ func (k Keeper) GetExistingStakingDelegations(ctx sdk.Context, delAddr sdk.AccAd existingDelegations := k.stakingKeeper.GetDelegatorDelegations(ctx, delAddr, math.MaxUint16) if len(existingDelegations) == 0 { - return nil, fmt.Errorf("No Existing delegation") + return nil, types.ErrNoDelegation } existingTotalShares := sdk.NewDec(0) diff --git a/x/valset-pref/types/errors.go b/x/valset-pref/types/errors.go new file mode 100644 index 00000000000..0d2b988e047 --- /dev/null +++ b/x/valset-pref/types/errors.go @@ -0,0 +1,7 @@ +package types + +import "errors" + +var ( + ErrNoDelegation = errors.New("No existing delegation") +) diff --git a/x/valset-pref/validator_set.go b/x/valset-pref/validator_set.go index 744171955ad..7fe800f6d59 100644 --- a/x/valset-pref/validator_set.go +++ b/x/valset-pref/validator_set.go @@ -74,7 +74,7 @@ func (k Keeper) DelegateToValidatorSet(ctx sdk.Context, delegatorAddr string, co // get valset formatted delegation either from existing val set preference or existing delegations existingSet, err := k.GetDelegationPreferences(ctx, delegatorAddr) if err != nil { - return fmt.Errorf("error upon getting delegation preference for addr %s", delegatorAddr) + return err } delegator, err := sdk.AccAddressFromBech32(delegatorAddr) From acd179efa5311483f4aaef6ef1b8bbb9cfd1015f Mon Sep 17 00:00:00 2001 From: "Matt, Park" <45252226+mattverse@users.noreply.github.com> Date: Tue, 8 Aug 2023 20:33:42 +0900 Subject: [PATCH 10/27] Update proto/osmosis/superfluid/tx.proto Co-authored-by: Adam Tucker --- proto/osmosis/superfluid/tx.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index 03b4904d6b6..d6291b15d55 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -247,7 +247,7 @@ message MsgUnbondConvertAndStake { uint64 lock_id = 1 [ (gogoproto.moretags) = "yaml:\"lock_id\"" ]; string sender = 2 [ (gogoproto.moretags) = "yaml:\"sender\"" ]; // validator address to delegate to. - // If provided empty string, we use validator returned from valset-preference + // If provided empty string, we use the validators returned from valset-preference // module. string val_addr = 3; // min_amt_to_stake indicates the minimum amount to stake after conversion From 693c4e299b7242b4edf526a13fa6f759ae6100b9 Mon Sep 17 00:00:00 2001 From: "Matt, Park" <45252226+mattverse@users.noreply.github.com> Date: Tue, 8 Aug 2023 20:35:34 +0900 Subject: [PATCH 11/27] Update x/superfluid/keeper/stake.go Co-authored-by: alpo <62043214+AlpinYukseloglu@users.noreply.github.com> --- x/superfluid/keeper/stake.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index b3170629345..7e886732111 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -675,7 +675,7 @@ func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, va return sdk.ZeroInt(), err } - // use routeMigration method to check status of lock(either superfluid staked, superfluid unbonding, vanialla locked, unlocked) + // use routeMigration method to check status of lock (either superfluid staked, superfluid unbonding, vanilla locked, unlocked) _, migrationType, err := k.routeMigration(ctx, senderAddr, int64(lockID)) if err != nil { return sdk.ZeroInt(), err From be4c03a5c093354abdd82edf83f16dc2d38d5bfd Mon Sep 17 00:00:00 2001 From: mattverse Date: Tue, 8 Aug 2023 20:53:13 +0900 Subject: [PATCH 12/27] Romans comment, add balancer check --- .golangci.yml | 1 + x/superfluid/keeper/stake.go | 7 +++++++ x/superfluid/keeper/stake_test.go | 14 ++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index 2be93b35685..3e367e1e7ae 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,6 +9,7 @@ linters: enable: - asciicheck - bidichk + - depguard - durationcheck - errcheck - errname diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 7e886732111..12c73631806 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + "strings" errorsmod "cosmossdk.io/errors" @@ -711,6 +712,12 @@ func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAd } lockCoin := lock.Coins[0] + + // Ensuring the sharesToMigrate contains gamm pool share prefix. + if !strings.HasPrefix(lockCoin.Denom, gammtypes.GAMMTokenPrefix) { + return sdk.ZeroInt(), types.SharesToMigrateDenomPrefixError{Denom: lockCoin.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} + } + poolIdLeaving, err := gammtypes.GetPoolIdFromShareDenom(lockCoin.Denom) if err != nil { return sdk.ZeroInt(), err diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 010e2a49eea..573988e270b 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1117,6 +1117,7 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { useMinAmountToStake bool senderIsNotOwnerOfLock bool + useNonBalancerLock bool expectedError bool } @@ -1135,6 +1136,10 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { useMinAmountToStake: true, expectedError: true, }, + "error: use non balancner lock": { + useNonBalancerLock: true, + expectedError: true, + }, "error: sender is not owner of lock ": { senderIsNotOwnerOfLock: true, expectedError: true, @@ -1153,6 +1158,15 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { if tc.senderIsNotOwnerOfLock { sender = s.TestAccs[1] } + + if tc.useNonBalancerLock { + nonBalancerShareDenomCoins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(100))) + s.FundAcc(sender, nonBalancerShareDenomCoins) + newLock, err := s.App.LockupKeeper.CreateLock(s.Ctx, sender, nonBalancerShareDenomCoins, time.Second) + s.Require().NoError(err) + lock = &newLock + } + valAddr := s.SetupValidator(stakingtypes.Bonded) minAmountToStake := sdk.ZeroInt() if tc.useMinAmountToStake { From c89e9c3945e0dc402e836c4805182c1718d81be5 Mon Sep 17 00:00:00 2001 From: mattverse Date: Tue, 8 Aug 2023 21:35:33 +0900 Subject: [PATCH 13/27] Adam's comment --- proto/osmosis/superfluid/tx.proto | 4 ++-- x/superfluid/keeper/stake_test.go | 30 +++++++++++++++++++----------- x/superfluid/types/tx.pb.go | 4 ++-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index d6291b15d55..b28eb1eefac 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -247,8 +247,8 @@ message MsgUnbondConvertAndStake { uint64 lock_id = 1 [ (gogoproto.moretags) = "yaml:\"lock_id\"" ]; string sender = 2 [ (gogoproto.moretags) = "yaml:\"sender\"" ]; // validator address to delegate to. - // If provided empty string, we use the validators returned from valset-preference - // module. + // If provided empty string, we use the validators returned from + // valset-preference module. string val_addr = 3; // min_amt_to_stake indicates the minimum amount to stake after conversion string min_amt_to_stake = 4 [ diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 573988e270b..12f22280901 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1034,11 +1034,11 @@ func (s *KeeperTestSuite) TestRefreshIntermediaryDelegationAmounts() { func (s *KeeperTestSuite) TestUnbondConvertAndStake() { defaultJoinTime := s.Ctx.BlockTime() type tc struct { + notSuperfluidDelegated bool superfluidUndelegating bool unlocking bool unlocked bool - - expectedError bool + expectedError bool } testCases := map[string]tc{ "lock that is superfluid delegated": {}, @@ -1046,13 +1046,17 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { unlocking: true, superfluidUndelegating: true, }, + "bonded lock, not superfluid delegated": { + notSuperfluidDelegated: true, + }, "lock that is unlocking": { unlocking: true, - superfluidUndelegating: false, + superfluidUndelegating: true, }, - "unlocked gamm shares": { - unlocking: true, - superfluidUndelegating: false, + "error: unlocked gamm shares": { + notSuperfluidDelegated: true, + unlocked: true, + expectedError: true, }, } @@ -1061,10 +1065,10 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { s.SetupTest() s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) // We bundle all migration setup into a single function to avoid repeating the same code for each test case. - _, _, lock, _, _, _, _, originalValAddr := s.SetupUnbondConvertAndStakeTest(s.Ctx, true, tc.superfluidUndelegating, false, false) + _, _, lock, _, joinPoolAcc, _, _, originalValAddr := s.SetupUnbondConvertAndStakeTest(s.Ctx, !tc.notSuperfluidDelegated, tc.superfluidUndelegating, tc.unlocking, tc.unlocked) // testing params - sender := sdk.MustAccAddressFromBech32(lock.Owner) + sender := sdk.MustAccAddressFromBech32(joinPoolAcc.String()) valAddr := s.SetupValidator(stakingtypes.Bonded) minAmountToStake := sdk.ZeroInt() @@ -1079,7 +1083,7 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { s.Require().NoError(err) // Staking & Delegation check - // check if old delegation is succesfully deleted + // check if original superfluid staked lock's delgation is successfully deleted _, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, originalValAddr) s.Require().False(found) // check if delegation amount matches @@ -1114,6 +1118,7 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { type tc struct { superfluidUndelegating bool unlocking bool + notSuperfluidDelegated bool useMinAmountToStake bool senderIsNotOwnerOfLock bool @@ -1131,6 +1136,9 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { unlocking: true, superfluidUndelegating: false, }, + "bonded lock, not superfluid delegated": { + notSuperfluidDelegated: true, + }, // error cases "error: min amount to stake greater than actual amount": { useMinAmountToStake: true, @@ -1151,7 +1159,7 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { s.SetupTest() s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) // We bundle all migration setup into a single function to avoid repeating the same code for each test case. - _, _, lock, _, _, _, _, originalValAddr := s.SetupUnbondConvertAndStakeTest(s.Ctx, true, tc.superfluidUndelegating, false, false) + _, _, lock, _, _, _, _, originalValAddr := s.SetupUnbondConvertAndStakeTest(s.Ctx, !tc.notSuperfluidDelegated, tc.superfluidUndelegating, false, false) // testing params sender := sdk.MustAccAddressFromBech32(lock.Owner) @@ -1219,7 +1227,7 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { expectedError bool } testCases := map[string]tc{ - "happy case": {}, + "superfluid staked": {}, "use val set preference (single validator)": { useValSetPrefSingleVal: true, }, diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index 6b9a99cb2f1..165902dc113 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -1028,8 +1028,8 @@ type MsgUnbondConvertAndStake struct { LockId uint64 `protobuf:"varint,1,opt,name=lock_id,json=lockId,proto3" json:"lock_id,omitempty" yaml:"lock_id"` Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` // validator address to delegate to. - // If provided empty string, we use validator returned from valset-preference - // module. + // If provided empty string, we use the validators returned from + // valset-preference module. ValAddr string `protobuf:"bytes,3,opt,name=val_addr,json=valAddr,proto3" json:"val_addr,omitempty"` // min_amt_to_stake indicates the minimum amount to stake after conversion MinAmtToStake github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=min_amt_to_stake,json=minAmtToStake,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_amt_to_stake" yaml:"min_amt_to_stake"` From 23f53c316a1918a54270ff183e77530e2e21faa0 Mon Sep 17 00:00:00 2001 From: mattverse Date: Tue, 8 Aug 2023 22:02:10 +0900 Subject: [PATCH 14/27] Change errors from bool to check error --- x/superfluid/keeper/stake.go | 9 ++++++ x/superfluid/keeper/stake_test.go | 49 +++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 12c73631806..fdb47c3ca98 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -711,6 +711,15 @@ func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAd return sdk.ZeroInt(), err } + // check lock owner is sender + if lock.Owner != sender.String() { + return sdk.ZeroInt(), types.LockOwnerMismatchError{ + LockId: lock.ID, + LockOwner: lock.Owner, + ProvidedOwner: sender.String(), + } + } + lockCoin := lock.Coins[0] // Ensuring the sharesToMigrate contains gamm pool share prefix. diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 12f22280901..14fecb059db 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "fmt" "time" abci "github.com/tendermint/tendermint/abci/types" @@ -1038,7 +1039,7 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { superfluidUndelegating bool unlocking bool unlocked bool - expectedError bool + expectedError error } testCases := map[string]tc{ "lock that is superfluid delegated": {}, @@ -1056,7 +1057,7 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { "error: unlocked gamm shares": { notSuperfluidDelegated: true, unlocked: true, - expectedError: true, + expectedError: fmt.Errorf("unsupported staking conversion type"), }, } @@ -1076,7 +1077,8 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { // system under test totalAmtConverted, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lock.ID, sender.String(), valAddr.String(), minAmountToStake) - if tc.expectedError { + if tc.expectedError != nil { + s.Require().Equal(err.Error(), tc.expectedError.Error()) s.Require().Error(err) return } @@ -1124,7 +1126,7 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { senderIsNotOwnerOfLock bool useNonBalancerLock bool - expectedError bool + expectedError error } testCases := map[string]tc{ "lock that is superfluid delegated": {}, @@ -1142,15 +1144,25 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { // error cases "error: min amount to stake greater than actual amount": { useMinAmountToStake: true, - expectedError: true, + expectedError: types.TokenConvertedLessThenDesiredStakeError{ + ActualTotalAmtToStake: sdk.NewInt(8309), + ExpectedTotalAmtToStake: sdk.NewInt(999999999), + }, }, - "error: use non balancner lock": { + "error: use non balancer lock": { useNonBalancerLock: true, - expectedError: true, + expectedError: types.SharesToMigrateDenomPrefixError{ + Denom: "foo", + ExpectedDenomPrefix: "gamm/pool/", + }, }, "error: sender is not owner of lock ": { senderIsNotOwnerOfLock: true, - expectedError: true, + expectedError: types.LockOwnerMismatchError{ + LockId: 1, + LockOwner: s.TestAccs[0].String(), + ProvidedOwner: s.TestAccs[1].String(), + }, }, } @@ -1185,8 +1197,14 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { // system under test totalAmtConverted, err := s.App.SuperfluidKeeper.ConvertLockToStake(s.Ctx, sender, valAddr.String(), lock.ID, minAmountToStake) - if tc.expectedError { + if tc.expectedError != nil { s.Require().Error(err) + // TODO: come back to this specific err case + // err check for LockOwnerMismatchError needs further refactoring for all these test cases + // since lock owner is not know-able at the time of test creation + if !tc.senderIsNotOwnerOfLock { + s.Require().Equal(err.Error(), tc.expectedError.Error()) + } return } s.Require().NoError(err) @@ -1224,28 +1242,26 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { useValSetPrefMultipleVal bool useSuperfluid bool - expectedError bool + expectedError string } testCases := map[string]tc{ - "superfluid staked": {}, + "superfluid staked, provide validator address": {}, "use val set preference (single validator)": { useValSetPrefSingleVal: true, }, "multiple validator returned from valset pref": { useValSetPrefMultipleVal: true, - expectedError: false, }, "No validator returned from valset, fall back to superfluid delegation": { useSuperfluid: true, - expectedError: false, }, "error: invalid val address": { useInvalidValAddr: true, - expectedError: true, + expectedError: "invalid Bech32 prefix; expected osmovaloper, got osmo", }, "error: min amount to stake exceeds actual amount staking": { useMinAmtToStake: true, - expectedError: true, + expectedError: "actual amount converted to stake (8309) is less then minimum amount expected to be staked (999999999)", }, } @@ -1319,7 +1335,8 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { // system under test. totalAmtConverted, err := s.App.SuperfluidKeeper.ConvertGammSharesToOsmoAndStake(s.Ctx, sender, valAddrString, poolId, exitCoins, minAmtToStake, originalSuperfluidValAddr) - if tc.expectedError { + if tc.expectedError != "" { + s.Require().Equal(err.Error(), tc.expectedError) s.Require().Error(err) return } From 0c5b652ee73f06c3fda563502f8a1f8e3b1669b5 Mon Sep 17 00:00:00 2001 From: mattverse Date: Wed, 9 Aug 2023 17:05:01 +0900 Subject: [PATCH 15/27] Add validate basic, bring back partial share migration for liquid gamm shares --- proto/osmosis/superfluid/tx.proto | 8 + x/superfluid/keeper/export_test.go | 5 + x/superfluid/keeper/msg_server.go | 2 +- x/superfluid/keeper/stake.go | 44 +++++- x/superfluid/keeper/stake_test.go | 117 ++++++++++++-- x/superfluid/types/msg_test.go | 80 ++++++++++ x/superfluid/types/msgs.go | 7 +- x/superfluid/types/tx.pb.go | 246 ++++++++++++++++++----------- 8 files changed, 396 insertions(+), 113 deletions(-) diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index b28eb1eefac..9ca6df217f5 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -256,6 +256,14 @@ message MsgUnbondConvertAndStake { (gogoproto.moretags) = "yaml:\"min_amt_to_stake\"", (gogoproto.nullable) = false ]; + // shares_to_convert indicates shares wanted to stake. + // Note that this field is only used for liquid(unlocked) gamm shares. + // For all other cases, this field would be disregarded. + cosmos.base.v1beta1.Coin shares_to_convert = 5 [ + (gogoproto.nullable) = false, + (gogoproto.moretags) = "yaml:\"shares_to_convert\"", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coin" + ]; } message MsgUnbondConvertAndStakeResponse { diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 77136372da2..93cf98654d9 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -73,3 +73,8 @@ func (k Keeper) ConvertGammSharesToOsmoAndStake( ) (totalAmtCoverted sdk.Int, err error) { return k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake, originalSuperfluidValAddr) } + +func (k Keeper) ConvertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, sharesToStake sdk.Coin, + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { + return k.convertUnlockedToStake(ctx, sender, valAddr, sharesToStake, minAmtToStake) +} diff --git a/x/superfluid/keeper/msg_server.go b/x/superfluid/keeper/msg_server.go index 5b635824818..4262f7fce26 100644 --- a/x/superfluid/keeper/msg_server.go +++ b/x/superfluid/keeper/msg_server.go @@ -246,7 +246,7 @@ func (server msgServer) AddToConcentratedLiquiditySuperfluidPosition(goCtx conte func (server msgServer) UnbondConvertAndStake(goCtx context.Context, msg *types.MsgUnbondConvertAndStake) (*types.MsgUnbondConvertAndStakeResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - totalAmtConverted, err := server.keeper.UnbondConvertAndStake(ctx, msg.LockId, msg.Sender, msg.ValAddr, msg.MinAmtToStake) + totalAmtConverted, err := server.keeper.UnbondConvertAndStake(ctx, msg.LockId, msg.Sender, msg.ValAddr, msg.MinAmtToStake, msg.SharesToConvert) if err != nil { return nil, err } diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index fdb47c3ca98..5f4607cea70 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -670,7 +670,7 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn // - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator // - Else: error func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, - minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { + minAmtToStake sdk.Int, sharesToConvert sdk.Coin) (totalAmtConverted sdk.Int, err error) { senderAddr, err := sdk.AccAddressFromBech32(sender) if err != nil { return sdk.ZeroInt(), err @@ -692,13 +692,16 @@ func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, va if migrationType == SuperfluidBonded || migrationType == SuperfluidUnbonding || migrationType == NonSuperfluid { totalAmtConverted, err = k.convertLockToStake(ctx, senderAddr, valAddr, lockID, minAmtToStake) - if err != nil { - return sdk.ZeroInt(), err - } + } else if migrationType == Unlocked { + totalAmtConverted, err = k.convertUnlockedToStake(ctx, senderAddr, valAddr, sharesToConvert, minAmtToStake) } else { // liquid gamm shares without locks are not supported return sdk.ZeroInt(), fmt.Errorf("unsupported staking conversion type") } + if err != nil { + return sdk.ZeroInt(), err + } + return totalAmtConverted, nil } @@ -753,6 +756,33 @@ func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAd return totalAmtConverted, nil } +func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, sharesToStake sdk.Coin, + minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { + if !strings.HasPrefix(sharesToStake.Denom, gammtypes.GAMMTokenPrefix) { + return sdk.ZeroInt(), types.SharesToMigrateDenomPrefixError{Denom: sharesToStake.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} + } + + // Get the balancer poolId by parsing the gamm share denom. + poolIdLeaving, err := gammtypes.GetPoolIdFromShareDenom(sharesToStake.Denom) + if err != nil { + return sdk.ZeroInt(), err + } + + // Exit the balancer pool position. + // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. + exitCoins, err := k.gk.ExitPool(ctx, sender, poolIdLeaving, sharesToStake.Amount, sdk.NewCoins()) + if err != nil { + return sdk.ZeroInt(), err + } + + totalAmtConverted, err = k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake, "") + if err != nil { + return sdk.ZeroInt(), err + } + + return totalAmtConverted, nil +} + // convertGammSharesToOsmoAndStake converts given gamm shares to osmo by swapping in the given pool // then stakes it to the designated validator. // minAmtToStake works as slippage bound, and would error if total amount being staked is less than min amount to stake. @@ -803,14 +833,14 @@ func (k Keeper) convertGammSharesToOsmoAndStake( } } - var val stakingtypes.Validator + // var val stakingtypes.Validator // if given valAddr is empty, we use delegation preference given from valset-pref module or reference from superfluid staking if valAddr == "" { err := k.vspk.DelegateToValidatorSet(ctx, sender.String(), sdk.NewCoin(bondDenom, totalAmtToStake)) // if valset-pref delegation errored due to no existing delegation existing, fall back and try using superfluid staked validator if err == valsettypes.ErrNoDelegation { - val, err = k.validateValAddrForDelegate(ctx, originalSuperfluidValAddr) + val, err := k.validateValAddrForDelegate(ctx, originalSuperfluidValAddr) if err != nil { return sdk.ZeroInt(), err } @@ -827,7 +857,7 @@ func (k Keeper) convertGammSharesToOsmoAndStake( return sdk.ZeroInt(), err } } else { - val, err = k.validateValAddrForDelegate(ctx, valAddr) + val, err := k.validateValAddrForDelegate(ctx, valAddr) if err != nil { return sdk.ZeroInt(), err } diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 14fecb059db..f344902ea6e 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "fmt" "time" abci "github.com/tendermint/tendermint/abci/types" @@ -1044,7 +1043,6 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { testCases := map[string]tc{ "lock that is superfluid delegated": {}, "lock that is superfluid undelegating": { - unlocking: true, superfluidUndelegating: true, }, "bonded lock, not superfluid delegated": { @@ -1054,10 +1052,9 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { unlocking: true, superfluidUndelegating: true, }, - "error: unlocked gamm shares": { + "unlocked gamm shares": { notSuperfluidDelegated: true, unlocked: true, - expectedError: fmt.Errorf("unsupported staking conversion type"), }, } @@ -1066,17 +1063,22 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { s.SetupTest() s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) // We bundle all migration setup into a single function to avoid repeating the same code for each test case. - _, _, lock, _, joinPoolAcc, _, _, originalValAddr := s.SetupUnbondConvertAndStakeTest(s.Ctx, !tc.notSuperfluidDelegated, tc.superfluidUndelegating, tc.unlocking, tc.unlocked) + _, _, lock, _, joinPoolAcc, _, balancerShareOut, originalValAddr := s.SetupUnbondConvertAndStakeTest(s.Ctx, !tc.notSuperfluidDelegated, tc.superfluidUndelegating, tc.unlocking, tc.unlocked) // testing params sender := sdk.MustAccAddressFromBech32(joinPoolAcc.String()) valAddr := s.SetupValidator(stakingtypes.Bonded) minAmountToStake := sdk.ZeroInt() + sharesToConvert := sdk.NewInt64Coin("foo", 0) + if tc.unlocked { + sharesToConvert = balancerShareOut + } - balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) + // only test with test related denoms + balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender).FilterDenoms([]string{"foo", "stake", "uosmo"}) // system under test - totalAmtConverted, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lock.ID, sender.String(), valAddr.String(), minAmountToStake) + totalAmtConverted, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lock.ID, sender.String(), valAddr.String(), minAmountToStake, sharesToConvert) if tc.expectedError != nil { s.Require().Equal(err.Error(), tc.expectedError.Error()) s.Require().Error(err) @@ -1093,8 +1095,15 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { s.Require().True(found) s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) + // Bank check + balanceAfterConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender).FilterDenoms([]string{"foo", "stake", "uosmo"}) + s.Require().True(balanceBeforeConvertLockToStake.IsEqual(balanceAfterConvertLockToStake)) + // Superfluid check // The synthetic lockup should be deleted. + if tc.unlocked { + return + } _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.StakingSyntheticDenom(lock.Coins[0].Denom, valAddr.String())) s.Require().Error(err) @@ -1108,9 +1117,6 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { _, err = s.App.LockupKeeper.GetLockByID(s.Ctx, lock.ID) s.Require().Error(err) - // Bank check - balanceAfterConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) - s.Require().True(balanceBeforeConvertLockToStake.IsEqual(balanceAfterConvertLockToStake)) }) } } @@ -1234,6 +1240,97 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { } } +func (s *KeeperTestSuite) TestConvertUnlockedToStake() { + defaultJoinTime := s.Ctx.BlockTime() + type tc struct { + usePartialShares bool + useMinAmountToStake bool + useNonGammPrefix bool + expectedError error + } + testCases := map[string]tc{ + "convert unlocked gamm shares": {}, + "convert partial shares": { + usePartialShares: true, + }, + "min amount to stake exceeds exit pool amount": { + useMinAmountToStake: true, + expectedError: types.TokenConvertedLessThenDesiredStakeError{ + ActualTotalAmtToStake: sdk.NewInt(8309), + ExpectedTotalAmtToStake: sdk.NewInt(999999999), + }, + }, + "error: use non gamm prefix": { + useNonGammPrefix: true, + expectedError: types.TokenConvertedLessThenDesiredStakeError{ + ActualTotalAmtToStake: sdk.NewInt(8309), + ExpectedTotalAmtToStake: sdk.NewInt(999999999), + }, + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.SetupTest() + s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) + + // We bundle all migration setup into a single function to avoid repeating the same code for each test case. + _, _, _, _, sender, poolId, shareOut, _ := s.SetupUnbondConvertAndStakeTest(s.Ctx, false, false, false, true) + + // testing params + valAddr := s.SetupValidator(stakingtypes.Bonded) + minAmtToStake := sdk.ZeroInt() + if tc.useMinAmountToStake { + minAmtToStake = sdk.NewInt(9999999999) + } + sharesToStake := shareOut + if tc.usePartialShares { + sharesToStake.Amount = sharesToStake.Amount.Quo(sdk.NewInt(2)) + } + if tc.useNonGammPrefix { + sharesToStake = sdk.NewInt64Coin("foo", 10) + } + + balanceBeforeConvert := s.App.BankKeeper.GetBalance(s.Ctx, sender, shareOut.Denom) + s.Require().True(!balanceBeforeConvert.Amount.IsZero()) + + bondDenom := s.App.StakingKeeper.BondDenom(s.Ctx) + totalPoolLiquidityBeforeConvert, err := s.App.GAMMKeeper.GetTotalPoolLiquidity(s.Ctx, poolId) + s.Require().NoError(err) + bondDenomPoolAmtBeforeConvert := totalPoolLiquidityBeforeConvert.AmountOf(bondDenom) + + // system under test + totalAmtConverted, err := s.App.SuperfluidKeeper.ConvertUnlockedToStake(s.Ctx, sender, valAddr.String(), sharesToStake, minAmtToStake) + if tc.expectedError != nil { + s.Require().Error(err) + return + } + s.Require().NoError(err) + + // gamm check + totalPoolLiquidityAfterConvert, err := s.App.GAMMKeeper.GetTotalPoolLiquidity(s.Ctx, poolId) + s.Require().NoError(err) + // check that pool liquidity have reduced + bondDenomPoolAmtAfterConvert := totalPoolLiquidityAfterConvert.AmountOf(bondDenom) + s.Require().True(bondDenomPoolAmtAfterConvert.LT(bondDenomPoolAmtBeforeConvert)) + + // Staking & Delegation check + // check if delegation amount matches + delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) + s.Require().True(found) + s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) + + // Bank check + balanceAfterConvertLockToStake := s.App.BankKeeper.GetBalance(s.Ctx, sender, shareOut.Denom) + if tc.usePartialShares { + s.Require().True(balanceAfterConvertLockToStake.Amount.Equal(sharesToStake.Amount)) + } else { + s.Require().True(balanceAfterConvertLockToStake.IsZero()) + } + }) + } +} + func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { type tc struct { useInvalidValAddr bool diff --git a/x/superfluid/types/msg_test.go b/x/superfluid/types/msg_test.go index 9d3d4ab6f20..c62ac21bb90 100644 --- a/x/superfluid/types/msg_test.go +++ b/x/superfluid/types/msg_test.go @@ -4,6 +4,8 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/osmosis-labs/osmosis/v17/app/apptesting" "github.com/osmosis-labs/osmosis/v17/x/superfluid/types" @@ -73,3 +75,81 @@ func TestAuthzMsg(t *testing.T) { }) } } + +func TestUnbondConvertAndStakeMsg(t *testing.T) { + pk1 := ed25519.GenPrivKey().PubKey() + addr1 := sdk.AccAddress(pk1.Address()).String() + + valPub := secp256k1.GenPrivKey().PubKey() + valAddr := sdk.ValAddress(valPub.Address()).String() + + testCases := []struct { + name string + msg sdk.Msg + expectedError bool + }{ + { + name: "happy case", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 2, + Sender: addr1, + ValAddr: valAddr, + MinAmtToStake: sdk.NewInt(10), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + }, + { + name: "lock id is 0 should not fail", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 0, + Sender: addr1, + ValAddr: valAddr, + MinAmtToStake: sdk.NewInt(10), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + }, + { + name: "err: sender is invalid", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 0, + Sender: "abcd", + ValAddr: valAddr, + MinAmtToStake: sdk.NewInt(10), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + expectedError: true, + }, + { + name: "err: val address in invalid", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 0, + Sender: addr1, + ValAddr: "", + MinAmtToStake: sdk.NewInt(10), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + expectedError: true, + }, + { + name: "err: min amount to stake is negative", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 0, + Sender: addr1, + ValAddr: "abcd", + MinAmtToStake: sdk.NewInt(10).Neg(), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + expectedError: true, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.msg.ValidateBasic() + if tc.expectedError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/superfluid/types/msgs.go b/x/superfluid/types/msgs.go index 78e49e9bb92..3b731f1d327 100644 --- a/x/superfluid/types/msgs.go +++ b/x/superfluid/types/msgs.go @@ -406,7 +406,12 @@ func (msg MsgUnbondConvertAndStake) ValidateBasic() error { if err != nil { return fmt.Errorf("Invalid sender address (%s)", err) } - + if msg.ValAddr == "" { + return fmt.Errorf("ValAddr should not be empty") + } + if msg.MinAmtToStake.IsNegative() { + return fmt.Errorf("Min amount to stake cannot be negative") + } return nil } diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index 165902dc113..1aa375fd1c2 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -1033,6 +1033,10 @@ type MsgUnbondConvertAndStake struct { ValAddr string `protobuf:"bytes,3,opt,name=val_addr,json=valAddr,proto3" json:"val_addr,omitempty"` // min_amt_to_stake indicates the minimum amount to stake after conversion MinAmtToStake github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=min_amt_to_stake,json=minAmtToStake,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"min_amt_to_stake" yaml:"min_amt_to_stake"` + // shares_to_convert indicates shares wanted to stake. + // Note that this field is only used for liquid(unlocked) gamm shares. + // For all other cases, this field would be disregarded. + SharesToConvert types.Coin `protobuf:"bytes,5,opt,name=shares_to_convert,json=sharesToConvert,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coin" json:"shares_to_convert" yaml:"shares_to_convert"` } func (m *MsgUnbondConvertAndStake) Reset() { *m = MsgUnbondConvertAndStake{} } @@ -1089,6 +1093,13 @@ func (m *MsgUnbondConvertAndStake) GetValAddr() string { return "" } +func (m *MsgUnbondConvertAndStake) GetSharesToConvert() types.Coin { + if m != nil { + return m.SharesToConvert + } + return types.Coin{} +} + type MsgUnbondConvertAndStakeResponse struct { TotalAmtStaked github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=total_amt_staked,json=totalAmtStaked,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"total_amt_staked" yaml:"total_amt_staked"` } @@ -1152,100 +1163,102 @@ func init() { func init() { proto.RegisterFile("osmosis/superfluid/tx.proto", fileDescriptor_55b645f187d22814) } var fileDescriptor_55b645f187d22814 = []byte{ - // 1476 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xbb, 0x6f, 0xdb, 0x46, - 0x18, 0x37, 0x25, 0xc7, 0x4e, 0xce, 0xb1, 0x63, 0xb3, 0x79, 0x28, 0x6a, 0x22, 0x29, 0x97, 0x47, - 0x9d, 0x87, 0x44, 0x2b, 0x69, 0x93, 0xc0, 0x53, 0x2c, 0x0b, 0x29, 0x9c, 0x5a, 0x68, 0xc0, 0x38, - 0x28, 0x90, 0x45, 0xa0, 0x74, 0x67, 0x86, 0x35, 0xc9, 0x53, 0x78, 0x27, 0xdb, 0x41, 0xa7, 0xb6, - 0x43, 0x81, 0x4c, 0xd9, 0xda, 0xad, 0x6b, 0xdb, 0xa1, 0xc8, 0x9f, 0xd0, 0xa1, 0x43, 0xd0, 0x29, - 0x63, 0xd1, 0x02, 0x4e, 0x91, 0x0c, 0xdd, 0xbd, 0x74, 0x2d, 0x8e, 0x3c, 0x9e, 0x28, 0x9b, 0xb4, - 0x4c, 0xc5, 0x1d, 0xba, 0xd8, 0xbc, 0xbb, 0xef, 0xf1, 0xfb, 0xbe, 0xfb, 0x5e, 0x27, 0xf0, 0x3e, - 0xa1, 0x0e, 0xa1, 0x16, 0xd5, 0x68, 0xb7, 0x83, 0xbd, 0x55, 0xbb, 0x6b, 0x21, 0x8d, 0x6d, 0x56, - 0x3a, 0x1e, 0x61, 0x44, 0x55, 0xc5, 0x61, 0xa5, 0x77, 0x98, 0x3f, 0x6e, 0x12, 0x93, 0xf8, 0xc7, - 0x1a, 0xff, 0x0a, 0x28, 0xf3, 0x33, 0x86, 0x63, 0xb9, 0x44, 0xf3, 0xff, 0x8a, 0xad, 0x82, 0x49, - 0x88, 0x69, 0x63, 0xcd, 0x5f, 0xb5, 0xba, 0xab, 0x1a, 0xea, 0x7a, 0x06, 0xb3, 0x88, 0x1b, 0x9e, - 0xb7, 0x7d, 0xe9, 0x5a, 0xcb, 0xa0, 0x58, 0x5b, 0xaf, 0xb6, 0x30, 0x33, 0xaa, 0x5a, 0x9b, 0x58, - 0xe1, 0x79, 0x71, 0x27, 0x3f, 0xb3, 0x1c, 0x4c, 0x99, 0xe1, 0x74, 0x04, 0xc1, 0xf9, 0x18, 0xe8, - 0xbd, 0xcf, 0x80, 0x08, 0x7e, 0xa7, 0x80, 0x13, 0x0d, 0x6a, 0x3e, 0x90, 0xfb, 0x75, 0x6c, 0x63, - 0xd3, 0x60, 0x58, 0xbd, 0x0c, 0xc6, 0x28, 0x76, 0x11, 0xf6, 0x72, 0x4a, 0x49, 0x99, 0x3d, 0x52, - 0x9b, 0xd9, 0xde, 0x2a, 0x4e, 0x3e, 0x35, 0x1c, 0x7b, 0x1e, 0x06, 0xfb, 0x50, 0x17, 0x04, 0xea, - 0x29, 0x30, 0x6e, 0x93, 0xf6, 0x5a, 0xd3, 0x42, 0xb9, 0x4c, 0x49, 0x99, 0x1d, 0xd5, 0xc7, 0xf8, - 0x72, 0x09, 0xa9, 0xa7, 0xc1, 0xe1, 0x75, 0xc3, 0x6e, 0x1a, 0x08, 0x79, 0xb9, 0x2c, 0x97, 0xa2, - 0x8f, 0xaf, 0x1b, 0xf6, 0x02, 0x42, 0xde, 0x7c, 0xe9, 0xd9, 0xdf, 0x2f, 0xae, 0xc4, 0x78, 0xb7, - 0x8c, 0x04, 0x00, 0x58, 0x04, 0x67, 0x63, 0x91, 0xe9, 0x98, 0x76, 0x88, 0x4b, 0x31, 0xfc, 0x52, - 0x01, 0xa7, 0xfa, 0x28, 0x1e, 0xba, 0xe8, 0x00, 0xd1, 0xcf, 0x43, 0x0e, 0xf1, 0x6c, 0x0c, 0xc4, - 0xae, 0xd4, 0x03, 0xcf, 0x81, 0x62, 0x02, 0x04, 0x09, 0xf3, 0xab, 0xdd, 0x30, 0x5b, 0xc4, 0x45, - 0xcb, 0xa4, 0xbd, 0x76, 0x20, 0x30, 0xcf, 0x73, 0x98, 0x85, 0x58, 0x98, 0x5c, 0x4f, 0x99, 0x93, - 0xc5, 0xe0, 0x0c, 0x31, 0x48, 0x9c, 0x3f, 0x2b, 0xe0, 0x42, 0x82, 0x2d, 0x0b, 0xee, 0x01, 0x83, - 0x56, 0x6b, 0x60, 0x94, 0xc7, 0xb2, 0x1f, 0x15, 0x13, 0xd7, 0x4f, 0x57, 0x82, 0x60, 0xaf, 0xf0, - 0x60, 0xaf, 0x88, 0x60, 0xaf, 0x2c, 0x12, 0xcb, 0xad, 0xbd, 0xf7, 0x72, 0xab, 0x38, 0xb2, 0xbd, - 0x55, 0x9c, 0x08, 0x14, 0x70, 0x26, 0xa8, 0xfb, 0xbc, 0xf0, 0x63, 0x70, 0x6d, 0x3f, 0x78, 0x43, - 0x03, 0xa3, 0x60, 0x94, 0x28, 0x18, 0xb8, 0xad, 0x80, 0x33, 0x0d, 0x6a, 0x72, 0xe2, 0x05, 0x17, - 0xbd, 0x5b, 0x2e, 0x18, 0xe0, 0x10, 0x07, 0x47, 0x73, 0x99, 0x52, 0x76, 0x6f, 0xcb, 0xe6, 0xb8, - 0x65, 0x3f, 0xbd, 0x2e, 0xce, 0x9a, 0x16, 0x7b, 0xdc, 0x6d, 0x55, 0xda, 0xc4, 0xd1, 0x44, 0xce, - 0x07, 0xff, 0xca, 0x14, 0xad, 0x69, 0xec, 0x69, 0x07, 0x53, 0x9f, 0x81, 0xea, 0x81, 0xe4, 0xbd, - 0xb2, 0xea, 0x32, 0x8f, 0x85, 0x0b, 0x61, 0x2c, 0x70, 0xf3, 0xca, 0x86, 0x8b, 0xca, 0x71, 0xe9, - 0x75, 0xd3, 0xbf, 0xed, 0x44, 0x9b, 0xa5, 0xd7, 0xa6, 0x40, 0x66, 0xa9, 0x2e, 0x1c, 0x96, 0x59, - 0xaa, 0xc3, 0x17, 0x19, 0xa0, 0x35, 0xa8, 0xb9, 0xe8, 0x61, 0x83, 0xe1, 0xbb, 0x5d, 0xdb, 0xd6, - 0x0d, 0xd7, 0xc4, 0xf7, 0x09, 0xb5, 0x78, 0xf1, 0xfa, 0x7f, 0xfb, 0x4f, 0xbd, 0x0a, 0xc6, 0x3b, - 0x84, 0xd8, 0x3c, 0x44, 0x46, 0xb9, 0xc5, 0x35, 0x75, 0x7b, 0xab, 0x38, 0x15, 0x20, 0x15, 0x07, - 0x50, 0x1f, 0xe3, 0x5f, 0x4b, 0x68, 0xfe, 0x03, 0xee, 0x6c, 0x18, 0x3a, 0x7b, 0xb5, 0x6b, 0xdb, - 0x65, 0x8f, 0xfb, 0x22, 0x70, 0xf9, 0x6a, 0xcf, 0xd5, 0x4f, 0xc0, 0xad, 0x94, 0x1e, 0x93, 0xde, - 0x3f, 0x09, 0x82, 0x20, 0xad, 0xf7, 0x85, 0x6c, 0x5d, 0x2d, 0x00, 0xd0, 0x11, 0x02, 0x96, 0xea, - 0x22, 0xb7, 0x22, 0x3b, 0xbc, 0xae, 0xe7, 0x1a, 0xd4, 0x7c, 0xe8, 0xde, 0x27, 0xc4, 0xfe, 0xec, - 0xb1, 0xc5, 0xb0, 0x6d, 0x51, 0x86, 0x11, 0x5f, 0xa6, 0xb9, 0x8e, 0x88, 0x43, 0x32, 0x03, 0x1d, - 0x72, 0x81, 0x3b, 0xa4, 0x18, 0x3a, 0xa4, 0xeb, 0xf2, 0xed, 0xf2, 0x46, 0x4f, 0x79, 0x99, 0x6f, - 0xc0, 0x7b, 0xa0, 0x94, 0x84, 0x4c, 0x9a, 0x7d, 0x09, 0x1c, 0xc3, 0x9b, 0x16, 0xc3, 0xa8, 0x29, - 0x32, 0x96, 0xe6, 0x94, 0x52, 0x76, 0x76, 0x54, 0x9f, 0x0c, 0xb6, 0x97, 0xfd, 0xc4, 0xa5, 0xf0, - 0xc7, 0x2c, 0xb8, 0xed, 0x0b, 0xb3, 0x83, 0x38, 0x6e, 0x58, 0xa6, 0x67, 0x30, 0xfc, 0xe0, 0xb1, - 0xe1, 0x61, 0xba, 0x42, 0xa4, 0xb3, 0x17, 0x89, 0xdb, 0xc6, 0x2e, 0xe3, 0x67, 0x28, 0x74, 0x7c, - 0x4a, 0x37, 0x44, 0xeb, 0x58, 0x36, 0xea, 0x06, 0x71, 0x00, 0x65, 0x6d, 0x33, 0xc1, 0x0c, 0xf5, - 0x01, 0x34, 0x19, 0x69, 0x3a, 0x01, 0xa2, 0xc1, 0x85, 0xae, 0x24, 0x0a, 0x5d, 0x4e, 0x20, 0xd8, - 0x29, 0x01, 0xea, 0xc7, 0xa8, 0x30, 0x4b, 0x58, 0xa9, 0x3e, 0x53, 0xc0, 0x14, 0x23, 0x6b, 0xd8, - 0x6d, 0x92, 0x2e, 0x6b, 0x3a, 0x3c, 0x6b, 0x46, 0x07, 0x65, 0xcd, 0x92, 0x50, 0x73, 0x22, 0x50, - 0xd3, 0xcf, 0x0e, 0x53, 0xa5, 0xd3, 0x51, 0x9f, 0xf9, 0xd3, 0x2e, 0x6b, 0x58, 0x2e, 0x9d, 0x2f, - 0xf2, 0xcb, 0xcf, 0xf7, 0x2e, 0x5f, 0x16, 0x9f, 0x10, 0xff, 0x6f, 0x59, 0x70, 0x67, 0xd8, 0xbb, - 0x92, 0x81, 0xf1, 0x08, 0x8c, 0x1b, 0x0e, 0xe9, 0xba, 0x6c, 0x4e, 0x5c, 0xda, 0x1d, 0x6e, 0xcf, - 0x1f, 0x5b, 0xc5, 0x4b, 0xfb, 0x80, 0xbd, 0xe4, 0xb2, 0xde, 0xb5, 0x09, 0x31, 0x50, 0x0f, 0x05, - 0xf6, 0x64, 0x57, 0xfd, 0x4b, 0x7e, 0x67, 0xd9, 0x55, 0x29, 0xbb, 0xaa, 0x6e, 0x80, 0x19, 0xdb, - 0x7a, 0xd2, 0xb5, 0x90, 0xc5, 0x9e, 0x36, 0xdb, 0x7e, 0x25, 0x40, 0x41, 0xf1, 0xa9, 0xdd, 0x4b, - 0xa1, 0xa5, 0x8e, 0xdb, 0xbd, 0x10, 0xd9, 0x25, 0x10, 0xea, 0xd3, 0x72, 0x2f, 0xa8, 0x36, 0x48, - 0x7d, 0x08, 0x8e, 0x7c, 0x4e, 0x2c, 0xb7, 0xc9, 0xa7, 0x43, 0xbf, 0xa6, 0x4d, 0x5c, 0xcf, 0x57, - 0x82, 0xd1, 0xb1, 0x12, 0x8e, 0x8e, 0x95, 0x95, 0x70, 0x74, 0xac, 0x9d, 0x11, 0xe1, 0x31, 0x1d, - 0xa8, 0x90, 0xac, 0xf0, 0xf9, 0xeb, 0xa2, 0xa2, 0x1f, 0xe6, 0x6b, 0x4e, 0x0c, 0xbf, 0xce, 0xfa, - 0x5d, 0x60, 0x01, 0xa1, 0x15, 0x12, 0xbd, 0xb0, 0xe5, 0x50, 0x7f, 0xaf, 0xa6, 0xc9, 0x7c, 0xbb, - 0x05, 0x26, 0xc2, 0x0a, 0x25, 0x7b, 0x70, 0xed, 0xe4, 0xf6, 0x56, 0x51, 0x0d, 0xeb, 0x89, 0x3c, - 0x84, 0x91, 0x62, 0x86, 0x22, 0x89, 0x9a, 0x19, 0x94, 0xa8, 0xcd, 0x30, 0x23, 0x10, 0xa6, 0x96, - 0x87, 0xd1, 0xdc, 0xe0, 0xc4, 0x3b, 0x1b, 0x97, 0x11, 0x21, 0x3b, 0xd4, 0x27, 0xfd, 0x8d, 0xba, - 0x58, 0xef, 0x52, 0x50, 0x15, 0x4e, 0x1d, 0x52, 0x41, 0x75, 0x87, 0x82, 0xea, 0xfc, 0x15, 0x9e, - 0x47, 0x17, 0xc3, 0x3c, 0x32, 0x10, 0x2a, 0x33, 0x52, 0x6e, 0xdb, 0xd1, 0x1e, 0x1e, 0xba, 0x06, - 0xfe, 0x9a, 0xf5, 0x3b, 0x4b, 0x9a, 0x5b, 0x90, 0x99, 0x34, 0xf4, 0x6d, 0x44, 0x52, 0x30, 0xf3, - 0x1f, 0xa6, 0x60, 0xf6, 0xa0, 0x53, 0x70, 0x0d, 0x4c, 0xba, 0x78, 0xa3, 0x29, 0x33, 0x24, 0x77, - 0xc8, 0xd7, 0x70, 0x37, 0x75, 0xfa, 0x1d, 0x0f, 0x34, 0xf4, 0x09, 0x83, 0xfa, 0x51, 0x17, 0x6f, - 0x48, 0xbf, 0x47, 0x1b, 0xc6, 0xae, 0x41, 0x62, 0x67, 0xc3, 0x80, 0x3f, 0x64, 0x44, 0xb3, 0xe6, - 0x23, 0xeb, 0x22, 0x71, 0xd7, 0xb1, 0xc7, 0xf8, 0x58, 0xc0, 0x8c, 0x35, 0x1c, 0x95, 0xa4, 0x0c, - 0x92, 0x94, 0x26, 0x53, 0xf6, 0x98, 0x82, 0x3c, 0x30, 0xed, 0x58, 0x6e, 0xd3, 0x70, 0x18, 0xef, - 0x3f, 0x94, 0xc3, 0xf0, 0xad, 0x38, 0x12, 0x74, 0x8f, 0x54, 0xd7, 0x71, 0x2a, 0xd0, 0xbe, 0x53, - 0x1e, 0xd4, 0x27, 0x1d, 0xcb, 0x5d, 0x70, 0xd8, 0x0a, 0xf1, 0xcd, 0x9c, 0xbf, 0xc8, 0xc3, 0xbe, - 0xd4, 0x6b, 0x1f, 0xfe, 0xd3, 0xa5, 0x1d, 0xf8, 0x22, 0x18, 0xa8, 0x7c, 0xb6, 0x6f, 0x15, 0x31, - 0x3d, 0xc4, 0xb8, 0x4a, 0x86, 0x36, 0x05, 0xd3, 0x8c, 0x30, 0x6e, 0x9c, 0xc3, 0x02, 0x75, 0x48, - 0x74, 0x8b, 0xa1, 0xf1, 0xef, 0x94, 0x07, 0xf5, 0x29, 0x7f, 0x6b, 0xc1, 0x61, 0xbe, 0x6e, 0x74, - 0xfd, 0x9f, 0x09, 0x90, 0x6d, 0x50, 0x53, 0xf5, 0x80, 0x1a, 0x37, 0x01, 0x57, 0x76, 0xff, 0x56, - 0x50, 0x89, 0x7d, 0xde, 0xe6, 0xab, 0xfb, 0x26, 0x95, 0x06, 0x6f, 0x82, 0xe3, 0xb1, 0xaf, 0xe0, - 0xab, 0x03, 0x45, 0xf5, 0x88, 0xf3, 0x37, 0x52, 0x10, 0x27, 0x69, 0x96, 0x6f, 0xc4, 0xfd, 0x68, - 0x0e, 0x89, 0xf7, 0xa5, 0x79, 0xd7, 0x6b, 0xee, 0x7b, 0x05, 0x9c, 0x1b, 0xfc, 0x56, 0xbd, 0x9d, - 0xc2, 0xa8, 0x3e, 0xce, 0xfc, 0x9d, 0x61, 0x39, 0x25, 0xc2, 0x6f, 0x14, 0x70, 0x3a, 0xf9, 0x4d, - 0x39, 0x97, 0x20, 0x3f, 0x91, 0x23, 0x7f, 0x3b, 0x2d, 0x87, 0x44, 0xf2, 0x8b, 0x02, 0xae, 0xa5, - 0x7a, 0xb0, 0x2d, 0x26, 0xa8, 0x4a, 0x23, 0x24, 0xff, 0xc9, 0x01, 0x08, 0x91, 0x26, 0x7c, 0x01, - 0x4e, 0xc4, 0x3f, 0x66, 0xae, 0x25, 0x68, 0x89, 0xa5, 0xce, 0x7f, 0x98, 0x86, 0x5a, 0x2a, 0xff, - 0x53, 0x01, 0x1f, 0x0d, 0xf7, 0xc6, 0x58, 0x4e, 0xd4, 0x37, 0x84, 0xb4, 0xfc, 0xca, 0x41, 0x4a, - 0xeb, 0x8b, 0x8e, 0x54, 0x83, 0x5c, 0x52, 0x74, 0xa4, 0x11, 0x92, 0x18, 0x1d, 0x43, 0x0d, 0x33, - 0x7e, 0x74, 0xc4, 0x75, 0xcf, 0xe4, 0xe8, 0x88, 0xa1, 0xde, 0x23, 0x3a, 0xf6, 0x68, 0x37, 0xb5, - 0xfb, 0x2f, 0xdf, 0x14, 0x94, 0x57, 0x6f, 0x0a, 0xca, 0x5f, 0x6f, 0x0a, 0xca, 0xf3, 0xb7, 0x85, - 0x91, 0x57, 0x6f, 0x0b, 0x23, 0xbf, 0xbf, 0x2d, 0x8c, 0x3c, 0xba, 0x19, 0x69, 0x33, 0x42, 0x72, - 0xd9, 0x36, 0x5a, 0x34, 0x5c, 0x68, 0xeb, 0xd5, 0x5b, 0xda, 0x66, 0xdf, 0x6f, 0xcb, 0xbc, 0xf5, - 0xb4, 0xc6, 0xfc, 0xc9, 0xfc, 0xc6, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x76, 0x62, 0x15, - 0x7e, 0x16, 0x00, 0x00, + // 1510 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xcd, 0x6f, 0x13, 0x47, + 0x1b, 0xcf, 0xda, 0x21, 0x81, 0x09, 0x09, 0xc9, 0xbe, 0x7c, 0x18, 0xbf, 0x60, 0x9b, 0xe1, 0x2b, + 0x7c, 0xd8, 0x1b, 0xc3, 0xfb, 0x02, 0xca, 0x89, 0x38, 0x16, 0xaf, 0x02, 0xb1, 0x5e, 0xb4, 0x04, + 0x55, 0xe2, 0x62, 0xad, 0x3d, 0x93, 0x65, 0x9b, 0xdd, 0x1d, 0xb3, 0x33, 0x4e, 0x82, 0x7a, 0x6a, + 0x7b, 0xa8, 0xc4, 0x09, 0xf5, 0xd2, 0xde, 0x7a, 0x6e, 0x0f, 0x15, 0xff, 0x40, 0xa5, 0x1e, 0x7a, + 0x40, 0x3d, 0x71, 0xac, 0x5a, 0x29, 0x54, 0x70, 0xe8, 0x3d, 0x97, 0x5e, 0xab, 0xd9, 0x9d, 0x1d, + 0xaf, 0x93, 0xdd, 0x38, 0x6b, 0xd2, 0x43, 0x2f, 0xe0, 0x9d, 0x79, 0xe6, 0x79, 0x7e, 0xcf, 0xc7, + 0xef, 0x79, 0x66, 0x02, 0xfe, 0x4d, 0xa8, 0x43, 0xa8, 0x45, 0x35, 0xda, 0xed, 0x60, 0x6f, 0xd5, + 0xee, 0x5a, 0x48, 0x63, 0x9b, 0x95, 0x8e, 0x47, 0x18, 0x51, 0x55, 0xb1, 0x59, 0xe9, 0x6d, 0xe6, + 0x8f, 0x9b, 0xc4, 0x24, 0xfe, 0xb6, 0xc6, 0x7f, 0x05, 0x92, 0xf9, 0x19, 0xc3, 0xb1, 0x5c, 0xa2, + 0xf9, 0xff, 0x8a, 0xa5, 0x82, 0x49, 0x88, 0x69, 0x63, 0xcd, 0xff, 0x6a, 0x75, 0x57, 0x35, 0xd4, + 0xf5, 0x0c, 0x66, 0x11, 0x37, 0xdc, 0x6f, 0xfb, 0xda, 0xb5, 0x96, 0x41, 0xb1, 0xb6, 0x5e, 0x6d, + 0x61, 0x66, 0x54, 0xb5, 0x36, 0xb1, 0xc2, 0xfd, 0xe2, 0xce, 0xf3, 0xcc, 0x72, 0x30, 0x65, 0x86, + 0xd3, 0x11, 0x02, 0xe7, 0x63, 0xa0, 0xf7, 0x7e, 0x06, 0x42, 0xf0, 0x6b, 0x05, 0x9c, 0x68, 0x50, + 0xf3, 0x91, 0x5c, 0xaf, 0x63, 0x1b, 0x9b, 0x06, 0xc3, 0xea, 0x15, 0x30, 0x46, 0xb1, 0x8b, 0xb0, + 0x97, 0x53, 0x4a, 0xca, 0xec, 0x91, 0xda, 0xcc, 0xf6, 0x56, 0x71, 0xf2, 0xb9, 0xe1, 0xd8, 0xf3, + 0x30, 0x58, 0x87, 0xba, 0x10, 0x50, 0x4f, 0x81, 0x71, 0x9b, 0xb4, 0xd7, 0x9a, 0x16, 0xca, 0x65, + 0x4a, 0xca, 0xec, 0xa8, 0x3e, 0xc6, 0x3f, 0x97, 0x90, 0x7a, 0x1a, 0x1c, 0x5e, 0x37, 0xec, 0xa6, + 0x81, 0x90, 0x97, 0xcb, 0x72, 0x2d, 0xfa, 0xf8, 0xba, 0x61, 0x2f, 0x20, 0xe4, 0xcd, 0x97, 0x5e, + 0xfc, 0xf1, 0xea, 0x6a, 0x4c, 0x74, 0xcb, 0x48, 0x00, 0x80, 0x45, 0x70, 0x36, 0x16, 0x99, 0x8e, + 0x69, 0x87, 0xb8, 0x14, 0xc3, 0x4f, 0x15, 0x70, 0xaa, 0x4f, 0xe2, 0xb1, 0x8b, 0x0e, 0x10, 0xfd, + 0x3c, 0xe4, 0x10, 0xcf, 0xc6, 0x40, 0xec, 0x4a, 0x3b, 0xf0, 0x1c, 0x28, 0x26, 0x40, 0x90, 0x30, + 0x3f, 0xdb, 0x0d, 0xb3, 0x45, 0x5c, 0xb4, 0x4c, 0xda, 0x6b, 0x07, 0x02, 0xf3, 0x3c, 0x87, 0x59, + 0x88, 0x85, 0xc9, 0xed, 0x94, 0xb9, 0x58, 0x0c, 0xce, 0x10, 0x83, 0xc4, 0xf9, 0xbd, 0x02, 0x2e, + 0x24, 0xf8, 0xb2, 0xe0, 0x1e, 0x30, 0x68, 0xb5, 0x06, 0x46, 0x79, 0x2d, 0xfb, 0x55, 0x31, 0x71, + 0xe3, 0x74, 0x25, 0x28, 0xf6, 0x0a, 0x2f, 0xf6, 0x8a, 0x28, 0xf6, 0xca, 0x22, 0xb1, 0xdc, 0xda, + 0xbf, 0x5e, 0x6f, 0x15, 0x47, 0xb6, 0xb7, 0x8a, 0x13, 0x81, 0x01, 0x7e, 0x08, 0xea, 0xfe, 0x59, + 0xf8, 0x3f, 0x70, 0x7d, 0x3f, 0x78, 0x43, 0x07, 0xa3, 0x60, 0x94, 0x28, 0x18, 0xb8, 0xad, 0x80, + 0x33, 0x0d, 0x6a, 0x72, 0xe1, 0x05, 0x17, 0x7d, 0x18, 0x17, 0x0c, 0x70, 0x88, 0x83, 0xa3, 0xb9, + 0x4c, 0x29, 0xbb, 0xb7, 0x67, 0x73, 0xdc, 0xb3, 0xef, 0xde, 0x16, 0x67, 0x4d, 0x8b, 0x3d, 0xed, + 0xb6, 0x2a, 0x6d, 0xe2, 0x68, 0x82, 0xf3, 0xc1, 0x7f, 0x65, 0x8a, 0xd6, 0x34, 0xf6, 0xbc, 0x83, + 0xa9, 0x7f, 0x80, 0xea, 0x81, 0xe6, 0xbd, 0x58, 0x75, 0x85, 0xd7, 0xc2, 0x85, 0xb0, 0x16, 0xb8, + 0x7b, 0x65, 0xc3, 0x45, 0xe5, 0x38, 0x7a, 0xdd, 0xf2, 0xb3, 0x9d, 0xe8, 0xb3, 0x8c, 0xda, 0x14, + 0xc8, 0x2c, 0xd5, 0x45, 0xc0, 0x32, 0x4b, 0x75, 0xf8, 0x2a, 0x03, 0xb4, 0x06, 0x35, 0x17, 0x3d, + 0x6c, 0x30, 0x7c, 0xaf, 0x6b, 0xdb, 0xba, 0xe1, 0x9a, 0xf8, 0x21, 0xa1, 0x16, 0x6f, 0x5e, 0xff, + 0xec, 0xf8, 0xa9, 0xd7, 0xc0, 0x78, 0x87, 0x10, 0x9b, 0x97, 0xc8, 0x28, 0xf7, 0xb8, 0xa6, 0x6e, + 0x6f, 0x15, 0xa7, 0x02, 0xa4, 0x62, 0x03, 0xea, 0x63, 0xfc, 0xd7, 0x12, 0x9a, 0xbf, 0xcc, 0x83, + 0x0d, 0xc3, 0x60, 0xaf, 0x76, 0x6d, 0xbb, 0xec, 0xf1, 0x58, 0x04, 0x21, 0x5f, 0xed, 0x85, 0xfa, + 0x19, 0xb8, 0x9d, 0x32, 0x62, 0x32, 0xfa, 0x27, 0x41, 0x50, 0xa4, 0xf5, 0xbe, 0x92, 0xad, 0xab, + 0x05, 0x00, 0x3a, 0x42, 0xc1, 0x52, 0x5d, 0x70, 0x2b, 0xb2, 0xc2, 0xfb, 0x7a, 0xae, 0x41, 0xcd, + 0xc7, 0xee, 0x43, 0x42, 0xec, 0x8f, 0x9e, 0x5a, 0x0c, 0xdb, 0x16, 0x65, 0x18, 0xf1, 0xcf, 0x34, + 0xe9, 0x88, 0x04, 0x24, 0x33, 0x30, 0x20, 0x17, 0x78, 0x40, 0x8a, 0x61, 0x40, 0xba, 0x2e, 0x5f, + 0x2e, 0x6f, 0xf4, 0x8c, 0x97, 0xf9, 0x02, 0xbc, 0x0f, 0x4a, 0x49, 0xc8, 0xa4, 0xdb, 0x97, 0xc0, + 0x31, 0xbc, 0x69, 0x31, 0x8c, 0x9a, 0x82, 0xb1, 0x34, 0xa7, 0x94, 0xb2, 0xb3, 0xa3, 0xfa, 0x64, + 0xb0, 0xbc, 0xec, 0x13, 0x97, 0xc2, 0x6f, 0xb3, 0xe0, 0x8e, 0xaf, 0xcc, 0x0e, 0xea, 0xb8, 0x61, + 0x99, 0x9e, 0xc1, 0xf0, 0xa3, 0xa7, 0x86, 0x87, 0xe9, 0x0a, 0x91, 0xc1, 0x5e, 0x24, 0x6e, 0x1b, + 0xbb, 0x8c, 0xef, 0xa1, 0x30, 0xf0, 0x29, 0xc3, 0x10, 0xed, 0x63, 0xd9, 0x68, 0x18, 0xc4, 0x06, + 0x94, 0xbd, 0xcd, 0x04, 0x33, 0xd4, 0x07, 0xd0, 0x64, 0xa4, 0xe9, 0x04, 0x88, 0x06, 0x37, 0xba, + 0x92, 0x68, 0x74, 0x39, 0x81, 0x60, 0xa7, 0x06, 0xa8, 0x1f, 0xa3, 0xc2, 0x2d, 0xe1, 0xa5, 0xfa, + 0x42, 0x01, 0x53, 0x8c, 0xac, 0x61, 0xb7, 0x49, 0xba, 0xac, 0xe9, 0x70, 0xd6, 0x8c, 0x0e, 0x62, + 0xcd, 0x92, 0x30, 0x73, 0x22, 0x30, 0xd3, 0x7f, 0x1c, 0xa6, 0xa2, 0xd3, 0x51, 0xff, 0xf0, 0xff, + 0xbb, 0xac, 0x61, 0xb9, 0x74, 0xbe, 0xc8, 0x93, 0x9f, 0xef, 0x25, 0x5f, 0x36, 0x9f, 0x10, 0xff, + 0xcf, 0x59, 0x70, 0x77, 0xd8, 0x5c, 0xc9, 0xc2, 0x78, 0x02, 0xc6, 0x0d, 0x87, 0x74, 0x5d, 0x36, + 0x27, 0x92, 0x76, 0x97, 0xfb, 0xf3, 0xeb, 0x56, 0xf1, 0xd2, 0x3e, 0x60, 0x2f, 0xb9, 0xac, 0x97, + 0x36, 0xa1, 0x06, 0xea, 0xa1, 0xc2, 0x9e, 0xee, 0xaa, 0x9f, 0xe4, 0x0f, 0xd6, 0x5d, 0x95, 0xba, + 0xab, 0xea, 0x06, 0x98, 0xb1, 0xad, 0x67, 0x5d, 0x0b, 0x59, 0xec, 0x79, 0xb3, 0xed, 0x77, 0x02, + 0x14, 0x34, 0x9f, 0xda, 0xfd, 0x14, 0x56, 0xea, 0xb8, 0xdd, 0x2b, 0x91, 0x5d, 0x0a, 0xa1, 0x3e, + 0x2d, 0xd7, 0x82, 0x6e, 0x83, 0xd4, 0xc7, 0xe0, 0xc8, 0xc7, 0xc4, 0x72, 0x9b, 0xfc, 0x76, 0xe8, + 0xf7, 0xb4, 0x89, 0x1b, 0xf9, 0x4a, 0x70, 0x75, 0xac, 0x84, 0x57, 0xc7, 0xca, 0x4a, 0x78, 0x75, + 0xac, 0x9d, 0x11, 0xe5, 0x31, 0x1d, 0x98, 0x90, 0x47, 0xe1, 0xcb, 0xb7, 0x45, 0x45, 0x3f, 0xcc, + 0xbf, 0xb9, 0x30, 0xfc, 0x3c, 0xeb, 0x4f, 0x81, 0x05, 0x84, 0x56, 0x48, 0x34, 0x61, 0xcb, 0xa1, + 0xfd, 0x5e, 0x4f, 0x93, 0x7c, 0xbb, 0x0d, 0x26, 0xc2, 0x0e, 0x25, 0x67, 0x70, 0xed, 0xe4, 0xf6, + 0x56, 0x51, 0x0d, 0xfb, 0x89, 0xdc, 0x84, 0x91, 0x66, 0x86, 0x22, 0x44, 0xcd, 0x0c, 0x22, 0x6a, + 0x33, 0x64, 0x04, 0xc2, 0xd4, 0xf2, 0x30, 0x9a, 0x1b, 0x4c, 0xbc, 0xb3, 0x71, 0x8c, 0x08, 0x8f, + 0x43, 0x7d, 0xd2, 0x5f, 0xa8, 0x8b, 0xef, 0x5d, 0x06, 0xaa, 0x22, 0xa8, 0x43, 0x1a, 0xa8, 0xee, + 0x30, 0x50, 0x9d, 0xbf, 0xca, 0x79, 0x74, 0x31, 0xe4, 0x91, 0x81, 0x50, 0x99, 0x91, 0x72, 0xdb, + 0x8e, 0xce, 0xf0, 0x30, 0x34, 0xf0, 0xa7, 0xac, 0x3f, 0x59, 0xd2, 0x64, 0x41, 0x32, 0x69, 0xe8, + 0x6c, 0x44, 0x28, 0x98, 0xf9, 0x1b, 0x29, 0x98, 0x3d, 0x68, 0x0a, 0xae, 0x81, 0x49, 0x17, 0x6f, + 0x34, 0x25, 0x43, 0x72, 0x87, 0x7c, 0x0b, 0xf7, 0x52, 0xd3, 0xef, 0x78, 0x60, 0xa1, 0x4f, 0x19, + 0xd4, 0x8f, 0xba, 0x78, 0x43, 0xc6, 0x3d, 0x3a, 0x30, 0x76, 0x5d, 0x24, 0x76, 0x0e, 0x0c, 0xf8, + 0x43, 0x56, 0x0c, 0x6b, 0x7e, 0x65, 0x5d, 0x24, 0xee, 0x3a, 0xf6, 0x18, 0xbf, 0x16, 0x30, 0x63, + 0x0d, 0x47, 0x35, 0x29, 0x83, 0x34, 0xa5, 0x61, 0xca, 0x1e, 0xb7, 0x20, 0x0f, 0x4c, 0x3b, 0x96, + 0xdb, 0x34, 0x1c, 0xc6, 0xe7, 0x0f, 0xe5, 0x30, 0x7c, 0x2f, 0x8e, 0x04, 0xd3, 0x23, 0x55, 0x3a, + 0x4e, 0x05, 0xd6, 0x77, 0xea, 0x83, 0xfa, 0xa4, 0x63, 0xb9, 0x0b, 0x0e, 0x5b, 0x21, 0x81, 0x9b, + 0x5f, 0x2a, 0xd1, 0xa9, 0xd9, 0x0e, 0x82, 0xe0, 0xa7, 0x68, 0x4f, 0x6e, 0x3d, 0x48, 0x9a, 0x9a, + 0x42, 0x03, 0x9f, 0x68, 0x97, 0xf7, 0x39, 0xd1, 0x7a, 0x03, 0x56, 0xe4, 0x60, 0xfe, 0x22, 0xe7, + 0x62, 0xa9, 0x37, 0xd3, 0xfc, 0xf7, 0x94, 0xd0, 0x1c, 0xdc, 0xf2, 0x7c, 0x5f, 0xbe, 0x52, 0xc4, + 0x95, 0x26, 0x26, 0x7f, 0x92, 0x6f, 0x14, 0x4c, 0x33, 0xc2, 0x78, 0xc4, 0x1d, 0x16, 0xc4, 0x00, + 0x89, 0x11, 0x36, 0x74, 0x50, 0x77, 0xea, 0x83, 0xfa, 0x94, 0xbf, 0xb4, 0xe0, 0x30, 0xdf, 0x36, + 0xba, 0xf1, 0xe7, 0x04, 0xc8, 0x36, 0xa8, 0xa9, 0x7a, 0x40, 0x8d, 0xbb, 0x96, 0x57, 0x76, 0xff, + 0x01, 0xa3, 0x12, 0xfb, 0xe6, 0xce, 0x57, 0xf7, 0x2d, 0x2a, 0x1d, 0xde, 0x04, 0xc7, 0x63, 0x9f, + 0xe6, 0xd7, 0x06, 0xaa, 0xea, 0x09, 0xe7, 0x6f, 0xa6, 0x10, 0x4e, 0xb2, 0x2c, 0x1f, 0xae, 0xfb, + 0xb1, 0x1c, 0x0a, 0xef, 0xcb, 0xf2, 0xae, 0x27, 0xe6, 0x37, 0x0a, 0x38, 0x37, 0xf8, 0x01, 0x7d, + 0x27, 0x85, 0x53, 0x7d, 0x27, 0xf3, 0x77, 0x87, 0x3d, 0x29, 0x11, 0x7e, 0xa1, 0x80, 0xd3, 0xc9, + 0x0f, 0xdd, 0xb9, 0x04, 0xfd, 0x89, 0x27, 0xf2, 0x77, 0xd2, 0x9e, 0x90, 0x48, 0x7e, 0x54, 0xc0, + 0xf5, 0x54, 0xaf, 0xc8, 0xc5, 0x04, 0x53, 0x69, 0x94, 0xe4, 0x1f, 0x1c, 0x80, 0x12, 0xe9, 0xc2, + 0x27, 0xe0, 0x44, 0xfc, 0x0b, 0xeb, 0x7a, 0x82, 0x95, 0x58, 0xe9, 0xfc, 0x7f, 0xd2, 0x48, 0x4b, + 0xe3, 0xbf, 0x29, 0xe0, 0xbf, 0xc3, 0x3d, 0x7c, 0x96, 0x13, 0xed, 0x0d, 0xa1, 0x2d, 0xbf, 0x72, + 0x90, 0xda, 0xfa, 0xaa, 0x23, 0xd5, 0xed, 0x32, 0xa9, 0x3a, 0xd2, 0x28, 0x49, 0xac, 0x8e, 0xa1, + 0x6e, 0x58, 0x7e, 0x75, 0xc4, 0x8d, 0xf4, 0xe4, 0xea, 0x88, 0x91, 0xde, 0xa3, 0x3a, 0xf6, 0x18, + 0x37, 0xb5, 0x87, 0xaf, 0xdf, 0x15, 0x94, 0x37, 0xef, 0x0a, 0xca, 0xef, 0xef, 0x0a, 0xca, 0xcb, + 0xf7, 0x85, 0x91, 0x37, 0xef, 0x0b, 0x23, 0xbf, 0xbc, 0x2f, 0x8c, 0x3c, 0xb9, 0x15, 0x19, 0x33, + 0x42, 0x73, 0xd9, 0x36, 0x5a, 0x34, 0xfc, 0xd0, 0xd6, 0xab, 0xb7, 0xb5, 0xcd, 0xbe, 0x3f, 0x78, + 0xf3, 0xd1, 0xd3, 0x1a, 0xf3, 0x9f, 0x0b, 0x37, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff, 0xca, 0x88, + 0xcf, 0x73, 0x13, 0x17, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2424,6 +2437,16 @@ func (m *MsgUnbondConvertAndStake) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + { + size, err := m.SharesToConvert.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size := m.MinAmtToStake.Size() i -= size @@ -2818,6 +2841,8 @@ func (m *MsgUnbondConvertAndStake) Size() (n int) { } l = m.MinAmtToStake.Size() n += 1 + l + sovTx(uint64(l)) + l = m.SharesToConvert.Size() + n += 1 + l + sovTx(uint64(l)) return n } @@ -5081,6 +5106,39 @@ func (m *MsgUnbondConvertAndStake) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SharesToConvert", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.SharesToConvert.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) From 69f6b398e3c57d36e71c9e79803018fb3bfbbf10 Mon Sep 17 00:00:00 2001 From: mattverse Date: Wed, 9 Aug 2023 17:46:14 +0900 Subject: [PATCH 16/27] Try abstracting check logics from test --- x/superfluid/keeper/stake.go | 6 +-- x/superfluid/keeper/stake_test.go | 72 +++++++++++++++---------------- 2 files changed, 35 insertions(+), 43 deletions(-) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 5f4607cea70..2195e8c2428 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -837,7 +837,6 @@ func (k Keeper) convertGammSharesToOsmoAndStake( // if given valAddr is empty, we use delegation preference given from valset-pref module or reference from superfluid staking if valAddr == "" { err := k.vspk.DelegateToValidatorSet(ctx, sender.String(), sdk.NewCoin(bondDenom, totalAmtToStake)) - // if valset-pref delegation errored due to no existing delegation existing, fall back and try using superfluid staked validator if err == valsettypes.ErrNoDelegation { val, err := k.validateValAddrForDelegate(ctx, originalSuperfluidValAddr) @@ -850,10 +849,7 @@ func (k Keeper) convertGammSharesToOsmoAndStake( if err != nil { return sdk.ZeroInt(), err } - } - - // for other errors, handle error - if err != nil { + } else if err != nil { // for other errors, handle error return sdk.ZeroInt(), err } } else { diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index f344902ea6e..e72a063b81e 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1087,35 +1087,19 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { s.Require().NoError(err) // Staking & Delegation check - // check if original superfluid staked lock's delgation is successfully deleted - _, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, originalValAddr) - s.Require().False(found) - // check if delegation amount matches - delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) - s.Require().True(found) - s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) + s.delegationCheck(s.Ctx, sender, originalValAddr, valAddr, totalAmtConverted) // Bank check balanceAfterConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender).FilterDenoms([]string{"foo", "stake", "uosmo"}) s.Require().True(balanceBeforeConvertLockToStake.IsEqual(balanceAfterConvertLockToStake)) - // Superfluid check - // The synthetic lockup should be deleted. + // if unlocked, no need to check locks since there is no lock existing if tc.unlocked { return } - _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.StakingSyntheticDenom(lock.Coins[0].Denom, valAddr.String())) - s.Require().Error(err) - // intermediary account should have been deleted in all case - if lock.ID != 0 { - _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.UnstakingSyntheticDenom(lock.Coins[0].Denom, valAddr.String())) - s.Require().Error(err) - } - - // Lock check - _, err = s.App.LockupKeeper.GetLockByID(s.Ctx, lock.ID) - s.Require().Error(err) + // lock check + s.lockCheck(s.Ctx, *lock, valAddr.String(), true) }) } @@ -1216,22 +1200,10 @@ func (s *KeeperTestSuite) TestConvertLockToStake() { s.Require().NoError(err) // Staking & Delegation check - // check if old delegation is succesfully deleted - _, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, originalValAddr) - s.Require().False(found) - // check if delegation amount matches - delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) - s.Require().True(found) - s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) - - // Superfluid check - // The synthetic lockup should be deleted. - _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.StakingSyntheticDenom(lock.Coins[0].Denom, valAddr.String())) - s.Require().Error(err) + s.delegationCheck(s.Ctx, sender, originalValAddr, valAddr, totalAmtConverted) // Lock check - _, err = s.App.LockupKeeper.GetLockByID(s.Ctx, lock.ID) - s.Require().Error(err) + s.lockCheck(s.Ctx, *lock, valAddr.String(), true) // Bank check balanceAfterConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) @@ -1315,10 +1287,7 @@ func (s *KeeperTestSuite) TestConvertUnlockedToStake() { s.Require().True(bondDenomPoolAmtAfterConvert.LT(bondDenomPoolAmtBeforeConvert)) // Staking & Delegation check - // check if delegation amount matches - delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, valAddr) - s.Require().True(found) - s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) + s.delegationCheck(s.Ctx, sender, sdk.ValAddress{}, valAddr, totalAmtConverted) // Bank check balanceAfterConvertLockToStake := s.App.BankKeeper.GetBalance(s.Ctx, sender, shareOut.Denom) @@ -1575,6 +1544,33 @@ func (s *KeeperTestSuite) SetupUnbondConvertAndStakeTest(ctx sdk.Context, superf s.Require().NoError(err) return joinPoolAmt, balancerIntermediaryAcc, balancerLock, poolCreateAcc, poolJoinAcc, balancerPooId, balancerPoolShareOut, valAddr + +} + +func (s *KeeperTestSuite) delegationCheck(ctx sdk.Context, sender sdk.AccAddress, originalValAddr, newValAddr sdk.ValAddress, totalAmtConverted sdk.Int) { + if !originalValAddr.Empty() { + // check if original superfluid staked lock's delgation is successfully deleted + _, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, originalValAddr) + s.Require().False(found) + } + // check if delegation amount matches + delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, newValAddr) + s.Require().True(found) + s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) +} + +func (s *KeeperTestSuite) lockCheck(ctx sdk.Context, lock lockuptypes.PeriodLock, valAddr string, checkUnstakingSynthLock bool) { + // The synthetic lockup should be deleted. + _, err := s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.StakingSyntheticDenom(lock.Coins[0].Denom, valAddr)) + s.Require().Error(err) + + // intermediary account should have been deleted + _, err = s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.UnstakingSyntheticDenom(lock.Coins[0].Denom, valAddr)) + s.Require().Error(err) + + // Lock check + _, err = s.App.LockupKeeper.GetLockByID(s.Ctx, lock.ID) + s.Require().Error(err) } // type superfluidRedelegation struct { From 96f486b5e1bbf468406fc4b5d1aa3e17ef6b4003 Mon Sep 17 00:00:00 2001 From: mattverse Date: Thu, 10 Aug 2023 17:05:28 +0900 Subject: [PATCH 17/27] Romans comment --- x/superfluid/keeper/export_test.go | 8 +- x/superfluid/keeper/migrate.go | 6 +- x/superfluid/keeper/migrate_test.go | 16 +-- x/superfluid/keeper/stake.go | 157 ++++++++++------------------ x/superfluid/keeper/stake_test.go | 104 ++++++++++++++++++ x/superfluid/types/errors.go | 7 -- x/superfluid/types/msg_test.go | 11 -- x/superfluid/types/msgs.go | 4 +- 8 files changed, 176 insertions(+), 137 deletions(-) diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 93cf98654d9..0b5201c56bf 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -37,8 +37,8 @@ func (k Keeper) ForceUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAd return k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins, exitCoinsLengthIsTwo) } -func (k Keeper) RouteMigration(ctx sdk.Context, sender sdk.AccAddress, lockId int64) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { - return k.routeMigration(ctx, sender, lockId) +func (k Keeper) GetMigrationType(ctx sdk.Context, sender sdk.AccAddress, lockId int64) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { + return k.getMigrationType(ctx, sender, lockId) } func (k Keeper) ValidateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (poolIdLeaving, poolIdEntering uint64, preMigrationLock *lockuptypes.PeriodLock, remainingLockTime time.Duration, err error) { @@ -78,3 +78,7 @@ func (k Keeper) ConvertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, v minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { return k.convertUnlockedToStake(ctx, sender, valAddr, sharesToStake, minAmtToStake) } + +func (k Keeper) DelegateBaseOnValsetPref(ctx sdk.Context, sender sdk.AccAddress, valAddr, originalSuperfluidValAddr string, totalAmtToStake sdk.Int) error { + return k.delegateBaseOnValsetPref(ctx, sender, valAddr, originalSuperfluidValAddr, totalAmtToStake) +} diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index 1b1c6d83ca7..140f38a3da6 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -47,7 +47,7 @@ const ( // // Errors if the lock is not found, if the lock is not a balancer pool lock, or if the lock is not owned by the sender. func (k Keeper) RouteLockedBalancerToConcentratedMigration(ctx sdk.Context, sender sdk.AccAddress, providedLockId int64, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, poolIdLeaving, poolIdEntering, concentratedLockId uint64, err error) { - synthLockBeforeMigration, migrationType, err := k.routeMigration(ctx, sender, providedLockId) + synthLockBeforeMigration, migrationType, err := k.getMigrationType(ctx, sender, providedLockId) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, 0, 0, err } @@ -215,9 +215,9 @@ func (k Keeper) migrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, return positionId, amount0, amount1, liquidity, concentratedLockId, poolIdLeaving, poolIdEntering, nil } -// routeMigration determines the status of the provided lock which is used to determine the method for migration. +// getMigrationType determines the status of the provided lock which is used to determine the method for migration. // It also returns the underlying synthetic locks of the provided lock, if any exist. -func (k Keeper) routeMigration(ctx sdk.Context, sender sdk.AccAddress, providedLockId int64) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { +func (k Keeper) getMigrationType(ctx sdk.Context, sender sdk.AccAddress, providedLockId int64) (synthLockBeforeMigration lockuptypes.SyntheticLock, migrationType MigrationType, err error) { // As a hack around to get frontend working, we decided to allow negative values for the provided lock ID to indicate that the user wants to migrate shares that are not locked. if providedLockId <= 0 { return lockuptypes.SyntheticLock{}, Unlocked, nil diff --git a/x/superfluid/keeper/migrate_test.go b/x/superfluid/keeper/migrate_test.go index bd9f3fdc4f2..109311ada6b 100644 --- a/x/superfluid/keeper/migrate_test.go +++ b/x/superfluid/keeper/migrate_test.go @@ -302,9 +302,9 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated() { coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // RouteMigration is called via the migration message router and is always run prior to the migration itself. + // GetMigrationType is called via the migration message router and is always run prior to the migration itself. // We use it here just to retrieve the synthetic lock before the migration. - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId)) + synthLockBeforeMigration, migrationType, err := superfluidKeeper.GetMigrationType(s.Ctx, poolJoinAcc, int64(originalGammLockId)) s.Require().NoError(err) s.Require().Equal(migrationType, keeper.SuperfluidBonded) @@ -458,8 +458,8 @@ func (s *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrated() coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // RouteMigration is called via the migration message router and is always run prior to the migration itself - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId)) + // GetMigrationType is called via the migration message router and is always run prior to the migration itself + synthLockBeforeMigration, migrationType, err := superfluidKeeper.GetMigrationType(s.Ctx, poolJoinAcc, int64(originalGammLockId)) s.Require().NoError(err) s.Require().Equal(migrationType, keeper.SuperfluidUnbonding) @@ -573,8 +573,8 @@ func (s *KeeperTestSuite) TestMigrateNonSuperfluidLockBalancerToConcentrated() { coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // RouteMigration is called via the migration message router and is always run prior to the migration itself - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, int64(originalGammLockId)) + // GetMigrationType is called via the migration message router and is always run prior to the migration itself + synthLockBeforeMigration, migrationType, err := superfluidKeeper.GetMigrationType(s.Ctx, poolJoinAcc, int64(originalGammLockId)) s.Require().NoError(err) s.Require().Equal((lockuptypes.SyntheticLock{}), synthLockBeforeMigration) s.Require().Equal(migrationType, keeper.NonSuperfluid) @@ -656,8 +656,8 @@ func (s *KeeperTestSuite) TestMigrateUnlockedPositionFromBalancerToConcentrated( coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // RouteMigration is called via the migration message router and is always run prior to the migration itself - synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(s.Ctx, poolJoinAcc, 0) + // GetMigrationType is called via the migration message router and is always run prior to the migration itself + synthLockBeforeMigration, migrationType, err := superfluidKeeper.GetMigrationType(s.Ctx, poolJoinAcc, 0) s.Require().NoError(err) s.Require().Equal((lockuptypes.SyntheticLock{}), synthLockBeforeMigration) s.Require().Equal(migrationType, keeper.Unlocked) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 2195e8c2428..59a70c714ae 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -311,63 +311,6 @@ func (k Keeper) SuperfluidUndelegateToConcentratedPosition(ctx sdk.Context, send return k.undelegateCommon(ctx, sender, gammLockID) } -// partialUndelegateCommon acts similarly to undelegateCommon, but undelegates a partial amount of the lock's delegation rather than the full amount. The amount -// that is undelegated is placed in a new lock. This function returns the intermediary account associated with the original lock ID as well as the new lock that was created. -// An error is returned if the amount to undelegate is greater than the locked amount. -// nolint: unused -func (k Keeper) partialUndelegateCommon(ctx sdk.Context, sender string, lockID uint64, amountToUndelegate sdk.Coin) (intermediaryAcc types.SuperfluidIntermediaryAccount, newlock *lockuptypes.PeriodLock, err error) { - lock, err := k.lk.GetLockByID(ctx, lockID) - if err != nil { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, err - } - err = k.validateLockForSF(ctx, lock, sender) - if err != nil { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, err - } - - if amountToUndelegate.Amount.GTE(lock.Coins[0].Amount) { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, fmt.Errorf("partial undelegate amount must be less than the locked amount") - } - - // get the intermediate account associated with lock id, and delete the connection. - intermediaryAcc, found := k.GetIntermediaryAccountFromLockId(ctx, lockID) - if !found { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, types.ErrNotSuperfluidUsedLockup - } - - // undelegate the desired lock amount, and burn the minted osmo. - amount, err := k.GetSuperfluidOSMOTokens(ctx, intermediaryAcc.Denom, amountToUndelegate.Amount) - if err != nil { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, err - } - err = k.forceUndelegateAndBurnOsmoTokens(ctx, amount, intermediaryAcc) - if err != nil { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, err - } - - // Move the funds from the old lock to a new lock with the remaining amount. - newLock, err := k.lk.SplitLock(ctx, *lock, sdk.NewCoins(amountToUndelegate), true) - if err != nil { - return types.SuperfluidIntermediaryAccount{}, &lockuptypes.PeriodLock{}, err - } - - return intermediaryAcc, &newLock, nil -} - -// partialSuperfluidUndelegate starts undelegating a portion of a superfluid delegated position for the given lock. -// Undelegation is done instantly and the equivalent amount is sent to the module account -// where it is burnt. Note that this method does not include unbonding the lock -// itself. -// nolint: unused -func (k Keeper) partialSuperfluidUndelegate(ctx sdk.Context, sender string, lockID uint64, amountToUndelegate sdk.Coin) error { - intermediaryAcc, newLock, err := k.partialUndelegateCommon(ctx, sender, lockID, amountToUndelegate) - if err != nil { - return err - } - // Create a new synthetic lockup representing the unstaking side. - return k.createSyntheticLockup(ctx, newLock.ID, intermediaryAcc, unlockingStatus) -} - // SuperfluidUnbondLock unbonds the lock that has been used for superfluid staking. // This method would return an error if the underlying lock is not superfluid undelegating. func (k Keeper) SuperfluidUnbondLock(ctx sdk.Context, underlyingLockId uint64, sender string) error { @@ -673,20 +616,20 @@ func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, va minAmtToStake sdk.Int, sharesToConvert sdk.Coin) (totalAmtConverted sdk.Int, err error) { senderAddr, err := sdk.AccAddressFromBech32(sender) if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } - // use routeMigration method to check status of lock (either superfluid staked, superfluid unbonding, vanilla locked, unlocked) - _, migrationType, err := k.routeMigration(ctx, senderAddr, int64(lockID)) + // use getMigrationType method to check status of lock (either superfluid staked, superfluid unbonding, vanilla locked, unlocked) + _, migrationType, err := k.getMigrationType(ctx, senderAddr, int64(lockID)) if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } // if superfluid bonded, first change it into superfluid undelegate to burn minted osmo and instantly undelegate. if migrationType == SuperfluidBonded { _, err = k.undelegateCommon(ctx, sender, lockID) if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } } @@ -695,11 +638,11 @@ func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, va } else if migrationType == Unlocked { totalAmtConverted, err = k.convertUnlockedToStake(ctx, senderAddr, valAddr, sharesToConvert, minAmtToStake) } else { // liquid gamm shares without locks are not supported - return sdk.ZeroInt(), fmt.Errorf("unsupported staking conversion type") + return sdk.Int{}, fmt.Errorf("unsupported staking conversion type") } if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } return totalAmtConverted, nil @@ -711,7 +654,7 @@ func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAd minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { lock, err := k.lk.GetLockByID(ctx, lockId) if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } // check lock owner is sender @@ -727,12 +670,12 @@ func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAd // Ensuring the sharesToMigrate contains gamm pool share prefix. if !strings.HasPrefix(lockCoin.Denom, gammtypes.GAMMTokenPrefix) { - return sdk.ZeroInt(), types.SharesToMigrateDenomPrefixError{Denom: lockCoin.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} + return sdk.Int{}, types.SharesToMigrateDenomPrefixError{Denom: lockCoin.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} } poolIdLeaving, err := gammtypes.GetPoolIdFromShareDenom(lockCoin.Denom) if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } var superfluidValAddr string @@ -745,12 +688,12 @@ func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAd // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. exitCoins, err := k.forceUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, lockCoin, sdk.NewCoins(), false) if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } totalAmtConverted, err = k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake, superfluidValAddr) if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } return totalAmtConverted, nil @@ -759,25 +702,25 @@ func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAd func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { if !strings.HasPrefix(sharesToStake.Denom, gammtypes.GAMMTokenPrefix) { - return sdk.ZeroInt(), types.SharesToMigrateDenomPrefixError{Denom: sharesToStake.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} + return sdk.Int{}, types.SharesToMigrateDenomPrefixError{Denom: sharesToStake.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} } // Get the balancer poolId by parsing the gamm share denom. poolIdLeaving, err := gammtypes.GetPoolIdFromShareDenom(sharesToStake.Denom) if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } // Exit the balancer pool position. // we exit with min token out amount zero since we are checking min amount designated to stake later on anyways. exitCoins, err := k.gk.ExitPool(ctx, sender, poolIdLeaving, sharesToStake.Amount, sdk.NewCoins()) if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } totalAmtConverted, err = k.convertGammSharesToOsmoAndStake(ctx, sender, valAddr, poolIdLeaving, exitCoins, minAmtToStake, "") if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } return totalAmtConverted, nil @@ -786,12 +729,7 @@ func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, v // convertGammSharesToOsmoAndStake converts given gamm shares to osmo by swapping in the given pool // then stakes it to the designated validator. // minAmtToStake works as slippage bound, and would error if total amount being staked is less than min amount to stake. -// If valAddr is empty, we attempt to get staking preference from valset-pref module and stake to the given validator. -// Delegation is done in the following logic: -// - If valAddr provided, single delegate. -// - If valAddr not provided and valset exists, valsetpref.Delegate -// - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator -// - Else: error +// Acutal delegation is done based on input paramters of delegateBaseOnValsetPref. func (k Keeper) convertGammSharesToOsmoAndStake( ctx sdk.Context, sender sdk.AccAddress, valAddr string, @@ -816,7 +754,7 @@ func (k Keeper) convertGammSharesToOsmoAndStake( for _, coinToConvert := range nonOsmoCoins { tokenOutAmt, err := k.pmk.SwapExactAmountIn(ctx, sender, poolIdLeaving, coinToConvert, bondDenom, sdk.ZeroInt()) if err != nil { - return sdk.ZeroInt(), err + return sdk.Int{}, err } totalAmtCoverted = totalAmtCoverted.Add(tokenOutAmt) @@ -833,36 +771,49 @@ func (k Keeper) convertGammSharesToOsmoAndStake( } } - // var val stakingtypes.Validator + err = k.delegateBaseOnValsetPref(ctx, sender, valAddr, originalSuperfluidValAddr, totalAmtToStake) + if err != nil { + return sdk.Int{}, err + } + + return totalAmtToStake, nil +} + +// delegateBaseOnValsetPref delegates based on given input parameters. +// Delegation is done in the following logic: +// - If valAddr provided, single delegate. +// - If valAddr not provided and valset exists, valsetpref.Delegate +// - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator +// - Else: error +func (k Keeper) delegateBaseOnValsetPref(ctx sdk.Context, sender sdk.AccAddress, valAddr, originalSuperfluidValAddr string, totalAmtToStake sdk.Int) error { + bondDenom := k.sk.BondDenom(ctx) + // if given valAddr is empty, we use delegation preference given from valset-pref module or reference from superfluid staking if valAddr == "" { err := k.vspk.DelegateToValidatorSet(ctx, sender.String(), sdk.NewCoin(bondDenom, totalAmtToStake)) + // if valset-pref delegation succeeded without error, end method + if err == nil { + return nil + } + // if valset-pref delegation errored due to no existing delegation existing, fall back and try using superfluid staked validator if err == valsettypes.ErrNoDelegation { - val, err := k.validateValAddrForDelegate(ctx, originalSuperfluidValAddr) - if err != nil { - return sdk.ZeroInt(), err - } - - // delegate now! - _, err = k.sk.Delegate(ctx, sender, totalAmtToStake, stakingtypes.Unbonded, val, true) - if err != nil { - return sdk.ZeroInt(), err - } + valAddr = originalSuperfluidValAddr } else if err != nil { // for other errors, handle error - return sdk.ZeroInt(), err - } - } else { - val, err := k.validateValAddrForDelegate(ctx, valAddr) - if err != nil { - return sdk.ZeroInt(), err - } - // delegate now! - _, err = k.sk.Delegate(ctx, sender, totalAmtToStake, stakingtypes.Unbonded, val, true) - if err != nil { - return sdk.ZeroInt(), err + return err } } - return totalAmtToStake, nil + val, err := k.validateValAddrForDelegate(ctx, valAddr) + if err != nil { + return err + } + + // delegate now! + _, err = k.sk.Delegate(ctx, sender, totalAmtToStake, stakingtypes.Unbonded, val, true) + if err != nil { + return err + } + + return nil } diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index e72a063b81e..14776115f1e 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1444,6 +1444,110 @@ func (s *KeeperTestSuite) TestConvertGammSharesToOsmoAndStake() { } } +func (s *KeeperTestSuite) TestDelegateBaseOnValsetPref() { + type tc struct { + useValAddr bool + haveExistingDelegation bool + useOriginalSuperfluidValAddr bool + + useInvalidValAddr bool + + expectedError string + } + testCases := map[string]tc{ + "provide val address": { + useValAddr: true, + }, + "use valset pref delegation": { + haveExistingDelegation: true, + }, + "using valset pref fail, fallback to using provided original superfluid address": { + useOriginalSuperfluidValAddr: true, + }, + "error: using valset pref fail, no superfluid address provided": { + expectedError: "empty address string is not allowed", + }, + "error: invalid val address provded": { + useInvalidValAddr: true, + expectedError: "ecoding bech32 failed: invalid character not part of charset", + }, + } + + for name, tc := range testCases { + s.Run(name, func() { + s.Setup() + bondDenom := s.App.StakingKeeper.BondDenom(s.Ctx) + stakeAmount := sdk.NewInt(100) + + sender := s.TestAccs[0] + s.FundAcc(sender, sdk.NewCoins(sdk.NewCoin(bondDenom, stakeAmount))) + + var valAddr string + if tc.useValAddr { + valAddr = s.SetupValidator(stakingtypes.Bonded).String() + } + if tc.useInvalidValAddr { + valAddr = s.SetupValidator(stakingtypes.Bonded).String() + "invalid" + } + + var originalSuperfluidValAddr string + if tc.useOriginalSuperfluidValAddr { + originalSuperfluidValAddr = s.SetupValidator(stakingtypes.Bonded).String() + } + + // by having existing delegation, we can test val set pref based delgation + var superfluidStakedValAddr sdk.ValAddress + if tc.haveExistingDelegation { + superfluidStakedValAddr = s.SetupValidator(stakingtypes.Bonded) + + stakeCoin := sdk.NewInt64Coin(bondDenom, 100) + s.FundAcc(sender, sdk.NewCoins(stakeCoin)) + validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, superfluidStakedValAddr) + s.Require().True(found) + _, err := s.App.StakingKeeper.Delegate(s.Ctx, sender, stakeCoin.Amount, stakingtypes.Unbonded, validator, true) + s.Require().NoError(err) + } + + // system under test + err := s.App.SuperfluidKeeper.DelegateBaseOnValsetPref(s.Ctx, sender, valAddr, originalSuperfluidValAddr, stakeAmount) + if tc.expectedError != "" { + s.Require().Error(err) + s.Require().ErrorContains(err, tc.expectedError) + return + } + + s.Require().NoError(err) + + // check delegation + if valAddr != "" || originalSuperfluidValAddr != "" { + // we want to check which ever param that was passed in with value + var delegatedAddr string + if valAddr == "" { + delegatedAddr = originalSuperfluidValAddr + } else { + delegatedAddr = valAddr + } + + val, err := sdk.ValAddressFromBech32(delegatedAddr) + s.Require().NoError(err) + del, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, val) + s.Require().True(found) + s.Require().True(del.Shares.RoundInt().Equal(stakeAmount)) + return + } + + // if we are testing valset-pref case(already deleated), check existing delegation address to see if delegation increased + if tc.haveExistingDelegation { + del, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, superfluidStakedValAddr) + s.Require().True(found) + // should be 200(original delegated amount + newly staked amount) + s.Require().True(del.Shares.RoundInt().Equal(stakeAmount.Mul(sdk.NewInt(2)))) + return + } + }) + } +} + func (s *KeeperTestSuite) SetupUnbondConvertAndStakeTest(ctx sdk.Context, superfluidDelegated, superfluidUndelegating, unlocking, noLock bool) (joinPoolAmt sdk.Coins, balancerIntermediaryAcc types.SuperfluidIntermediaryAccount, balancerLock *lockuptypes.PeriodLock, poolCreateAcc, poolJoinAcc sdk.AccAddress, balancerPooId uint64, balancerPoolShareOut sdk.Coin, valAddr sdk.ValAddress) { bankKeeper := s.App.BankKeeper gammKeeper := s.App.GAMMKeeper diff --git a/x/superfluid/types/errors.go b/x/superfluid/types/errors.go index bc8ded5d8f4..61dbf5625af 100644 --- a/x/superfluid/types/errors.go +++ b/x/superfluid/types/errors.go @@ -126,10 +126,3 @@ type TokenConvertedLessThenDesiredStakeError struct { func (e TokenConvertedLessThenDesiredStakeError) Error() string { return fmt.Sprintf("actual amount converted to stake (%s) is less then minimum amount expected to be staked (%s)", e.ActualTotalAmtToStake, e.ExpectedTotalAmtToStake) } - -type NoValsetNoSuperfluidDelegationError struct { -} - -func (e NoValsetNoSuperfluidDelegationError) Error() string { - return fmt.Sprintf("No val-set for delegation nor existing superfluid delegation") -} diff --git a/x/superfluid/types/msg_test.go b/x/superfluid/types/msg_test.go index c62ac21bb90..cf302f68fd9 100644 --- a/x/superfluid/types/msg_test.go +++ b/x/superfluid/types/msg_test.go @@ -119,17 +119,6 @@ func TestUnbondConvertAndStakeMsg(t *testing.T) { }, expectedError: true, }, - { - name: "err: val address in invalid", - msg: &types.MsgUnbondConvertAndStake{ - LockId: 0, - Sender: addr1, - ValAddr: "", - MinAmtToStake: sdk.NewInt(10), - SharesToConvert: sdk.NewInt64Coin("foo", 10), - }, - expectedError: true, - }, { name: "err: min amount to stake is negative", msg: &types.MsgUnbondConvertAndStake{ diff --git a/x/superfluid/types/msgs.go b/x/superfluid/types/msgs.go index 3b731f1d327..9994cb5e24f 100644 --- a/x/superfluid/types/msgs.go +++ b/x/superfluid/types/msgs.go @@ -406,9 +406,7 @@ func (msg MsgUnbondConvertAndStake) ValidateBasic() error { if err != nil { return fmt.Errorf("Invalid sender address (%s)", err) } - if msg.ValAddr == "" { - return fmt.Errorf("ValAddr should not be empty") - } + if msg.MinAmtToStake.IsNegative() { return fmt.Errorf("Min amount to stake cannot be negative") } From ba1d94388f923880c7a956407855d7c66caf2f4c Mon Sep 17 00:00:00 2001 From: mattverse Date: Thu, 10 Aug 2023 19:23:43 +0900 Subject: [PATCH 18/27] Add cli --- x/superfluid/client/cli/tx.go | 58 +++++++++++++++++++++++++++++++++++ x/superfluid/keeper/stake.go | 3 ++ x/superfluid/types/msgs.go | 10 ++++++ 3 files changed, 71 insertions(+) diff --git a/x/superfluid/client/cli/tx.go b/x/superfluid/client/cli/tx.go index 0de2f6b6e34..421791ba422 100644 --- a/x/superfluid/client/cli/tx.go +++ b/x/superfluid/client/cli/tx.go @@ -34,6 +34,7 @@ func GetTxCmd() *cobra.Command { // NewSuperfluidRedelegateCmd(), NewCmdLockAndSuperfluidDelegate(), NewCmdUnPoolWhitelistedPool(), + NewUnbondConvertAndStake(), ) osmocli.AddTxCmd(cmd, NewCreateFullRangePositionAndSuperfluidDelegateCmd) osmocli.AddTxCmd(cmd, NewAddToConcentratedLiquiditySuperfluidPositionCmd) @@ -423,3 +424,60 @@ func NewUnlockAndMigrateSharesToFullRangeConcentratedPositionCmd() (*osmocli.TxC Example: "unlock-and-migrate-cl 10 25000000000gamm/pool/2 1000000000uosmo,10000000uion", }, &types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition{} } + +func NewUnbondConvertAndStake() *cobra.Command { + cmd := &cobra.Command{ + Use: "unbond-convert-and-stake [lock-id] [valAddr] [min-amount-to-stake](optional) [shares-to-convert](optional)", + Short: "instantly unbond any locked gamm shares convert them into osmo and stake", + Example: "unbond-convert-and-stake 10 osmo1xxx 100000uosmo", + Args: cobra.MinimumNArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) + + sender := clientCtx.GetFromAddress() + + lockId, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + + valAddr := args[1] + + // if user provided args for min amount to stake, use it. If not, use Zero Int. + var minAmtToStake sdk.Int + if len(args) > 3 { + convertedInt, ok := sdk.NewIntFromString(args[2]) + if !ok { + return fmt.Errorf("Conversion for sdk.Int failed") + } + minAmtToStake = convertedInt + } else { + minAmtToStake = sdk.ZeroInt() + } + + // if user provided args for min amount to stake, use it. If not, use empty coin struct + var sharesToConvert sdk.Coin + if len(args) > 4 { + coins, err := sdk.ParseCoinNormalized(args[3]) + if err != nil { + return err + } + sharesToConvert = coins + } else { + sharesToConvert = sdk.Coin{} + } + + msg := types.NewMsgUnbondConvertAndStake(sender, uint64(lockId), valAddr, minAmtToStake, sharesToConvert) + + return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 59a70c714ae..0d5a6bfed1f 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -183,14 +183,17 @@ func (k Keeper) validateLockForSFDelegate(ctx sdk.Context, lock *lockuptypes.Per // ensure the valAddr is correctly formatted & corresponds to a real validator on chain. func (k Keeper) validateValAddrForDelegate(ctx sdk.Context, valAddr string) (stakingtypes.Validator, error) { + ctx.Logger().Error("here") valAddress, err := sdk.ValAddressFromBech32(valAddr) if err != nil { return stakingtypes.Validator{}, err } + ctx.Logger().Error("here2") validator, found := k.sk.GetValidator(ctx, valAddress) if !found { return stakingtypes.Validator{}, stakingtypes.ErrNoValidatorFound } + ctx.Logger().Error("here3") return validator, nil } diff --git a/x/superfluid/types/msgs.go b/x/superfluid/types/msgs.go index 9994cb5e24f..c6a85aa2195 100644 --- a/x/superfluid/types/msgs.go +++ b/x/superfluid/types/msgs.go @@ -396,6 +396,16 @@ func (msg MsgAddToConcentratedLiquiditySuperfluidPosition) GetSigners() []sdk.Ac var _ sdk.Msg = &MsgUnbondConvertAndStake{} +func NewMsgUnbondConvertAndStake(sender sdk.AccAddress, lockId uint64, valAddr string, minAmtToStake sdk.Int, sharesToConvert sdk.Coin) *MsgUnbondConvertAndStake { + return &MsgUnbondConvertAndStake{ + Sender: sender.String(), + LockId: lockId, + ValAddr: valAddr, + MinAmtToStake: minAmtToStake, + SharesToConvert: sharesToConvert, + } +} + func (msg MsgUnbondConvertAndStake) Route() string { return RouterKey } func (msg MsgUnbondConvertAndStake) Type() string { return TypeMsgUnbondConvertAndStake From c8b9f31cfcef20ff0f05e786a13ce38bf5ebc464 Mon Sep 17 00:00:00 2001 From: mattverse Date: Fri, 11 Aug 2023 00:38:57 +0900 Subject: [PATCH 19/27] Fix cli --- x/superfluid/client/cli/tx.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/x/superfluid/client/cli/tx.go b/x/superfluid/client/cli/tx.go index 421791ba422..697b1af7a59 100644 --- a/x/superfluid/client/cli/tx.go +++ b/x/superfluid/client/cli/tx.go @@ -440,7 +440,6 @@ func NewUnbondConvertAndStake() *cobra.Command { txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) sender := clientCtx.GetFromAddress() - lockId, err := strconv.Atoi(args[0]) if err != nil { return err @@ -450,25 +449,23 @@ func NewUnbondConvertAndStake() *cobra.Command { // if user provided args for min amount to stake, use it. If not, use Zero Int. var minAmtToStake sdk.Int - if len(args) > 3 { + // if user provided args for min amount to stake, use it. If not, use empty coin struct + var sharesToConvert sdk.Coin + if len(args) >= 3 { convertedInt, ok := sdk.NewIntFromString(args[2]) if !ok { return fmt.Errorf("Conversion for sdk.Int failed") } minAmtToStake = convertedInt - } else { - minAmtToStake = sdk.ZeroInt() - } - - // if user provided args for min amount to stake, use it. If not, use empty coin struct - var sharesToConvert sdk.Coin - if len(args) > 4 { - coins, err := sdk.ParseCoinNormalized(args[3]) - if err != nil { - return err + if len(args) == 4 { + coins, err := sdk.ParseCoinNormalized(args[3]) + if err != nil { + return err + } + sharesToConvert = coins } - sharesToConvert = coins } else { + minAmtToStake = sdk.ZeroInt() sharesToConvert = sdk.Coin{} } From 0e9eeb1cb511480bbc83db88ee7c3fdbe106b69f Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Thu, 10 Aug 2023 12:49:39 -0500 Subject: [PATCH 20/27] Update x/superfluid/keeper/stake.go --- x/superfluid/keeper/stake.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 0d5a6bfed1f..59a70c714ae 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -183,17 +183,14 @@ func (k Keeper) validateLockForSFDelegate(ctx sdk.Context, lock *lockuptypes.Per // ensure the valAddr is correctly formatted & corresponds to a real validator on chain. func (k Keeper) validateValAddrForDelegate(ctx sdk.Context, valAddr string) (stakingtypes.Validator, error) { - ctx.Logger().Error("here") valAddress, err := sdk.ValAddressFromBech32(valAddr) if err != nil { return stakingtypes.Validator{}, err } - ctx.Logger().Error("here2") validator, found := k.sk.GetValidator(ctx, valAddress) if !found { return stakingtypes.Validator{}, stakingtypes.ErrNoValidatorFound } - ctx.Logger().Error("here3") return validator, nil } From 97c80f22376719ab65835c649bd419d0c0b337d8 Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Thu, 10 Aug 2023 12:50:04 -0500 Subject: [PATCH 21/27] Update x/superfluid/client/cli/tx.go --- x/superfluid/client/cli/tx.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/superfluid/client/cli/tx.go b/x/superfluid/client/cli/tx.go index 697b1af7a59..68902957820 100644 --- a/x/superfluid/client/cli/tx.go +++ b/x/superfluid/client/cli/tx.go @@ -447,7 +447,6 @@ func NewUnbondConvertAndStake() *cobra.Command { valAddr := args[1] - // if user provided args for min amount to stake, use it. If not, use Zero Int. var minAmtToStake sdk.Int // if user provided args for min amount to stake, use it. If not, use empty coin struct var sharesToConvert sdk.Coin From a2ad24b03cd231b21d1934ec6ce944b647a3fd0f Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Thu, 10 Aug 2023 12:50:23 -0500 Subject: [PATCH 22/27] Update x/superfluid/keeper/stake.go Co-authored-by: Roman --- x/superfluid/keeper/stake.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 59a70c714ae..2c23745fed6 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -729,7 +729,7 @@ func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, v // convertGammSharesToOsmoAndStake converts given gamm shares to osmo by swapping in the given pool // then stakes it to the designated validator. // minAmtToStake works as slippage bound, and would error if total amount being staked is less than min amount to stake. -// Acutal delegation is done based on input paramters of delegateBaseOnValsetPref. +// Actual delegation is done based on input parameters of delegateBaseOnValsetPref. func (k Keeper) convertGammSharesToOsmoAndStake( ctx sdk.Context, sender sdk.AccAddress, valAddr string, From 18f4bd7e81d8dd238fc541fa4d9cd840ca82d0ed Mon Sep 17 00:00:00 2001 From: mattverse Date: Fri, 11 Aug 2023 22:27:08 +0900 Subject: [PATCH 23/27] Roman and Adam --- x/superfluid/keeper/stake.go | 16 ++++++++++----- x/superfluid/keeper/stake_test.go | 33 +++++++++++++++++++++++++++---- x/superfluid/types/msg_test.go | 10 +++++++++- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 2c23745fed6..623eeebbe59 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -610,10 +610,12 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn // Delegation is done in the following logic: // - If valAddr provided, single delegate. // - If valAddr not provided and valset exists, valsetpref.Delegate -// - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator +// - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator if it was a superfluid lock // - Else: error func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, minAmtToStake sdk.Int, sharesToConvert sdk.Coin) (totalAmtConverted sdk.Int, err error) { + ctx.Logger().Error(sharesToConvert.String()) + ctx.Logger().Error(minAmtToStake.String()) senderAddr, err := sdk.AccAddressFromBech32(sender) if err != nil { return sdk.Int{}, err @@ -635,9 +637,9 @@ func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, va if migrationType == SuperfluidBonded || migrationType == SuperfluidUnbonding || migrationType == NonSuperfluid { totalAmtConverted, err = k.convertLockToStake(ctx, senderAddr, valAddr, lockID, minAmtToStake) - } else if migrationType == Unlocked { + } else if migrationType == Unlocked { // liquid gamm shares without locks totalAmtConverted, err = k.convertUnlockedToStake(ctx, senderAddr, valAddr, sharesToConvert, minAmtToStake) - } else { // liquid gamm shares without locks are not supported + } else { // any other types of migration should fail return sdk.Int{}, fmt.Errorf("unsupported staking conversion type") } @@ -699,6 +701,8 @@ func (k Keeper) convertLockToStake(ctx sdk.Context, sender sdk.AccAddress, valAd return totalAmtConverted, nil } +// convertUnlockedToStake converts liquid gamm shares to staking delegation. +// minAmtToStake works as slippage bound for the conversion process. func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, valAddr string, sharesToStake sdk.Coin, minAmtToStake sdk.Int) (totalAmtConverted sdk.Int, err error) { if !strings.HasPrefix(sharesToStake.Denom, gammtypes.GAMMTokenPrefix) { @@ -729,7 +733,8 @@ func (k Keeper) convertUnlockedToStake(ctx sdk.Context, sender sdk.AccAddress, v // convertGammSharesToOsmoAndStake converts given gamm shares to osmo by swapping in the given pool // then stakes it to the designated validator. // minAmtToStake works as slippage bound, and would error if total amount being staked is less than min amount to stake. -// Actual delegation is done based on input parameters of delegateBaseOnValsetPref. +// Depending on user inputs, valAddr and originalSuperfluidValAddr could be an empty string, +// each leading to a different delegation scenario. func (k Keeper) convertGammSharesToOsmoAndStake( ctx sdk.Context, sender sdk.AccAddress, valAddr string, @@ -780,10 +785,11 @@ func (k Keeper) convertGammSharesToOsmoAndStake( } // delegateBaseOnValsetPref delegates based on given input parameters. +// valAddr and originalSuperfluidValAddr can be an empty string depending on user input and original lock's status. // Delegation is done in the following logic: // - If valAddr provided, single delegate. // - If valAddr not provided and valset exists, valsetpref.Delegate -// - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator +// - If valAddr not provided and valset delegation is not possible, refer back to original lock's superfluid validator if it was a superfluid lock // - Else: error func (k Keeper) delegateBaseOnValsetPref(ctx sdk.Context, sender sdk.AccAddress, valAddr, originalSuperfluidValAddr string, totalAmtToStake sdk.Int) error { bondDenom := k.sk.BondDenom(ctx) diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 14776115f1e..183f7632d4e 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1038,6 +1038,7 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { superfluidUndelegating bool unlocking bool unlocked bool + testCLLock bool expectedError error } testCases := map[string]tc{ @@ -1056,16 +1057,40 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { notSuperfluidDelegated: true, unlocked: true, }, + "error: concentrated lock should fail": { + testCLLock: true, + expectedError: types.SharesToMigrateDenomPrefixError{ + Denom: "cl/pool/2", + ExpectedDenomPrefix: "gamm/pool/", + }, + }, } for name, tc := range testCases { s.Run(name, func() { s.SetupTest() s.Ctx = s.Ctx.WithBlockTime(defaultJoinTime) - // We bundle all migration setup into a single function to avoid repeating the same code for each test case. - _, _, lock, _, joinPoolAcc, _, balancerShareOut, originalValAddr := s.SetupUnbondConvertAndStakeTest(s.Ctx, !tc.notSuperfluidDelegated, tc.superfluidUndelegating, tc.unlocking, tc.unlocked) - // testing params + var ( + lock *lockuptypes.PeriodLock + lockId uint64 + joinPoolAcc sdk.AccAddress + originalValAddr sdk.ValAddress + balancerShareOut sdk.Coin + ) + // we use migration setup for testing with cl lock + if tc.testCLLock { + _, _, lock, _, joinPoolAcc, _, _, balancerShareOut, originalValAddr = s.SetupMigrationTest(s.Ctx, !tc.notSuperfluidDelegated, tc.superfluidUndelegating, tc.unlocking, tc.unlocked, sdk.MustNewDecFromStr("1")) + synthLockBeforeMigration, _, err := s.App.SuperfluidKeeper.GetMigrationType(s.Ctx, joinPoolAcc, int64(lock.ID)) + s.Require().NoError(err) + _, lockId, _, _, err = s.App.SuperfluidKeeper.MigrateSuperfluidBondedBalancerToConcentrated(s.Ctx, joinPoolAcc, lock.ID, lock.Coins[0], synthLockBeforeMigration.SynthDenom, sdk.NewCoins()) + s.Require().NoError(err) + } else { + // We bundle all migration setup into a single function to avoid repeating the same code for each test case. + _, _, lock, _, joinPoolAcc, _, balancerShareOut, originalValAddr = s.SetupUnbondConvertAndStakeTest(s.Ctx, !tc.notSuperfluidDelegated, tc.superfluidUndelegating, tc.unlocking, tc.unlocked) + lockId = lock.ID + } + sender := sdk.MustAccAddressFromBech32(joinPoolAcc.String()) valAddr := s.SetupValidator(stakingtypes.Bonded) minAmountToStake := sdk.ZeroInt() @@ -1078,7 +1103,7 @@ func (s *KeeperTestSuite) TestUnbondConvertAndStake() { balanceBeforeConvertLockToStake := s.App.BankKeeper.GetAllBalances(s.Ctx, sender).FilterDenoms([]string{"foo", "stake", "uosmo"}) // system under test - totalAmtConverted, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lock.ID, sender.String(), valAddr.String(), minAmountToStake, sharesToConvert) + totalAmtConverted, err := s.App.SuperfluidKeeper.UnbondConvertAndStake(s.Ctx, lockId, sender.String(), valAddr.String(), minAmountToStake, sharesToConvert) if tc.expectedError != nil { s.Require().Equal(err.Error(), tc.expectedError.Error()) s.Require().Error(err) diff --git a/x/superfluid/types/msg_test.go b/x/superfluid/types/msg_test.go index cf302f68fd9..761a1792d46 100644 --- a/x/superfluid/types/msg_test.go +++ b/x/superfluid/types/msg_test.go @@ -108,6 +108,15 @@ func TestUnbondConvertAndStakeMsg(t *testing.T) { SharesToConvert: sdk.NewInt64Coin("foo", 10), }, }, + { + name: "no val address should not fail", + msg: &types.MsgUnbondConvertAndStake{ + LockId: 0, + Sender: addr1, + MinAmtToStake: sdk.NewInt(10), + SharesToConvert: sdk.NewInt64Coin("foo", 10), + }, + }, { name: "err: sender is invalid", msg: &types.MsgUnbondConvertAndStake{ @@ -124,7 +133,6 @@ func TestUnbondConvertAndStakeMsg(t *testing.T) { msg: &types.MsgUnbondConvertAndStake{ LockId: 0, Sender: addr1, - ValAddr: "abcd", MinAmtToStake: sdk.NewInt(10).Neg(), SharesToConvert: sdk.NewInt64Coin("foo", 10), }, From e5a054c10f932194da367b9a2b399890d0b18810 Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Fri, 11 Aug 2023 16:50:41 -0500 Subject: [PATCH 24/27] Update x/superfluid/keeper/stake.go --- x/superfluid/keeper/stake.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 623eeebbe59..0d9ceb87ab2 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -614,8 +614,6 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn // - Else: error func (k Keeper) UnbondConvertAndStake(ctx sdk.Context, lockID uint64, sender, valAddr string, minAmtToStake sdk.Int, sharesToConvert sdk.Coin) (totalAmtConverted sdk.Int, err error) { - ctx.Logger().Error(sharesToConvert.String()) - ctx.Logger().Error(minAmtToStake.String()) senderAddr, err := sdk.AccAddressFromBech32(sender) if err != nil { return sdk.Int{}, err From fe4b64c8b1e1d22c45d3492539dfeb244ec1fcb0 Mon Sep 17 00:00:00 2001 From: mattverse Date: Sat, 12 Aug 2023 21:52:39 +0900 Subject: [PATCH 25/27] Adams comment --- x/superfluid/keeper/stake_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index 80cbb1777cc..aeee59c8559 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1296,6 +1296,12 @@ func (s *KeeperTestSuite) TestConvertUnlockedToStake() { s.Require().NoError(err) bondDenomPoolAmtBeforeConvert := totalPoolLiquidityBeforeConvert.AmountOf(bondDenom) + var expectedBondDenomAmt sdk.Int + // check expected bond denom pool liquidity amount after conversion(only for non error cases) + if tc.expectedError == nil { + expectedBondDenomAmt = s.getExpectedBondDenomPoolAmtAfterConvert(sender, poolId, sharesToStake) + } + // system under test totalAmtConverted, err := s.App.SuperfluidKeeper.ConvertUnlockedToStake(s.Ctx, sender, valAddr.String(), sharesToStake, minAmtToStake) if tc.expectedError != nil { @@ -1310,6 +1316,7 @@ func (s *KeeperTestSuite) TestConvertUnlockedToStake() { // check that pool liquidity have reduced bondDenomPoolAmtAfterConvert := totalPoolLiquidityAfterConvert.AmountOf(bondDenom) s.Require().True(bondDenomPoolAmtAfterConvert.LT(bondDenomPoolAmtBeforeConvert)) + s.Require().True(expectedBondDenomAmt.Equal(bondDenomPoolAmtAfterConvert)) // Staking & Delegation check s.delegationCheck(s.Ctx, sender, sdk.ValAddress{}, valAddr, totalAmtConverted) @@ -1702,6 +1709,27 @@ func (s *KeeperTestSuite) lockCheck(ctx sdk.Context, lock lockuptypes.PeriodLock s.Require().Error(err) } +func (s *KeeperTestSuite) getExpectedBondDenomPoolAmtAfterConvert(sender sdk.AccAddress, poolId uint64, sharesToStake sdk.Coin) sdk.Int { + bondDenom := s.App.StakingKeeper.BondDenom(s.Ctx) + cc, _ := s.Ctx.CacheContext() + exitCoins, err := s.App.GAMMKeeper.ExitPool(cc, sender, poolId, sharesToStake.Amount, sdk.NewCoins()) + s.Require().NoError(err) + + var nonOsmoCoin sdk.Coin + for _, exitCoin := range exitCoins { + // if coin is not uosmo, add it to non-osmo Coins + if exitCoin.Denom != bondDenom { + nonOsmoCoin = exitCoin + } + } + _, err = s.App.PoolManagerKeeper.SwapExactAmountIn(cc, sender, poolId, nonOsmoCoin, bondDenom, sdk.ZeroInt()) + s.Require().NoError(err) + expectedLiquidity, err := s.App.GAMMKeeper.GetTotalPoolLiquidity(cc, poolId) + s.Require().NoError(err) + + return expectedLiquidity.AmountOf(bondDenom) +} + // type superfluidRedelegation struct { // lockId uint64 // oldValIndex int64 From c7b9fdb2dc9b795ff528ba917ecd1bc8173ddfff Mon Sep 17 00:00:00 2001 From: "Matt, Park" <45252226+mattverse@users.noreply.github.com> Date: Mon, 14 Aug 2023 22:46:43 +0900 Subject: [PATCH 26/27] Update x/superfluid/keeper/stake.go Co-authored-by: Roman --- x/superfluid/keeper/stake.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 8cd1b449eaf..0df2464477d 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -824,7 +824,7 @@ func (k Keeper) convertGammSharesToOsmoAndStake( // check if the total amount to stake after all conversion is greater than provided min amount to stake if totalAmtToStake.LT(minAmtToStake) { - return sdk.ZeroInt(), types.TokenConvertedLessThenDesiredStakeError{ + return sdk.Int{}, types.TokenConvertedLessThenDesiredStakeError{ ActualTotalAmtToStake: totalAmtToStake, ExpectedTotalAmtToStake: minAmtToStake, } From 924468690468f7d48e2f52a521bcc297fe4b4792 Mon Sep 17 00:00:00 2001 From: mattverse Date: Mon, 14 Aug 2023 22:56:25 +0900 Subject: [PATCH 27/27] Romans comments --- x/superfluid/keeper/stake_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index aeee59c8559..c0eb11aa5d9 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -1683,6 +1683,10 @@ func (s *KeeperTestSuite) SetupUnbondConvertAndStakeTest(ctx sdk.Context, superf } +// delegationCheck checks staking related invariants of the test. +// We check the following in this method: +// - if superfluid staked previously, check if the original validator's delegation has been deleted. +// - Cehck if the delegation of the new validator matches what's expected. func (s *KeeperTestSuite) delegationCheck(ctx sdk.Context, sender sdk.AccAddress, originalValAddr, newValAddr sdk.ValAddress, totalAmtConverted sdk.Int) { if !originalValAddr.Empty() { // check if original superfluid staked lock's delgation is successfully deleted @@ -1693,8 +1697,13 @@ func (s *KeeperTestSuite) delegationCheck(ctx sdk.Context, sender sdk.AccAddress delegation, found := s.App.StakingKeeper.GetDelegation(s.Ctx, sender, newValAddr) s.Require().True(found) s.Require().True(totalAmtConverted.ToDec().Equal(delegation.Shares)) + s.Require().True(delegation.Shares.Equal(totalAmtConverted.ToDec())) } +// lockCheck checks lock related invariants of the test. +// We check the following in this method: +// - check if old synth lock has been deleted (both staking & unstaking) +// - check if old lock has been succesfully deleted. func (s *KeeperTestSuite) lockCheck(ctx sdk.Context, lock lockuptypes.PeriodLock, valAddr string, checkUnstakingSynthLock bool) { // The synthetic lockup should be deleted. _, err := s.App.LockupKeeper.GetSyntheticLockup(s.Ctx, lock.ID, keeper.StakingSyntheticDenom(lock.Coins[0].Denom, valAddr))