From db88fa29e30ed0bef97a0601ed13d3a6ee90379f Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Wed, 10 May 2023 21:37:33 -0500 Subject: [PATCH 01/15] initial push --- proto/osmosis/superfluid/tx.proto | 4 +- x/gamm/types/key.go | 5 +- x/superfluid/client/cli/tx.go | 9 + x/superfluid/keeper/concentrated_liquidity.go | 2 +- x/superfluid/keeper/export_test.go | 38 ++- x/superfluid/keeper/migrate.go | 282 +++++++++-------- x/superfluid/keeper/migrate_test.go | 292 +++++++++++------- x/superfluid/keeper/msg_server.go | 64 ++-- x/superfluid/keeper/msg_server_test.go | 16 +- x/superfluid/keeper/stake.go | 6 +- x/superfluid/keeper/unpool.go | 42 ++- x/superfluid/keeper/unpool_test.go | 146 +++++++++ x/superfluid/types/errors.go | 52 ++++ x/superfluid/types/tx.pb.go | 153 +++++---- 14 files changed, 707 insertions(+), 404 deletions(-) diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index a30df7a5299..f7860e388ec 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -150,8 +150,8 @@ message MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition { ]; // token_out_mins indicates minimum token to exit Balancer pool with. repeated cosmos.base.v1beta1.Coin token_out_mins = 4 [ - (gogoproto.moretags) = "yaml:\"token_out_min_amounts\"", - (gogoproto.nullable) = false + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" ]; } diff --git a/x/gamm/types/key.go b/x/gamm/types/key.go index ebfea87e42f..36a00421380 100644 --- a/x/gamm/types/key.go +++ b/x/gamm/types/key.go @@ -33,12 +33,11 @@ var ( ) func MustGetPoolIdFromShareDenom(denom string) uint64 { - numberStr := strings.TrimLeft(denom, GAMMTokenPrefix) - number, err := strconv.Atoi(numberStr) + number, err := GetPoolIdFromShareDenom(denom) if err != nil { panic(err) } - return uint64(number) + return number } func GetPoolIdFromShareDenom(denom string) (uint64, error) { diff --git a/x/superfluid/client/cli/tx.go b/x/superfluid/client/cli/tx.go index 6f7e38137dd..bb3f0de6496 100644 --- a/x/superfluid/client/cli/tx.go +++ b/x/superfluid/client/cli/tx.go @@ -37,6 +37,7 @@ func GetTxCmd() *cobra.Command { ) osmocli.AddTxCmd(cmd, NewCreateFullRangePositionAndSuperfluidDelegateCmd) osmocli.AddTxCmd(cmd, NewAddToConcentratedLiquiditySuperfluidPositionCmd) + osmocli.AddTxCmd(cmd, NewUnlockAndMigrateSharesToFullRangeConcentratedPositionCmd) return cmd } @@ -414,3 +415,11 @@ func NewAddToConcentratedLiquiditySuperfluidPositionCmd() (*osmocli.TxCliDesc, * Example: "add-to-superfluid-cl-position 10 1000000000uosmo 10000000uion", }, &types.MsgAddToConcentratedLiquiditySuperfluidPosition{} } + +func NewUnlockAndMigrateSharesToFullRangeConcentratedPositionCmd() (*osmocli.TxCliDesc, *types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) { + return &osmocli.TxCliDesc{ + Use: "unlock-and-migrate-to-cl [lock-id] [shares-to-migrate] [token-out-mins]", + Short: "unlock and migrate gamm shares to full range concentrated position", + Example: "unlock-and-migrate-cl 10 25000000000gamm/pool/2 1000000000uosmo,10000000uion", + }, &types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition{} +} diff --git a/x/superfluid/keeper/concentrated_liquidity.go b/x/superfluid/keeper/concentrated_liquidity.go index 0a2085831c8..bdba4278db9 100644 --- a/x/superfluid/keeper/concentrated_liquidity.go +++ b/x/superfluid/keeper/concentrated_liquidity.go @@ -69,7 +69,7 @@ func (k Keeper) addToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, ow // Superfluid undelegate the superfluid delegated position. // This deletes the connection between the lock and the intermediate account, deletes the synthetic lock, and burns the synthetic osmo. - intermediateAccount, err := k.SuperfluidUndelegateToConcentratedPosition(ctx, owner.String(), lockId) + intermediateAccount, err := k.superfluidUndelegateToConcentratedPosition(ctx, owner.String(), lockId) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err } diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 87c821c7cb5..edff6da3f45 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -5,8 +5,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - cltypes "github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/types" lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types" + superfluidtypes "github.com/osmosis-labs/osmosis/v15/x/superfluid/types" ) var ( @@ -22,26 +22,42 @@ func (k Keeper) PrepareConcentratedLockForSlash(ctx sdk.Context, lock *lockuptyp return k.prepareConcentratedLockForSlash(ctx, lock, slashAmt) } -func (k Keeper) MigrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving, poolIdEntering uint64, preMigrationLock *lockuptypes.PeriodLock, lockId uint64, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, concentratedPool cltypes.ConcentratedPoolExtension, remainingLockTime time.Duration, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId uint64, err error) { - return k.migrateSuperfluidBondedBalancerToConcentrated(ctx, sender, poolIdLeaving, poolIdEntering, preMigrationLock, lockId, sharesToMigrate, synthDenomBeforeMigration, concentratedPool, remainingLockTime, tokenOutMins) +func (k Keeper) MigrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { + return k.migrateSuperfluidBondedBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthDenomBeforeMigration, tokenOutMins) } -func (k Keeper) MigrateSuperfluidUnbondingBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving, poolIdEntering uint64, preMigrationLock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, concentratedPool cltypes.ConcentratedPoolExtension, remainingLockTime time.Duration, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId uint64, err error) { - return k.migrateSuperfluidUnbondingBalancerToConcentrated(ctx, sender, poolIdLeaving, poolIdEntering, preMigrationLock, sharesToMigrate, synthDenomBeforeMigration, concentratedPool, remainingLockTime, tokenOutMins) +func (k Keeper) MigrateSuperfluidUnbondingBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { + return k.migrateSuperfluidUnbondingBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthDenomBeforeMigration, tokenOutMins) } -func (k Keeper) MigrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving, poolIdEntering uint64, preMigrationLock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, concentratedPool cltypes.ConcentratedPoolExtension, remainingLockTime time.Duration, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId uint64, err error) { - return k.migrateNonSuperfluidLockBalancerToConcentrated(ctx, sender, poolIdLeaving, poolIdEntering, preMigrationLock, sharesToMigrate, concentratedPool, remainingLockTime, tokenOutMins) +func (k Keeper) MigrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { + 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) ValidateSharesToMigrateUnlockAndExitBalancerPool(ctx sdk.Context, sender sdk.AccAddress, poolIdLeaving uint64, lock *lockuptypes.PeriodLock, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins, remainingLockTime time.Duration) (exitCoins sdk.Coins, remainingSharesLock lockuptypes.PeriodLock, err error) { + return k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins, remainingLockTime) } -func (k Keeper) PrepareMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (poolIdLeaving, poolIdEntering uint64, concentratedPool cltypes.ConcentratedPoolExtension, preMigrationLock *lockuptypes.PeriodLock, remainingLockTime time.Duration, synthLockBeforeMigration []lockuptypes.SyntheticLock, isSuperfluidBonded, isSuperfluidUnbonding bool, err error) { - return k.prepareMigration(ctx, sender, lockId, sharesToMigrate) +func (k Keeper) RouteMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (synthLocksBeforeMigration []lockuptypes.SyntheticLock, isSuperfluidBonded, isSuperfluidUnbonding bool, err error) { + return k.routeMigration(ctx, sender, lockId, sharesToMigrate) +} + +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) { + return k.validateMigration(ctx, sender, lockId, sharesToMigrate) } func (k Keeper) AddToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, owner sdk.AccAddress, positionId uint64, amount0Added, amount1Added sdk.Int) (uint64, sdk.Int, sdk.Int, sdk.Dec, uint64, error) { return k.addToConcentratedLiquiditySuperfluidPosition(ctx, owner, positionId, amount0Added, amount1Added) } + +func (k Keeper) SuperfluidUndelegateToConcentratedPosition(ctx sdk.Context, sender string, gammLockID uint64) (superfluidtypes.SuperfluidIntermediaryAccount, error) { + return k.superfluidUndelegateToConcentratedPosition(ctx, sender, gammLockID) +} + +func (k Keeper) ValidateGammLockForSuperfluid(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { + return k.validateGammLockForSuperfluid(ctx, sender, poolId, lockId) +} + +func (k Keeper) GetExistingLockRemainingDuration(ctx sdk.Context, lock *lockuptypes.PeriodLock) (time.Duration, error) { + return k.getExistingLockRemainingDuration(ctx, lock) +} diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index b9a5ea10f60..3b77aedf1e0 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -10,29 +10,30 @@ import ( cltypes "github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/types" gammtypes "github.com/osmosis-labs/osmosis/v15/x/gamm/types" lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types" + "github.com/osmosis-labs/osmosis/v15/x/superfluid/types" ) // RouteLockedBalancerToConcentratedMigration routes the provided lock to the proper migration function based on the lock status. -// If the lock is superfluid delegated, it will undelegate the superfluid position and redelegate it as a concentrated liquidity position. +// If the lock is superfluid delegated, it will instantly undelegate the superfluid position and redelegate it as a concentrated liquidity position. // If the lock is superfluid undelegating, it will instantly undelegate the superfluid position and redelegate it as a concentrated liquidity position, but continue to unlock where it left off. // If the lock is locked or unlocking but not superfluid delegated/undelegating, it will migrate the position and either start unlocking or continue unlocking where it left off. // 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, lockId uint64, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, poolIdLeaving, poolIdEntering, gammLockId, concentratedLockId uint64, err error) { // Validate and retrieve pertinent data required for migration - poolIdLeaving, poolIdEntering, concentratedPool, preMigrationLock, remainingLockTime, synthLockBeforeMigration, isSuperfluidBonded, isSuperfluidUnbonding, err := k.prepareMigration(ctx, sender, lockId, sharesToMigrate) + synthLocksBeforeMigration, isSuperfluidBonded, isSuperfluidUnbonding, err := k.routeMigration(ctx, sender, lockId, sharesToMigrate) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } if isSuperfluidBonded { // Migration logic for superfluid bonded locks - positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, err = k.migrateSuperfluidBondedBalancerToConcentrated(ctx, sender, poolIdLeaving, poolIdEntering, preMigrationLock, lockId, sharesToMigrate, synthLockBeforeMigration[0].SynthDenom, concentratedPool, remainingLockTime, tokenOutMins) + positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, err = k.migrateSuperfluidBondedBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthLocksBeforeMigration[0].SynthDenom, tokenOutMins) } else if isSuperfluidUnbonding { // Migration logic for superfluid unbonding locks - positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, err = k.migrateSuperfluidUnbondingBalancerToConcentrated(ctx, sender, poolIdLeaving, poolIdEntering, preMigrationLock, sharesToMigrate, synthLockBeforeMigration[0].SynthDenom, concentratedPool, remainingLockTime, tokenOutMins) - } else if !isSuperfluidBonded && !isSuperfluidUnbonding && len(synthLockBeforeMigration) == 0 { + positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, err = k.migrateSuperfluidUnbondingBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthLocksBeforeMigration[0].SynthDenom, tokenOutMins) + } else if !isSuperfluidBonded && !isSuperfluidUnbonding && len(synthLocksBeforeMigration) == 0 { // Migration logic for non-superfluid locks - positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, err = k.migrateNonSuperfluidLockBalancerToConcentrated(ctx, sender, poolIdLeaving, poolIdEntering, preMigrationLock, sharesToMigrate, concentratedPool, remainingLockTime, tokenOutMins) + positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, err = k.migrateNonSuperfluidLockBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, tokenOutMins) } else { // Unsupported migration return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, fmt.Errorf("unexpected synth lock state for lock %d", lockId) @@ -41,71 +42,64 @@ func (k Keeper) RouteLockedBalancerToConcentratedMigration(ctx sdk.Context, send } // migrateSuperfluidBondedBalancerToConcentrated migrates a user's superfluid bonded balancer position to a superfluid bonded concentrated liquidity position. -// The function first undelegates the superfluid delegated position, force unlocks and exits the balancer pool, creates a full range concentrated liquidity position, and locks it while superfluid delegating it. -// If there are any remaining gamm shares, they are re-locked and superfluid delegated as normal. The function returns the concentrated liquidity position ID, amounts of +// The function first undelegates the superfluid delegated position, force unlocks and exits the balancer pool, creates a full range concentrated liquidity position, locks it, then superfluid delegates it. +// If there are any remaining gamm shares, they are re-locked back in the gamm pool and superfluid delegated as normal. The function returns the concentrated liquidity position ID, amounts of // tokens in the position, the liquidity amount, join time, and IDs of the involved pools and locks. func (k Keeper) migrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, - poolIdLeaving, poolIdEntering uint64, - preMigrationLock *lockuptypes.PeriodLock, lockId uint64, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, - concentratedPool cltypes.ConcentratedPoolExtension, - remainingLockTime time.Duration, tokenOutMins sdk.Coins, -) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId uint64, err error) { +) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { + poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, err := k.validateMigration(ctx, sender, lockId, sharesToMigrate) + if err != nil { + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err + } + // Get the validator address from the synth denom and ensure it is a valid address. valAddr := strings.Split(synthDenomBeforeMigration, "/")[4] _, err = sdk.ValAddressFromBech32(valAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } - gammSharesInLock := preMigrationLock.Coins[0] - // Superfluid undelegate the superfluid delegated position. - // This deletes the connection between the lock and the intermediate account, deletes the synthetic lock, and burns the synthetic osmo. - intermediateAccount, err := k.SuperfluidUndelegateToConcentratedPosition(ctx, sender.String(), lockId) + // This deletes the connection between the gamm lock and the intermediate account, deletes the synthetic lock, and burns the synthetic osmo. + intermediateAccount, err := k.superfluidUndelegateToConcentratedPosition(ctx, sender.String(), lockId) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } // 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. - exitCoins, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + // It also returns the lock object that contains the remaining shares that were not used in this migration. + exitCoins, remainingSharesLock, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins, remainingLockTime) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } // Create a full range (min to max tick) concentrated liquidity position, lock it, and superfluid delegate it. positionId, amount0, amount1, liquidity, joinTime, concentratedLockId, err = k.clk.CreateFullRangePositionLocked(ctx, poolIdEntering, sender, exitCoins, remainingLockTime) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } err = k.SuperfluidDelegate(ctx, sender.String(), concentratedLockId, intermediateAccount.ValAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } - // If there are remaining gamm shares, we must re-lock them. - remainingGammShares := gammSharesInLock.Sub(sharesToMigrate) - if !remainingGammShares.IsZero() { - // Create a new lock with the remaining gamm shares for the remaining lock time. - newLock, err := k.lk.CreateLock(ctx, sender, sdk.NewCoins(remainingGammShares), remainingLockTime) - gammLockId = newLock.ID - if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err - } - - // If the gamm lock was previously superfluid bonded, superfluid delegate the gamm like normal - err = k.SuperfluidDelegate(ctx, sender.String(), gammLockId, valAddr) + // If there are any remaining gamm shares after the migration, we must re-superfluid delegate them as they were previously in the gamm pool. + if remainingSharesLock.ID != 0 { + gammLockId = remainingSharesLock.ID + // Superfluid delegate the gamm lock. + err = k.SuperfluidDelegate(ctx, sender.String(), remainingSharesLock.ID, valAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } } - return positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, nil + return positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, nil } // migrateSuperfluidUnbondingBalancerToConcentrated migrates a user's superfluid unbonding balancer position to a superfluid unbonding concentrated liquidity position. @@ -115,38 +109,39 @@ func (k Keeper) migrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, // The function returns the concentrated liquidity position ID, amounts of tokens in the position, the liquidity amount, join time, and IDs of the involved pools and locks. func (k Keeper) migrateSuperfluidUnbondingBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, - poolIdLeaving, poolIdEntering uint64, - preMigrationLock *lockuptypes.PeriodLock, + lockId uint64, sharesToMigrate sdk.Coin, synthDenomBeforeMigration string, - concentratedPool cltypes.ConcentratedPoolExtension, - remainingLockTime time.Duration, tokenOutMins sdk.Coins, -) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId uint64, err error) { +) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { + poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, err := k.validateMigration(ctx, sender, lockId, sharesToMigrate) + if err != nil { + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err + } + // Get the validator address from the synth denom and ensure it is a valid address. valAddr := strings.Split(synthDenomBeforeMigration, "/")[4] _, err = sdk.ValAddressFromBech32(valAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } - gammSharesInLock := preMigrationLock.Coins[0] - // Save unlocking state of lock before force unlocking wasUnlocking := preMigrationLock.IsUnlocking() // 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. - exitCoins, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + // It also returns the lock object that contains the remaining shares that were not used in this migration. + exitCoins, remainingSharesLock, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins, remainingLockTime) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } // Create a full range (min to max tick) concentrated liquidity position. // If the lock was unlocking, we create a new lock that is unlocking for the remaining time of the old lock. positionId, amount0, amount1, liquidity, joinTime, concentratedLockId, err = k.clk.CreateFullRangePositionUnlocking(ctx, poolIdEntering, sender, exitCoins, remainingLockTime) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } // The previous gamm intermediary account is now invalid for the new lock, since the underlying denom has changed and intermediary accounts are @@ -155,157 +150,164 @@ func (k Keeper) migrateSuperfluidUnbondingBalancerToConcentrated(ctx sdk.Context concentratedLockupDenom := cltypes.GetConcentratedLockupDenomFromPoolId(poolIdEntering) clIntermediateAccount, err := k.GetOrCreateIntermediaryAccount(ctx, concentratedLockupDenom, valAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } // Create a new synthetic lockup for the new intermediary account in an unlocking status err = k.createSyntheticLockup(ctx, concentratedLockId, clIntermediateAccount, unlockingStatus) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } - // If there are remaining gamm shares, we must re-lock them. - remainingGammShares := gammSharesInLock.Sub(sharesToMigrate) - if !remainingGammShares.IsZero() { - // Create a new lock with the remaining gamm shares for the remaining lock time. - newLock, err := k.lk.CreateLock(ctx, sender, sdk.NewCoins(remainingGammShares), remainingLockTime) - gammLockId = newLock.ID - if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err - } - - // Get the previous gamm intermediary account, create a new gamm synthetic lockup, and set it to unlocking - gammIntermediateAccount, err := k.GetOrCreateIntermediaryAccount(ctx, remainingGammShares.Denom, valAddr) + // If there are any remaining gamm shares after the migration, we must re-create the synthetic lock and begin unlocking it from where it left off. + if remainingSharesLock.ID != 0 { + gammLockId = remainingSharesLock.ID + // Get the previous gamm intermediary account, create a new gamm synthetic lockup, and set it to unlocking. + gammIntermediateAccount, err := k.GetOrCreateIntermediaryAccount(ctx, remainingSharesLock.Coins[0].Denom, valAddr) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } err = k.createSyntheticLockup(ctx, gammLockId, gammIntermediateAccount, unlockingStatus) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } - // If the gamm lock was unlocking, we begin the unlock from where it left off. + // If lock was previously unlocking, begin the unlock from where it left off. if wasUnlocking { - _, err := k.lk.BeginForceUnlock(ctx, newLock.ID, newLock.Coins) + _, err = k.lk.BeginForceUnlock(ctx, remainingSharesLock.ID, remainingSharesLock.Coins) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } } } - return positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, nil + return positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, nil } // migrateNonSuperfluidLockBalancerToConcentrated migrates a user's non-superfluid locked or unlocking balancer position to an unlocking concentrated liquidity position. // The function force unlocks and exits the balancer pool, creates a full range concentrated liquidity position, locks it, and begins unlocking from where the locked or unlocking lock left off. -// If there are any remaining gamm shares, they are re-locked. The function returns the concentrated liquidity position ID, amounts of tokens in the position, the liquidity amount, join time, and IDs of the involved pools and locks. +// If there are any remaining gamm shares, they are re-locked back in the gamm pool. The function returns the concentrated liquidity position ID, amounts of tokens in the position, +// the liquidity amount, join time, and IDs of the involved pools and locks. func (k Keeper) migrateNonSuperfluidLockBalancerToConcentrated(ctx sdk.Context, sender sdk.AccAddress, - poolIdLeaving, poolIdEntering uint64, - preMigrationLock *lockuptypes.PeriodLock, + lockId uint64, sharesToMigrate sdk.Coin, - concentratedPool cltypes.ConcentratedPoolExtension, - remainingLockTime time.Duration, tokenOutMins sdk.Coins, -) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId uint64, err error) { +) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering uint64, err error) { + poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, err := k.validateMigration(ctx, sender, lockId, sharesToMigrate) + if err != nil { + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err + } // Save unlocking state of lock before force unlocking wasUnlocking := preMigrationLock.IsUnlocking() - gammSharesInLock := preMigrationLock.Coins[0] - // 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. - exitCoins, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins) + // It also returns the lock object that contains the remaining shares that were not used in this migration. + exitCoins, remainingSharesLock, err := k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, preMigrationLock, sharesToMigrate, tokenOutMins, remainingLockTime) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } // 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. positionId, amount0, amount1, liquidity, joinTime, concentratedLockId, err = k.clk.CreateFullRangePositionUnlocking(ctx, poolIdEntering, sender, exitCoins, remainingLockTime) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } // If there are remaining gamm shares, we must re-lock them. - remainingGammShares := gammSharesInLock.Sub(sharesToMigrate) - if !remainingGammShares.IsZero() { - // Create a new lock with the remaining gamm shares for the remaining lock time. - newLock, err := k.lk.CreateLock(ctx, sender, sdk.NewCoins(remainingGammShares), remainingLockTime) - gammLockId = newLock.ID - if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err - } - + if remainingSharesLock.ID != 0 { + gammLockId = remainingSharesLock.ID // If the gamm lock was unlocking, we begin the unlock from where it left off. if wasUnlocking { - _, err := k.lk.BeginForceUnlock(ctx, newLock.ID, newLock.Coins) + _, err := k.lk.BeginForceUnlock(ctx, remainingSharesLock.ID, remainingSharesLock.Coins) if err != nil { - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } } } - return positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, nil + return positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, nil +} + +// 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, lockId uint64, sharesToMigrate sdk.Coin) (synthLocksBeforeMigration []lockuptypes.SyntheticLock, isSuperfluidBonded, isSuperfluidUnbonding bool, err error) { + // Check if the lock has any corresponding synthetic locks. + // Synthetic lock existence implies that the lock is superfluid delegated or undelegating. + synthLocksBeforeMigration = k.lk.GetAllSyntheticLockupsByLockup(ctx, lockId) + + for _, synthLockBeforeMigration := range synthLocksBeforeMigration { + // Check if the synthetic lock is superfluid delegated or undelegating. + if strings.Contains(synthLockBeforeMigration.SynthDenom, "superbonding") { + isSuperfluidBonded = true + } + if strings.Contains(synthLockBeforeMigration.SynthDenom, "superunbonding") { + isSuperfluidUnbonding = true + } + } + + if isSuperfluidBonded && isSuperfluidUnbonding { + // This should never happen, but if it does, we don't support it. + return []lockuptypes.SyntheticLock{}, false, false, types.LockBothSuperfluidBondedAndUnbondingError{LockId: lockId} + } + + return synthLocksBeforeMigration, isSuperfluidBonded, isSuperfluidUnbonding, nil } -// prepareMigration prepares for the migration of gamm LP tokens from the Balancer pool to the Concentrated pool. It performs the following steps: +// validateMigration performs validation for the migration of gamm LP tokens from a Balancer pool to the canonical Concentrated pool. It performs the following steps: // // 1. Gets the pool ID of the Balancer pool from the gamm share denomination. // 2. Ensures a governance-sanctioned link exists between the Balancer pool and the Concentrated pool. -// 3. Validates that the lock corresponds to the sender, contains the correct denomination of LP shares, and retrieves the gamm shares from the lock. +// 3. Validates that the provided lock corresponds to the sender and contains the correct denomination of LP shares. // 4. Determines the remaining time on the lock. -// 5. Checks if the lock has a corresponding synthetic lock, indicating it is superfluid delegated or undelegating. // // The function returns the following values: // // poolIdLeaving: The ID of the balancer pool being migrated from. // poolIdEntering: The ID of the concentrated pool being migrated to. -// gammSharesInLock: The GAMM shares contained in the lock. -// concentratedPool: The concentrated pool that will be entered. // preMigrationLock: The original lock before migration. // remainingLockTime: The remaining time on the lock before it expires. -// synthLockBeforeMigration: The synthetic lock associated with the lock before migration, if any. -// isSuperfluidBonded: A boolean indicating if the lock is superfluid delegated. -// isSuperfluidUnbonding: A boolean indicating if the lock is superfluid undelegating. // err: An error, if any occurred. -func (k Keeper) prepareMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (poolIdLeaving, poolIdEntering uint64, concentratedPool cltypes.ConcentratedPoolExtension, preMigrationLock *lockuptypes.PeriodLock, remainingLockTime time.Duration, synthLockBeforeMigration []lockuptypes.SyntheticLock, isSuperfluidBonded, isSuperfluidUnbonding bool, err error) { +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) { + // Defense in depth, ensuring the sharesToMigrate contains gamm pool share prefix. + if !strings.HasPrefix(sharesToMigrate.Denom, gammtypes.GAMMTokenPrefix) { + return 0, 0, &lockuptypes.PeriodLock{}, 0, types.SharesToMigrateDenomPrefixError{Denom: sharesToMigrate.Denom, ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix} + } + // Get the balancer poolId by parsing the gamm share denom. - poolIdLeaving = gammtypes.MustGetPoolIdFromShareDenom(sharesToMigrate.Denom) + poolIdLeaving, err = gammtypes.GetPoolIdFromShareDenom(sharesToMigrate.Denom) + if err != nil { + return 0, 0, &lockuptypes.PeriodLock{}, 0, err + } - // Ensure a governance sanctioned link exists between the balancer pool and the concentrated pool. + // Ensure a governance sanctioned link exists between the balancer pool and a concentrated pool. poolIdEntering, err = k.gk.GetLinkedConcentratedPoolID(ctx, poolIdLeaving) if err != nil { - return 0, 0, nil, &lockuptypes.PeriodLock{}, 0, nil, false, false, err + return 0, 0, &lockuptypes.PeriodLock{}, 0, err } - // Get the concentrated pool from the provided ID and type cast it to ConcentratedPoolExtension. - concentratedPool, err = k.clk.GetPoolFromPoolIdAndConvertToConcentrated(ctx, poolIdEntering) + // Further defense in depth, ensuring that the pool ID we are entering can be type cased to a concentrated pool extension. + _, err = k.clk.GetPoolFromPoolIdAndConvertToConcentrated(ctx, poolIdEntering) if err != nil { - return 0, 0, nil, &lockuptypes.PeriodLock{}, 0, nil, false, false, err + return 0, 0, &lockuptypes.PeriodLock{}, 0, err } - // Check that lockID corresponds to sender, and contains correct denomination of LP shares. - preMigrationLock, err = k.validateLockForUnpool(ctx, sender, poolIdLeaving, lockId) + // Check that lockID corresponds to sender and that the denomination of LP shares corresponds to the poolId. + preMigrationLock, err = k.validateGammLockForSuperfluid(ctx, sender, poolIdLeaving, lockId) if err != nil { - return 0, 0, nil, &lockuptypes.PeriodLock{}, 0, nil, false, false, err + return 0, 0, &lockuptypes.PeriodLock{}, 0, err } // Before we break the lock, we must note the time remaining on the lock. - remainingLockTime = k.getExistingLockRemainingDuration(ctx, preMigrationLock) - - // Check if the lock has a corresponding synthetic lock. - // Synthetic lock existence implies that the lock is superfluid delegated or undelegating. - synthLockBeforeMigration = k.lk.GetAllSyntheticLockupsByLockup(ctx, lockId) - - isSuperfluidBonded = len(synthLockBeforeMigration) > 0 && strings.Contains(synthLockBeforeMigration[0].SynthDenom, "superbonding") - isSuperfluidUnbonding = len(synthLockBeforeMigration) > 0 && strings.Contains(synthLockBeforeMigration[0].SynthDenom, "superunbonding") - if isSuperfluidBonded && isSuperfluidUnbonding { - // This should never happen, but if it does, we don't support it. - return 0, 0, nil, &lockuptypes.PeriodLock{}, 0, nil, false, false, fmt.Errorf("synthetic lock %d must be either superfluid delegated or superfluid undelegating, not both", lockId) + remainingLockTime, err = k.getExistingLockRemainingDuration(ctx, preMigrationLock) + if err != nil { + return 0, 0, &lockuptypes.PeriodLock{}, 0, err } - return poolIdLeaving, poolIdEntering, concentratedPool, preMigrationLock, remainingLockTime, synthLockBeforeMigration, isSuperfluidBonded, isSuperfluidUnbonding, nil + 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: @@ -315,14 +317,9 @@ func (k Keeper) prepareMigration(ctx sdk.Context, sender sdk.AccAddress, lockId // 3. Ensures that the number of shares to migrate is less than or equal to the number of shares in the lock. // 4. Exits the position in the Balancer pool. // 5. Ensures that exactly two coins are returned. -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) { - // Finish unlocking directly for locked or unlocking locks - // This also breaks and deletes associated synthetic locks. - err = k.lk.ForceUnlock(ctx, *lock) - if err != nil { - return sdk.Coins{}, err - } - +// 6. Any remaining shares that were not migrated are re-locked 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, remainingLockTime time.Duration) (exitCoins sdk.Coins, remainingSharesLock lockuptypes.PeriodLock, err error) { + // validateMigration ensures that the preMigrationLock contains coins of length 1. gammSharesInLock := lock.Coins[0] // If shares to migrate is not specified, we migrate all shares. @@ -332,18 +329,37 @@ func (k Keeper) validateSharesToMigrateUnlockAndExitBalancerPool(ctx sdk.Context // Otherwise, we must ensure that the shares to migrate is less than or equal to the shares in the lock. if sharesToMigrate.Amount.GT(gammSharesInLock.Amount) { - return sdk.Coins{}, fmt.Errorf("shares to migrate must be less than or equal to shares in lock") + return sdk.Coins{}, lockuptypes.PeriodLock{}, types.MigrateMoreSharesThanLockHasError{SharesToMigrate: sharesToMigrate.Amount.String(), SharesInLock: gammSharesInLock.Amount.String()} + } + + // Determine if there will be any remaining gamm shares after migration. + remainingGammShares := gammSharesInLock.Sub(sharesToMigrate) + + // Finish unlocking directly for locked or unlocking locks + // This also breaks and deletes associated synthetic locks. + err = k.lk.ForceUnlock(ctx, *lock) + if err != nil { + return sdk.Coins{}, lockuptypes.PeriodLock{}, err } // Exit the balancer pool position. exitCoins, err = k.gk.ExitPool(ctx, sender, poolIdLeaving, sharesToMigrate.Amount, tokenOutMins) if err != nil { - return sdk.Coins{}, err + return sdk.Coins{}, lockuptypes.PeriodLock{}, err } // Defense in depth, ensuring we are returning exactly two coins. if len(exitCoins) != 2 { - return sdk.Coins{}, fmt.Errorf("Balancer pool must have exactly two tokens") + return sdk.Coins{}, lockuptypes.PeriodLock{}, types.TwoTokenBalancerPoolError{NumberOfTokens: len(exitCoins)} } - return exitCoins, nil + + // If there is a remainder of gamm shares, create a new lock with the remaining gamm shares for the remaining lock time. + if !remainingGammShares.IsZero() { + remainingSharesLock, err = k.lk.CreateLock(ctx, sender, sdk.NewCoins(remainingGammShares), remainingLockTime) + if err != nil { + return sdk.Coins{}, lockuptypes.PeriodLock{}, err + } + } + + return exitCoins, remainingSharesLock, nil } diff --git a/x/superfluid/keeper/migrate_test.go b/x/superfluid/keeper/migrate_test.go index 15c4592a127..e940cb68806 100644 --- a/x/superfluid/keeper/migrate_test.go +++ b/x/superfluid/keeper/migrate_test.go @@ -193,8 +193,6 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated( type sendTest struct { overwriteValidatorAddress bool overwriteLockId bool - overwriteShares bool - overwritePool bool percentOfSharesToMigrate sdk.Dec tokenOutMins sdk.Coins expectedError error @@ -206,6 +204,10 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated( "lock that is superfluid delegated, not unlocking (partial shares)": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("0.5"), }, + "error: migrate more shares than lock has": { + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1.1"), + expectedError: types.MigrateMoreSharesThanLockHasError{SharesToMigrate: "55000000000000000000", SharesInLock: "50000000000000000000"}, + }, "error: invalid validator address": { overwriteValidatorAddress: true, percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), @@ -216,16 +218,6 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated( percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), expectedError: lockuptypes.ErrLockupNotFound, }, - "error: attempt to migrate more shares than the lock has": { - overwriteShares: true, - percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), - expectedError: fmt.Errorf("shares to migrate must be less than or equal to shares in lock"), - }, - "error: pool has more than two assets": { - overwritePool: true, - percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), - expectedError: fmt.Errorf("Balancer pool must have exactly two tokens"), - }, "error: lock that is superfluid delegated, not unlocking (full shares), token out mins is more than exit coins": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), tokenOutMins: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(100000))), @@ -250,9 +242,12 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated( coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // PrepareMigration is called via the migration message router and is always run prior to the migration itself - poolIdLeaving, poolIdEntering, concentratedPool, preMigrationLock, remainingLockTime, synthLockBeforeMigration, _, _, err := superfluidKeeper.PrepareMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) + // 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, isSuperfluidBonded, isSuperfluidUnbonding, err := superfluidKeeper.RouteMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) suite.Require().NoError(err) + suite.Require().True(isSuperfluidBonded) + suite.Require().False(isSuperfluidUnbonding) // Modify migration inputs if necessary @@ -267,20 +262,8 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated( originalGammLockId = originalGammLockId + 1 } - if tc.overwriteShares { - coinsToMigrate.Amount = preMigrationLock.Coins[0].Amount.Add(sdk.NewInt(1)) - } - - if tc.overwritePool { - multiCoinBalancerPoolId := suite.PrepareBalancerPool() - poolIdLeaving = multiCoinBalancerPoolId - shareAmt := sdk.MustNewDecFromStr("50000000000000000000").TruncateInt() - newShares := sdk.NewCoin(fmt.Sprintf("gamm/pool/%d", multiCoinBalancerPoolId), shareAmt) - suite.FundAcc(poolJoinAcc, sdk.NewCoins(newShares)) - } - // System under test. - positionId, amount0, amount1, _, _, newGammLockId, concentratedLockId, err := superfluidKeeper.MigrateSuperfluidBondedBalancerToConcentrated(ctx, poolJoinAcc, poolIdLeaving, poolIdEntering, preMigrationLock, originalGammLockId, coinsToMigrate, synthLockBeforeMigration[0].SynthDenom, concentratedPool, remainingLockTime, tc.tokenOutMins) + positionId, amount0, amount1, _, _, newGammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, err := superfluidKeeper.MigrateSuperfluidBondedBalancerToConcentrated(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate, synthLockBeforeMigration[0].SynthDenom, tc.tokenOutMins) if tc.expectedError != nil { suite.Require().Error(err) suite.Require().ErrorContains(err, tc.expectedError.Error()) @@ -324,7 +307,7 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidBondedBalancerToConcentrated( } // Run slashing logic and check if the new and old locks are slashed. - suite.SlashAndValidateResult(ctx, newGammLockId, concentratedLockId, poolIdEntering, tc.percentOfSharesToMigrate, valAddr, *balancerLock, true) + suite.SlashAndValidateResult(ctx, newGammLockId, concentratedLockId, clPoolId, tc.percentOfSharesToMigrate, valAddr, *balancerLock, true) }) } } @@ -334,9 +317,6 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrat type sendTest struct { unlocking bool overwriteValidatorAddress bool - overwritePreMigrationLock bool - overwriteShares bool - overwritePool bool percentOfSharesToMigrate sdk.Dec tokenOutMins sdk.Coins expectedError error @@ -361,21 +341,6 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrat percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), expectedError: fmt.Errorf("decoding bech32 failed: invalid checksum"), }, - "error: non-existent pre migration lock": { - overwritePreMigrationLock: true, - percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), - expectedError: lockuptypes.ErrLockupNotFound, - }, - "error: attempt to migrate more shares than the lock has": { - overwriteShares: true, - percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), - expectedError: fmt.Errorf("shares to migrate must be less than or equal to shares in lock"), - }, - "error: pool has more than two assets": { - overwritePool: true, - percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), - expectedError: fmt.Errorf("Balancer pool must have exactly two tokens"), - }, "error: lock that is superfluid undelegating, not unlocking (full shares), token out mins is more than exit coins": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), tokenOutMins: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(100000))), @@ -399,9 +364,11 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrat coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // PrepareMigration is called via the migration message router and is always run prior to the migration itself - poolIdLeaving, poolIdEntering, concentratedPool, preMigrationLock, remainingLockTime, synthLockBeforeMigration, _, _, err := superfluidKeeper.PrepareMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) + // RouteMigration is called via the migration message router and is always run prior to the migration itself + synthLockBeforeMigration, isSuperfluidBonded, isSuperfluidUnbonding, err := superfluidKeeper.RouteMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) suite.Require().NoError(err) + suite.Require().False(isSuperfluidBonded) + suite.Require().True(isSuperfluidUnbonding) // Modify migration inputs if necessary @@ -412,24 +379,8 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrat synthLockBeforeMigration[0].SynthDenom = newSynthDenom } - if tc.overwritePreMigrationLock { - preMigrationLock.ID = preMigrationLock.ID + 1 - } - - if tc.overwriteShares { - coinsToMigrate.Amount = preMigrationLock.Coins[0].Amount.Add(sdk.NewInt(1)) - } - - if tc.overwritePool { - multiCoinBalancerPoolId := suite.PrepareBalancerPool() - poolIdLeaving = multiCoinBalancerPoolId - shareAmt := sdk.MustNewDecFromStr("50000000000000000000").TruncateInt() - newShares := sdk.NewCoin(fmt.Sprintf("gamm/pool/%d", multiCoinBalancerPoolId), shareAmt) - suite.FundAcc(poolJoinAcc, sdk.NewCoins(newShares)) - } - // System under test. - positionId, amount0, amount1, _, _, newGammLockId, concentratedLockId, err := superfluidKeeper.MigrateSuperfluidUnbondingBalancerToConcentrated(ctx, poolJoinAcc, poolIdLeaving, poolIdEntering, preMigrationLock, coinsToMigrate, synthLockBeforeMigration[0].SynthDenom, concentratedPool, remainingLockTime, tc.tokenOutMins) + positionId, amount0, amount1, _, _, newGammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, err := superfluidKeeper.MigrateSuperfluidUnbondingBalancerToConcentrated(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate, synthLockBeforeMigration[0].SynthDenom, tc.tokenOutMins) if tc.expectedError != nil { suite.Require().Error(err) suite.Require().ErrorContains(err, tc.expectedError.Error()) @@ -455,7 +406,7 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrat suite.Require().NoError(err) // Run slashing logic and check if the new and old locks are slashed. - suite.SlashAndValidateResult(ctx, newGammLockId, concentratedLockId, poolIdEntering, tc.percentOfSharesToMigrate, valAddr, *balancerLock, true) + suite.SlashAndValidateResult(ctx, newGammLockId, concentratedLockId, clPoolId, tc.percentOfSharesToMigrate, valAddr, *balancerLock, true) }) } } @@ -463,14 +414,10 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrat func (suite *KeeperTestSuite) TestMigrateNonSuperfluidLockBalancerToConcentrated() { defaultJoinTime := suite.Ctx.BlockTime() type sendTest struct { - unlocking bool - overwritePreMigrationLock bool - overwriteSender bool - overwriteShares bool - overwritePool bool - percentOfSharesToMigrate sdk.Dec - tokenOutMins sdk.Coins - expectedError error + unlocking bool + percentOfSharesToMigrate sdk.Dec + tokenOutMins sdk.Coins + expectedError error } testCases := map[string]sendTest{ "lock that is not superfluid delegated, not unlocking (full shares)": { @@ -487,21 +434,6 @@ func (suite *KeeperTestSuite) TestMigrateNonSuperfluidLockBalancerToConcentrated unlocking: true, percentOfSharesToMigrate: sdk.MustNewDecFromStr("0.6"), }, - "error: non-existent pre migration lock": { - overwritePreMigrationLock: true, - percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), - expectedError: lockuptypes.ErrLockupNotFound, - }, - "error: attempt to migrate more shares than the lock has": { - overwriteShares: true, - percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), - expectedError: fmt.Errorf("shares to migrate must be less than or equal to shares in lock"), - }, - "error: pool has more than two assets": { - overwritePool: true, - percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), - expectedError: fmt.Errorf("Balancer pool must have exactly two tokens"), - }, "error: lock that is not superfluid delegated, not unlocking (full shares), token out mins is more than exit coins": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), tokenOutMins: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(10000))), @@ -524,30 +456,15 @@ func (suite *KeeperTestSuite) TestMigrateNonSuperfluidLockBalancerToConcentrated coinsToMigrate := balancerPoolShareOut coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() - // PrepareMigration is called via the migration message router and is always run prior to the migration itself - poolIdLeaving, poolIdEntering, concentratedPool, preMigrationLock, remainingLockTime, _, _, _, err := superfluidKeeper.PrepareMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) + // RouteMigration is called via the migration message router and is always run prior to the migration itself + synthLockBeforeMigration, isSuperfluidBonded, isSuperfluidUnbonding, err := superfluidKeeper.RouteMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) suite.Require().NoError(err) - - // Modify migration inputs if necessary - - if tc.overwritePreMigrationLock { - preMigrationLock.ID = preMigrationLock.ID + 1 - } - - if tc.overwriteShares { - coinsToMigrate.Amount = preMigrationLock.Coins[0].Amount.Add(sdk.NewInt(1)) - } - - if tc.overwritePool { - multiCoinBalancerPoolId := suite.PrepareBalancerPool() - poolIdLeaving = multiCoinBalancerPoolId - shareAmt := sdk.MustNewDecFromStr("50000000000000000000").TruncateInt() - newShares := sdk.NewCoin(fmt.Sprintf("gamm/pool/%d", multiCoinBalancerPoolId), shareAmt) - suite.FundAcc(poolJoinAcc, sdk.NewCoins(newShares)) - } + suite.Require().Equal(0, len(synthLockBeforeMigration)) + suite.Require().False(isSuperfluidBonded) + suite.Require().False(isSuperfluidUnbonding) // System under test. - positionId, amount0, amount1, _, _, newGammLockId, concentratedLockId, err := superfluidKeeper.MigrateNonSuperfluidLockBalancerToConcentrated(ctx, poolJoinAcc, poolIdLeaving, poolIdEntering, preMigrationLock, coinsToMigrate, concentratedPool, remainingLockTime, tc.tokenOutMins) + positionId, amount0, amount1, _, _, newGammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, err := superfluidKeeper.MigrateNonSuperfluidLockBalancerToConcentrated(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate, tc.tokenOutMins) if tc.expectedError != nil { suite.Require().Error(err) suite.Require().ErrorContains(err, tc.expectedError.Error()) @@ -567,7 +484,143 @@ func (suite *KeeperTestSuite) TestMigrateNonSuperfluidLockBalancerToConcentrated ) // Run slashing logic and check if the new and old locks are not slashed. - suite.SlashAndValidateResult(ctx, newGammLockId, concentratedLockId, poolIdEntering, tc.percentOfSharesToMigrate, valAddr, *balancerLock, false) + suite.SlashAndValidateResult(ctx, newGammLockId, concentratedLockId, clPoolId, tc.percentOfSharesToMigrate, valAddr, *balancerLock, false) + }) + } +} + +func (suite *KeeperTestSuite) TestValidateMigration() { + defaultJoinTime := suite.Ctx.BlockTime() + type sendTest struct { + isSuperfluidDelegated bool + isSuperfluidUndelegating bool + unlocking bool + overwritePreMigrationLock bool + overwriteSender bool + overwriteSharesDenomValue string + overwriteLockId bool + percentOfSharesToMigrate sdk.Dec + tokenOutMins sdk.Coins + expectedError error + } + testCases := map[string]sendTest{ + "lock that is not superfluid delegated, not unlocking (full shares)": { + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + isSuperfluidDelegated: false, + isSuperfluidUndelegating: false, + }, + "lock that is not superfluid delegated, not unlocking (partial shares)": { + percentOfSharesToMigrate: sdk.MustNewDecFromStr("0.9"), + isSuperfluidDelegated: false, + isSuperfluidUndelegating: false, + }, + "lock that is not superfluid delegated, unlocking (full shares)": { + unlocking: true, + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + isSuperfluidDelegated: false, + isSuperfluidUndelegating: false, + }, + "lock that is not superfluid delegated, unlocking (partial shares)": { + unlocking: true, + percentOfSharesToMigrate: sdk.MustNewDecFromStr("0.6"), + isSuperfluidDelegated: false, + isSuperfluidUndelegating: false, + }, + "lock that is superfluid undelegating, not unlocking (full shares)": { + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + isSuperfluidDelegated: true, + isSuperfluidUndelegating: true, + }, + "lock that is superfluid undelegating, not unlocking (partial shares)": { + percentOfSharesToMigrate: sdk.MustNewDecFromStr("0.5"), + isSuperfluidDelegated: true, + isSuperfluidUndelegating: true, + }, + "lock that is superfluid undelegating, unlocking (full shares)": { + unlocking: true, + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + isSuperfluidDelegated: true, + isSuperfluidUndelegating: true, + }, + "lock that is superfluid undelegating, unlocking (partial shares)": { + unlocking: true, + percentOfSharesToMigrate: sdk.MustNewDecFromStr("0.3"), + isSuperfluidDelegated: true, + isSuperfluidUndelegating: true, + }, + "lock that is superfluid delegated, not unlocking (full shares)": { + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + isSuperfluidDelegated: true, + }, + "lock that is superfluid delegated, not unlocking (partial shares)": { + percentOfSharesToMigrate: sdk.MustNewDecFromStr("0.5"), + isSuperfluidDelegated: true, + }, + "error: denom prefix error": { + overwriteSharesDenomValue: "cl/pool/2", + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + expectedError: types.SharesToMigrateDenomPrefixError{Denom: "cl/pool/2", ExpectedDenomPrefix: gammtypes.GAMMTokenPrefix}, + }, + "error: no canonical link": { + overwriteSharesDenomValue: "gamm/pool/2", + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + expectedError: gammtypes.ConcentratedPoolMigrationLinkNotFoundError{PoolIdLeaving: 2}, + }, + "error: wrong sender": { + overwriteSender: true, + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + expectedError: lockuptypes.ErrNotLockOwner, + }, + "error: wrong lock ID": { + overwriteLockId: true, + percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), + expectedError: lockuptypes.ErrLockupNotFound, + }, + } + + for name, tc := range testCases { + suite.Run(name, func() { + suite.SetupTest() + suite.Ctx = suite.Ctx.WithBlockTime(defaultJoinTime) + ctx := suite.Ctx + superfluidKeeper := suite.App.SuperfluidKeeper + + // We bundle all migration setup into a single function to avoid repeating the same code for each test case. + // joinPoolAmt, _, balancerLock, _, poolJoinAcc, balancerPooId, clPoolId, balancerPoolShareOut, valAddr := suite.SetupMigrationTest(ctx, false, false, tc.unlocking, tc.percentOfSharesToMigrate) + _, _, balancerLock, _, poolJoinAcc, balancerPooId, clPoolId, balancerPoolShareOut, _ := suite.SetupMigrationTest(ctx, tc.isSuperfluidDelegated, tc.isSuperfluidUndelegating, tc.unlocking, tc.percentOfSharesToMigrate) + originalGammLockId := balancerLock.GetID() + + // Depending on the test case, we attempt to migrate a subset of the balancer LP tokens we originally created. + coinsToMigrate := balancerPoolShareOut + coinsToMigrate.Amount = coinsToMigrate.Amount.ToDec().Mul(tc.percentOfSharesToMigrate).RoundInt() + + // Modify migration inputs if necessary + if tc.overwriteSender { + poolJoinAcc = suite.TestAccs[0] + } + + if tc.overwriteLockId { + originalGammLockId = originalGammLockId + 10 + } + + if tc.overwriteSharesDenomValue != "" { + coinsToMigrate.Denom = tc.overwriteSharesDenomValue + } + + // System under test. + poolIdLeaving, poolIdEntering, preMigrationLock, remainingLockTime, err := superfluidKeeper.ValidateMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) + if tc.expectedError != nil { + suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.expectedError.Error()) + return + } + + suite.Require().NoError(err) + suite.Require().Equal(poolIdLeaving, balancerPooId) + suite.Require().Equal(poolIdEntering, clPoolId) + suite.Require().Equal(preMigrationLock.GetID(), originalGammLockId) + suite.Require().Equal(preMigrationLock.GetCoins(), sdk.NewCoins(balancerPoolShareOut)) + suite.Require().Equal(preMigrationLock.GetDuration(), remainingLockTime) }) } } @@ -600,7 +653,7 @@ func (suite *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPo "error: attempt to migrate more than lock has": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), overwriteShares: true, - expectedError: fmt.Errorf("shares to migrate must be less than or equal to shares in lock"), + expectedError: types.MigrateMoreSharesThanLockHasError{SharesToMigrate: "50000000000000000001", SharesInLock: "50000000000000000000"}, }, "error: attempt to leave a pool that does not exist": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), @@ -610,7 +663,7 @@ func (suite *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPo "error: attempt to leave a pool that has more than two denoms": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), overwritePool: true, - expectedError: fmt.Errorf("Balancer pool must have exactly two tokens"), + expectedError: types.TwoTokenBalancerPoolError{NumberOfTokens: 4}, }, "error: happy path (full shares), token out mins is more than exit coins": { percentOfSharesToMigrate: sdk.MustNewDecFromStr("1"), @@ -667,10 +720,11 @@ func (suite *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPo unbondingDuration := stakingKeeper.GetParams(ctx).UnbondingTime // Lock the LP tokens for the duration of the unbonding period. - originalGammLockId := suite.LockTokens(poolJoinAcc, sdk.NewCoins(coinsToMigrate), unbondingDuration) + originalGammLockId := suite.LockTokens(poolJoinAcc, sdk.NewCoins(balancerPoolShareOut), unbondingDuration) lock, err := lockupKeeper.GetLockByID(ctx, originalGammLockId) suite.Require().NoError(err) + preMigrationLock := *lock if tc.overwritePreMigrationLock { lock.ID = lock.ID + 1 @@ -693,7 +747,7 @@ func (suite *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPo } // System under test - exitCoins, err := superfluidKeeper.ValidateSharesToMigrateUnlockAndExitBalancerPool(ctx, poolJoinAcc, balancerPooId, lock, coinsToMigrate, tc.tokenOutMins) + exitCoins, remainingSharesLock, err := superfluidKeeper.ValidateSharesToMigrateUnlockAndExitBalancerPool(ctx, poolJoinAcc, balancerPooId, lock, coinsToMigrate, tc.tokenOutMins, lock.Duration) if tc.expectedError != nil { suite.Require().Error(err) suite.Require().ErrorContains(err, tc.expectedError.Error()) @@ -701,6 +755,14 @@ func (suite *KeeperTestSuite) TestValidateSharesToMigrateUnlockAndExitBalancerPo } suite.Require().NoError(err) + if tc.percentOfSharesToMigrate.Equal(sdk.OneDec()) { + suite.Require().Equal(lockuptypes.PeriodLock{}, remainingSharesLock) + } else { + suite.Require().Equal(preMigrationLock.Coins[0].Amount.Sub(sharesToMigrate), remainingSharesLock.Coins[0].Amount) + suite.Require().Equal(preMigrationLock.Coins[0].Denom, remainingSharesLock.Coins[0].Denom) + suite.Require().Equal(preMigrationLock.Duration, remainingSharesLock.Duration) + } + defaultErrorTolerance := osmomath.ErrTolerance{ AdditiveTolerance: sdk.NewDec(1), RoundingDir: osmomath.RoundDown, diff --git a/x/superfluid/keeper/msg_server.go b/x/superfluid/keeper/msg_server.go index 7b9a8339ebf..cded0c8eab4 100644 --- a/x/superfluid/keeper/msg_server.go +++ b/x/superfluid/keeper/msg_server.go @@ -132,38 +132,6 @@ func (server msgServer) LockAndSuperfluidDelegate(goCtx context.Context, msg *ty }, err } -func (server msgServer) CreateFullRangePositionAndSuperfluidDelegate(goCtx context.Context, msg *types.MsgCreateFullRangePositionAndSuperfluidDelegate) (*types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - address, err := sdk.AccAddressFromBech32(msg.Sender) - if err != nil { - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err - } - positionId, _, _, _, _, lockId, err := server.keeper.clk.CreateFullRangePositionLocked(ctx, msg.PoolId, address, msg.Coins, server.keeper.sk.GetParams(ctx).UnbondingTime) - if err != nil { - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err - } - - superfluidDelegateMsg := types.MsgSuperfluidDelegate{ - Sender: msg.Sender, - LockId: lockId, - ValAddr: msg.ValAddr, - } - - _, err = server.SuperfluidDelegate(goCtx, &superfluidDelegateMsg) - - if err != nil { - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err - } - - events.EmitCreateFullRangePositionAndSuperfluidDelegateEvent(ctx, lockId, positionId, msg.ValAddr) - - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{ - LockID: lockId, - PositionID: positionId, - }, nil -} - func (server msgServer) UnPoolWhitelistedPool(goCtx context.Context, msg *types.MsgUnPoolWhitelistedPool) (*types.MsgUnPoolWhitelistedPoolResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) @@ -197,6 +165,38 @@ func (server msgServer) UnPoolWhitelistedPool(goCtx context.Context, msg *types. return &types.MsgUnPoolWhitelistedPoolResponse{ExitedLockIds: allExitedLockIDs}, nil } +func (server msgServer) CreateFullRangePositionAndSuperfluidDelegate(goCtx context.Context, msg *types.MsgCreateFullRangePositionAndSuperfluidDelegate) (*types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + address, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err + } + positionId, _, _, _, _, lockId, err := server.keeper.clk.CreateFullRangePositionLocked(ctx, msg.PoolId, address, msg.Coins, server.keeper.sk.GetParams(ctx).UnbondingTime) + if err != nil { + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err + } + + superfluidDelegateMsg := types.MsgSuperfluidDelegate{ + Sender: msg.Sender, + LockId: lockId, + ValAddr: msg.ValAddr, + } + + _, err = server.SuperfluidDelegate(goCtx, &superfluidDelegateMsg) + + if err != nil { + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err + } + + events.EmitCreateFullRangePositionAndSuperfluidDelegateEvent(ctx, lockId, positionId, msg.ValAddr) + + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{ + LockID: lockId, + PositionID: positionId, + }, nil +} + func (server msgServer) UnlockAndMigrateSharesToFullRangeConcentratedPosition(goCtx context.Context, msg *types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) (*types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) diff --git a/x/superfluid/keeper/msg_server_test.go b/x/superfluid/keeper/msg_server_test.go index 2cf3795daae..c31e0072463 100644 --- a/x/superfluid/keeper/msg_server_test.go +++ b/x/superfluid/keeper/msg_server_test.go @@ -469,15 +469,6 @@ func (suite *KeeperTestSuite) TestMsgUnPoolWhitelistedPool_Event() { func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedPosition_Event() { suite.SetupTest() - const ( - token0Denom = "token0" - ) - - // Update authorized quote denoms with the quote denom relied on by the test - concentratedLiquidityParams := suite.App.ConcentratedLiquidityKeeper.GetParams(suite.Ctx) - concentratedLiquidityParams.AuthorizedQuoteDenoms = append(concentratedLiquidityParams.AuthorizedQuoteDenoms, token0Denom) - suite.App.ConcentratedLiquidityKeeper.SetParams(suite.Ctx, concentratedLiquidityParams) - msgServer := keeper.NewMsgServerImpl(suite.App.SuperfluidKeeper) suite.FundAcc(suite.TestAccs[0], defaultAcctFunds) fullRangeCoins := sdk.NewCoins(defaultPoolAssets[0].Token, defaultPoolAssets[1].Token) @@ -485,7 +476,7 @@ func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedP // Set validators valAddrs := suite.SetupValidators([]stakingtypes.BondStatus{stakingtypes.Bonded}) - // Set balancer pool and make its respective gamm share an authorized superfluid asset + // Set balancer pool (foo and stake) and make its respective gamm share an authorized superfluid asset msg := balancer.NewMsgCreateBalancerPool(suite.TestAccs[0], balancer.PoolParams{ SwapFee: sdk.NewDecWithPrec(1, 2), ExitFee: sdk.NewDec(0), @@ -500,7 +491,7 @@ func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedP }) suite.Require().NoError(err) - // Set concentrated pool with the same denoms as the balancer pool + // Set concentrated pool with the same denoms as the balancer pool (foo and stake) clPool := suite.PrepareCustomConcentratedPool(suite.TestAccs[0], defaultPoolAssets[0].Token.Denom, defaultPoolAssets[1].Token.Denom, 1, sdk.ZeroDec()) // Set migration link between the balancer and concentrated pool @@ -524,7 +515,8 @@ func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedP suite.Require().NoError(err) // Execute UnlockAndMigrateSharesToFullRangeConcentratedPosition message - sender, _ := sdk.AccAddressFromBech32(locks[0].Owner) + sender, err := sdk.AccAddressFromBech32(locks[0].Owner) + suite.Require().NoError(err) _, err = msgServer.UnlockAndMigrateSharesToFullRangeConcentratedPosition(sdk.WrapSDKContext(suite.Ctx), types.NewMsgUnlockAndMigrateSharesToFullRangeConcentratedPosition(sender, locks[0].ID, locks[0].Coins[0])) suite.Require().NoError(err) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index d643ce94c9d..64dfcb276fc 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -242,7 +242,7 @@ func (k Keeper) SuperfluidDelegate(ctx sdk.Context, sender string, lockID uint64 return k.mintOsmoTokensAndDelegate(ctx, amount, acc) } -// undelegateCommon is a helper function for SuperfluidUndelegate and SuperfluidUndelegateToConcentratedPosition. +// undelegateCommon is a helper function for SuperfluidUndelegate and superfluidUndelegateToConcentratedPosition. // It performs the following tasks: // - checks that the lock is valid for superfluid staking // - gets the intermediary account associated with the lock id @@ -300,10 +300,10 @@ func (k Keeper) SuperfluidUndelegate(ctx sdk.Context, sender string, lockID uint return k.createSyntheticLockup(ctx, lockID, intermediaryAcc, unlockingStatus) } -// SuperfluidUndelegateToConcentratedPosition starts undelegating superfluid delegated position for the given lock. It behaves similarly to SuperfluidUndelegate, +// superfluidUndelegateToConcentratedPosition starts undelegating superfluid delegated position for the given lock. It behaves similarly to SuperfluidUndelegate, // however it does not create a new synthetic lockup representing the unstaking side. This is because at the time this function is called, the new concentrated liquidity side // lock has not yet been created. Once the new cl side lock is created, the synthetic lockup representing the unstaking side is created. -func (k Keeper) SuperfluidUndelegateToConcentratedPosition(ctx sdk.Context, sender string, gammLockID uint64) (types.SuperfluidIntermediaryAccount, error) { +func (k Keeper) superfluidUndelegateToConcentratedPosition(ctx sdk.Context, sender string, gammLockID uint64) (types.SuperfluidIntermediaryAccount, error) { return k.undelegateCommon(ctx, sender, gammLockID) } diff --git a/x/superfluid/keeper/unpool.go b/x/superfluid/keeper/unpool.go index 0f3dfadc4b5..9f07c7d9284 100644 --- a/x/superfluid/keeper/unpool.go +++ b/x/superfluid/keeper/unpool.go @@ -33,14 +33,17 @@ func (k Keeper) UnpoolAllowedPools(ctx sdk.Context, sender sdk.AccAddress, poolI // 2) Consistency check that lockID corresponds to sender, and contains correct LP shares. // These are expected to be true by the caller, but good to double check // TODO: Try to minimize dependence on lock here - lock, err := k.validateLockForUnpool(ctx, sender, poolId, lockId) + lock, err := k.validateGammLockForSuperfluid(ctx, sender, poolId, lockId) if err != nil { return []uint64{}, err } gammSharesInLock := lock.Coins[0] // 3) Get remaining duration on the lock. Handle if the lock was unbonding. - lockRemainingDuration := k.getExistingLockRemainingDuration(ctx, lock) + lockRemainingDuration, err := k.getExistingLockRemainingDuration(ctx, lock) + if err != nil { + return []uint64{}, err + } // 4) If superfluid delegated, superfluid undelegate err = k.unbondSuperfluidIfExists(ctx, sender, lockId) @@ -99,39 +102,48 @@ func (k Keeper) checkUnpoolWhitelisted(ctx sdk.Context, poolId uint64) error { return types.ErrPoolNotWhitelisted } -// check if pool is whitelisted for unpool -func (k Keeper) validateLockForUnpool(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { +// validateGammLockForSuperfluid checks if the provided lock: +// 1) is owned by the provided sender +// 2) contains only 1 coin +// 3) contains the gamm LP shares associated with the provided poolId +func (k Keeper) validateGammLockForSuperfluid(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { lock, err := k.lk.GetLockByID(ctx, lockId) if err != nil { - return lock, err + return &lockuptypes.PeriodLock{}, err } - // consistency check: validate lock owner - // However, we expect this to be guaranteed by caller though. + // Validate lock owner. + // We expect this to be guaranteed by caller, though. if lock.Owner != sender.String() { - return lock, lockuptypes.ErrNotLockOwner + return &lockuptypes.PeriodLock{}, lockuptypes.ErrNotLockOwner } if lock.Coins.Len() != 1 { - return lock, types.ErrMultipleCoinsLockupNotSupported + return &lockuptypes.PeriodLock{}, types.ErrMultipleCoinsLockupNotSupported } gammShare := lock.Coins[0] if gammShare.Denom != gammtypes.GetPoolShareDenom(poolId) { - return lock, types.ErrLockUnpoolNotAllowed + return &lockuptypes.PeriodLock{}, types.UnexpectedDenomError{ExpectedDenom: gammtypes.GetPoolShareDenom(poolId), ProvidedDenom: gammShare.Denom} } return lock, nil } -func (k Keeper) getExistingLockRemainingDuration(ctx sdk.Context, lock *lockuptypes.PeriodLock) time.Duration { +// getExistingLockRemainingDuration returns the time remaining until the lock is finished unlocking. +// If the lock is not unlocking, then the duration field of the lock is returned. +func (k Keeper) getExistingLockRemainingDuration(ctx sdk.Context, lock *lockuptypes.PeriodLock) (time.Duration, error) { if lock.IsUnlocking() { - // lock is unlocking, so remaining duration equals lock.EndTime - ctx.BlockTime + // Lock is unlocking, so remaining duration equals lock.EndTime - ctx.BlockTime. remainingDuration := lock.EndTime.Sub(ctx.BlockTime()) - return remainingDuration + // Defense in depth, ensure the duration is not negative. + if remainingDuration < 0 { + return 0, types.NegativeDurationError{Duration: remainingDuration} + } + return remainingDuration, nil } - // lock is bonded, thus the time it should take to unlock is lock.Duration - return lock.Duration + // Lock is not unlocking, thus the time it should take to unlock is the locks duration. + return lock.Duration, nil } // TODO: Review this in more depth diff --git a/x/superfluid/keeper/unpool_test.go b/x/superfluid/keeper/unpool_test.go index 917d515c273..296f6a74d3e 100644 --- a/x/superfluid/keeper/unpool_test.go +++ b/x/superfluid/keeper/unpool_test.go @@ -1,14 +1,17 @@ package keeper_test import ( + "fmt" "time" + 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" "github.com/osmosis-labs/osmosis/v15/x/gamm/pool-models/balancer" gammtypes "github.com/osmosis-labs/osmosis/v15/x/gamm/types" + lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types" "github.com/osmosis-labs/osmosis/v15/x/superfluid/keeper" "github.com/osmosis-labs/osmosis/v15/x/superfluid/types" ) @@ -286,3 +289,146 @@ func (suite *KeeperTestSuite) TestUnpoolAllowedPools_WhiteList() { suite.Error(err) suite.Require().ErrorIs(err, types.ErrPoolNotWhitelisted) } + +func (suite *KeeperTestSuite) TestValidateGammLockForSuperfluid() { + lockCreator := suite.TestAccs[0] + nonLockCreator := suite.TestAccs[1] + type sendTest struct { + fundsToLock sdk.Coins + accountToValidate sdk.AccAddress + poolIdToValidate uint64 + lockIdToValidate uint64 + expectedError error + } + testCases := map[string]sendTest{ + "happy path": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + }, + "error: non-existent lock ID": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 2, + expectedError: errorsmod.Wrap(lockuptypes.ErrLockupNotFound, fmt.Sprintf("lock with ID %d does not exist", 2)), + }, + "error: mismatched owner": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: nonLockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + expectedError: lockuptypes.ErrNotLockOwner, + }, + "error: more than one coin in lock": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100)), + sdk.NewCoin("gamm/pool/2", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + expectedError: types.ErrMultipleCoinsLockupNotSupported, + }, + "error: wrong pool ID provided when compared to lock denom": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 2, + lockIdToValidate: 1, + expectedError: types.UnexpectedDenomError{ExpectedDenom: gammtypes.GetPoolShareDenom(2), ProvidedDenom: "gamm/pool/1"}, + }, + "error: right pool ID provided but not gamm/pool/ prefix": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("cl/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + expectedError: types.UnexpectedDenomError{ExpectedDenom: gammtypes.GetPoolShareDenom(1), ProvidedDenom: "cl/pool/1"}, + }, + } + + for name, tc := range testCases { + suite.Run(name, func() { + suite.SetupTest() + + ctx := suite.Ctx + superfluidKeeper := suite.App.SuperfluidKeeper + + suite.FundAcc(lockCreator, tc.fundsToLock) + _, err := suite.App.LockupKeeper.CreateLock(ctx, lockCreator, tc.fundsToLock, time.Hour) + suite.Require().NoError(err) + + // System under test + _, err = superfluidKeeper.ValidateGammLockForSuperfluid(ctx, tc.accountToValidate, tc.poolIdToValidate, tc.lockIdToValidate) + if tc.expectedError != nil { + suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.expectedError.Error()) + return + } + suite.Require().NoError(err) + }) + } +} + +func (suite *KeeperTestSuite) TestGetExistingLockRemainingDuration() { + defaultJoinTime := suite.Ctx.BlockTime() + lockCreator := suite.TestAccs[0] + type sendTest struct { + isUnlocking bool + lockDuration time.Duration + timePassed time.Duration + expectedRemainingDuration time.Duration + expectedError error + } + testCases := map[string]sendTest{ + "lock that is not unlocking": { + isUnlocking: false, + lockDuration: time.Hour, + timePassed: time.Hour, + expectedRemainingDuration: time.Hour, + }, + "lock that is unlocking": { + isUnlocking: true, + lockDuration: time.Hour, + timePassed: time.Minute, + expectedRemainingDuration: time.Hour - time.Minute, + }, + "error: negative duration": { + isUnlocking: true, + lockDuration: time.Hour, + timePassed: time.Hour + time.Minute, + expectedError: types.NegativeDurationError{Duration: -time.Minute}, + }, + } + + for name, tc := range testCases { + suite.Run(name, func() { + suite.SetupTest() + ctx := suite.Ctx.WithBlockTime(defaultJoinTime) + + superfluidKeeper := suite.App.SuperfluidKeeper + + suite.FundAcc(lockCreator, defaultAcctFunds) + lock, err := suite.App.LockupKeeper.CreateLock(ctx, lockCreator, defaultAcctFunds, tc.lockDuration) + suite.Require().NoError(err) + + if tc.isUnlocking { + _, err = suite.App.LockupKeeper.BeginUnlock(ctx, lock.ID, defaultAcctFunds) + suite.Require().NoError(err) + } + + ctx = ctx.WithBlockTime(defaultJoinTime.Add(tc.timePassed)) + + lockAfterTime, err := suite.App.LockupKeeper.GetLockByID(ctx, lock.ID) + suite.Require().NoError(err) + + // System under test + remainingDuration, err := superfluidKeeper.GetExistingLockRemainingDuration(ctx, lockAfterTime) + if tc.expectedError != nil { + suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.expectedError.Error()) + return + } + suite.Require().NoError(err) + suite.Require().Equal(tc.expectedRemainingDuration, remainingDuration) + }) + } +} diff --git a/x/superfluid/types/errors.go b/x/superfluid/types/errors.go index 45f8c886c8a..b15c59108c8 100644 --- a/x/superfluid/types/errors.go +++ b/x/superfluid/types/errors.go @@ -2,6 +2,7 @@ package types import ( fmt "fmt" + "time" errorsmod "cosmossdk.io/errors" ) @@ -51,3 +52,54 @@ type LockOwnerMismatchError struct { func (e LockOwnerMismatchError) Error() string { return fmt.Sprintf("lock ID %d owner %s does not match provided owner %s.", e.LockId, e.LockOwner, e.ProvidedOwner) } + +type SharesToMigrateDenomPrefixError struct { + Denom string + ExpectedDenomPrefix string +} + +func (e SharesToMigrateDenomPrefixError) Error() string { + return fmt.Sprintf("shares to migrate denom %s does not have expected prefix %s.", e.Denom, e.ExpectedDenomPrefix) +} + +type LockBothSuperfluidBondedAndUnbondingError struct { + LockId uint64 +} + +func (e LockBothSuperfluidBondedAndUnbondingError) Error() string { + return fmt.Sprintf("lock ID %d is both superfluid bonded and unbonding.", e.LockId) +} + +type NegativeDurationError struct { + Duration time.Duration +} + +func (e NegativeDurationError) Error() string { + return fmt.Sprintf("duration cannot be negative (%s)", e.Duration) +} + +type MigrateMoreSharesThanLockHasError struct { + SharesToMigrate string + SharesInLock string +} + +func (e MigrateMoreSharesThanLockHasError) Error() string { + return fmt.Sprintf("cannot migrate more shares (%s) than lock has (%s)", e.SharesToMigrate, e.SharesInLock) +} + +type TwoTokenBalancerPoolError struct { + NumberOfTokens int +} + +func (e TwoTokenBalancerPoolError) Error() string { + return fmt.Sprintf("balancer pool must have two tokens, got %d tokens", e.NumberOfTokens) +} + +type UnexpectedDenomError struct { + ExpectedDenom string + ProvidedDenom string +} + +func (e UnexpectedDenomError) Error() string { + return fmt.Sprintf("provided denom (%s) was expected to be formatted as follows: %s", e.ProvidedDenom, e.ExpectedDenom) +} diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index 786cc740f9b..ec958f97c3c 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -766,7 +766,7 @@ type MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition struct { LockId uint64 `protobuf:"varint,2,opt,name=lock_id,json=lockId,proto3" json:"lock_id,omitempty" yaml:"lock_id"` SharesToMigrate types.Coin `protobuf:"bytes,3,opt,name=shares_to_migrate,json=sharesToMigrate,proto3" json:"shares_to_migrate" yaml:"shares_to_migrate"` // token_out_mins indicates minimum token to exit Balancer pool with. - TokenOutMins []types.Coin `protobuf:"bytes,4,rep,name=token_out_mins,json=tokenOutMins,proto3" json:"token_out_mins" yaml:"token_out_min_amounts"` + TokenOutMins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=token_out_mins,json=tokenOutMins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"token_out_mins"` } func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) Reset() { @@ -827,7 +827,7 @@ func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) GetSharesToMi return types.Coin{} } -func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) GetTokenOutMins() []types.Coin { +func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) GetTokenOutMins() github_com_cosmos_cosmos_sdk_types.Coins { if m != nil { return m.TokenOutMins } @@ -1040,81 +1040,80 @@ func init() { func init() { proto.RegisterFile("osmosis/superfluid/tx.proto", fileDescriptor_55b645f187d22814) } var fileDescriptor_55b645f187d22814 = []byte{ - // 1173 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0x41, 0x4f, 0x1b, 0x47, - 0x14, 0x66, 0x6d, 0x17, 0x92, 0xa1, 0x90, 0xb0, 0x0d, 0x89, 0x71, 0x89, 0xd7, 0x99, 0x46, 0x11, - 0x55, 0xc8, 0x2e, 0x26, 0x4d, 0x40, 0x3d, 0x81, 0xb1, 0x5a, 0x39, 0xc5, 0x2a, 0xda, 0x80, 0x2a, - 0x45, 0xaa, 0x56, 0x6b, 0xcf, 0x64, 0xd9, 0xb2, 0xde, 0x71, 0x76, 0xc6, 0x14, 0xd4, 0x1f, 0x90, - 0x6b, 0xfe, 0x41, 0xef, 0x3d, 0xf4, 0x37, 0xf4, 0xd6, 0xa8, 0xa7, 0xdc, 0x5a, 0xb5, 0x92, 0x53, - 0xc1, 0x3f, 0xe0, 0xd8, 0x53, 0x35, 0xbb, 0xb3, 0xeb, 0x35, 0x78, 0x31, 0xeb, 0x90, 0x43, 0x4e, - 0xec, 0xcc, 0xbc, 0xf7, 0x7d, 0xef, 0xbd, 0x79, 0xef, 0xcd, 0xc3, 0xe0, 0x53, 0x42, 0x5b, 0x84, - 0xda, 0x54, 0xa3, 0x9d, 0x36, 0xf6, 0x9e, 0x3b, 0x1d, 0x1b, 0x69, 0xec, 0x40, 0x6d, 0x7b, 0x84, - 0x11, 0x59, 0x16, 0x87, 0x6a, 0xef, 0xb0, 0x70, 0xc3, 0x22, 0x16, 0xf1, 0x8f, 0x35, 0xfe, 0x15, - 0x48, 0x16, 0x8a, 0x16, 0x21, 0x96, 0x83, 0x35, 0x7f, 0xd5, 0xe8, 0x3c, 0xd7, 0x50, 0xc7, 0x33, - 0x99, 0x4d, 0xdc, 0xf0, 0xbc, 0xe9, 0x43, 0x69, 0x0d, 0x93, 0x62, 0x6d, 0xbf, 0xdc, 0xc0, 0xcc, - 0x2c, 0x6b, 0x4d, 0x62, 0x87, 0xe7, 0xca, 0x69, 0x7d, 0x66, 0xb7, 0x30, 0x65, 0x66, 0xab, 0x2d, - 0x04, 0x3e, 0x1b, 0x60, 0x67, 0xef, 0x33, 0x10, 0x82, 0xfb, 0x60, 0xb6, 0x4e, 0xad, 0xa7, 0xd1, - 0x76, 0x15, 0x3b, 0xd8, 0x32, 0x19, 0x96, 0x3f, 0x07, 0xe3, 0x14, 0xbb, 0x08, 0x7b, 0x79, 0xa9, - 0x24, 0x2d, 0x5c, 0xad, 0xcc, 0x9c, 0x74, 0x95, 0xa9, 0x43, 0xb3, 0xe5, 0x7c, 0x09, 0x83, 0x7d, - 0xa8, 0x0b, 0x01, 0xf9, 0x16, 0x98, 0x70, 0x48, 0x73, 0xcf, 0xb0, 0x51, 0x3e, 0x53, 0x92, 0x16, - 0x72, 0xfa, 0x38, 0x5f, 0xd6, 0x90, 0x3c, 0x07, 0xae, 0xec, 0x9b, 0x8e, 0x61, 0x22, 0xe4, 0xe5, - 0xb3, 0x1c, 0x45, 0x9f, 0xd8, 0x37, 0x9d, 0x75, 0x84, 0x3c, 0xa8, 0x80, 0xdb, 0x03, 0x79, 0x75, - 0x4c, 0xdb, 0xc4, 0xa5, 0x18, 0x7e, 0x0f, 0x6e, 0xf5, 0x09, 0xec, 0xb8, 0xe8, 0x12, 0x4d, 0x83, - 0x77, 0x80, 0x92, 0x00, 0x7f, 0x8e, 0x05, 0x0d, 0xe2, 0xa2, 0x4d, 0xd2, 0xdc, 0x7b, 0x4f, 0x16, - 0x84, 0xf0, 0x91, 0x05, 0xbf, 0x4a, 0xe0, 0x6e, 0x82, 0x95, 0xeb, 0xee, 0x25, 0xdb, 0x23, 0x57, - 0x40, 0x8e, 0x67, 0x97, 0x7f, 0x51, 0x93, 0xcb, 0x73, 0x6a, 0x90, 0x7e, 0x2a, 0x4f, 0x3f, 0x55, - 0xa4, 0x9f, 0xba, 0x41, 0x6c, 0xb7, 0xf2, 0xc9, 0xeb, 0xae, 0x32, 0x76, 0xd2, 0x55, 0x26, 0x03, - 0x02, 0xae, 0x04, 0x75, 0x5f, 0x17, 0x7e, 0x0d, 0x16, 0x2f, 0x62, 0x6f, 0xe8, 0x60, 0xdc, 0x18, - 0xa9, 0x2f, 0x38, 0xbf, 0x4b, 0x60, 0xbe, 0x4e, 0x2d, 0x2e, 0xbc, 0xee, 0xa2, 0x77, 0x4b, 0x4f, - 0x13, 0x7c, 0xc4, 0x8d, 0xa3, 0xf9, 0x4c, 0x29, 0x7b, 0xbe, 0x67, 0x4b, 0xdc, 0xb3, 0x5f, 0xde, - 0x2a, 0x0b, 0x96, 0xcd, 0x76, 0x3b, 0x0d, 0xb5, 0x49, 0x5a, 0x9a, 0xa8, 0xc2, 0xe0, 0xcf, 0x03, - 0x8a, 0xf6, 0x34, 0x76, 0xd8, 0xc6, 0xd4, 0x57, 0xa0, 0x7a, 0x80, 0x7c, 0x5e, 0xa2, 0x3f, 0xf6, - 0xaf, 0x30, 0xd1, 0x91, 0x28, 0x14, 0xd3, 0x20, 0x53, 0xab, 0x8a, 0x28, 0x64, 0x6a, 0x55, 0xf8, - 0x32, 0x03, 0xb4, 0x3a, 0xb5, 0x36, 0x3c, 0x6c, 0x32, 0xfc, 0x55, 0xc7, 0x71, 0x74, 0xd3, 0xb5, - 0xf0, 0x16, 0xa1, 0x36, 0xef, 0x11, 0x1f, 0x76, 0x50, 0xe4, 0xfb, 0x60, 0xa2, 0x4d, 0x88, 0xc3, - 0xef, 0x3d, 0xc7, 0x3d, 0xae, 0xc8, 0x27, 0x5d, 0x65, 0x3a, 0xb0, 0x54, 0x1c, 0x40, 0x7d, 0x9c, - 0x7f, 0xd5, 0x10, 0x7c, 0x01, 0x56, 0x52, 0x06, 0x22, 0x0a, 0xea, 0x4d, 0x10, 0x24, 0x54, 0xb5, - 0x2f, 0xbd, 0xaa, 0x72, 0x11, 0x80, 0xb6, 0x00, 0xa8, 0x55, 0x45, 0x1d, 0xc4, 0x76, 0xa0, 0x07, - 0xf2, 0x75, 0x6a, 0xed, 0xb8, 0x5b, 0x84, 0x38, 0xdf, 0xed, 0xda, 0x0c, 0x3b, 0x36, 0x65, 0x18, - 0xf1, 0x65, 0x9a, 0x20, 0xc7, 0xdc, 0xcc, 0x0c, 0x75, 0xf3, 0x09, 0x28, 0x25, 0x71, 0x46, 0xfe, - 0xdc, 0x03, 0xd7, 0xf0, 0x81, 0xcd, 0x30, 0x32, 0x44, 0xd9, 0xd0, 0xbc, 0x54, 0xca, 0x2e, 0xe4, - 0xf4, 0xa9, 0x60, 0x7b, 0xd3, 0xaf, 0x1e, 0x0a, 0x8f, 0x32, 0x60, 0xd5, 0x07, 0x73, 0x82, 0xbc, - 0xab, 0xdb, 0x96, 0x67, 0x32, 0xfc, 0x74, 0xd7, 0xf4, 0x30, 0xdd, 0x26, 0x51, 0x14, 0x37, 0x88, - 0xdb, 0xc4, 0x2e, 0xe3, 0x67, 0x28, 0x8c, 0x68, 0x4a, 0x07, 0xfb, 0x9a, 0x49, 0xdc, 0x41, 0x71, - 0x00, 0xa3, 0x06, 0x63, 0x81, 0x19, 0xea, 0x1b, 0x60, 0x30, 0x62, 0xb4, 0x02, 0x8b, 0x86, 0x77, - 0x9b, 0x92, 0xe8, 0x36, 0x79, 0x61, 0xc1, 0x69, 0x04, 0xa8, 0x5f, 0xa3, 0xc2, 0x2d, 0xe1, 0xa5, - 0x8c, 0xc1, 0x34, 0x23, 0x7b, 0xd8, 0x35, 0x48, 0x87, 0x19, 0x2d, 0x9e, 0xe4, 0xb9, 0x61, 0x49, - 0x7e, 0x57, 0xb0, 0xcc, 0x07, 0x2c, 0x7d, 0xea, 0x86, 0xd9, 0x22, 0x1d, 0x97, 0x51, 0xa8, 0x7f, - 0xec, 0xef, 0x7f, 0xdb, 0x61, 0x75, 0xdb, 0xa5, 0xf0, 0x8f, 0x2c, 0x58, 0x1b, 0x35, 0xc8, 0xd1, - 0x8d, 0x3e, 0x03, 0x13, 0x01, 0xfc, 0x92, 0x88, 0xf6, 0x1a, 0xb7, 0xe4, 0xef, 0xae, 0x72, 0xef, - 0x02, 0xe5, 0x56, 0x73, 0x59, 0x2f, 0xde, 0x02, 0x06, 0xea, 0x21, 0x60, 0x0f, 0xbb, 0xec, 0xdf, - 0xce, 0x3b, 0x63, 0x97, 0x23, 0xec, 0xb2, 0xfc, 0x23, 0x98, 0x71, 0xec, 0x17, 0x1d, 0x1b, 0xd9, - 0xec, 0xd0, 0x68, 0xfa, 0xb5, 0x89, 0x82, 0x2a, 0xaf, 0x3c, 0x49, 0xc1, 0x52, 0xc5, 0xcd, 0xde, - 0xdd, 0x9e, 0x01, 0x84, 0xfa, 0xf5, 0x68, 0x2f, 0xa8, 0x7f, 0x24, 0xef, 0x80, 0xab, 0x3f, 0x10, - 0xdb, 0x35, 0xf8, 0xb4, 0xe3, 0x37, 0x8f, 0xc9, 0xe5, 0x82, 0x1a, 0x8c, 0x42, 0x6a, 0x38, 0x0a, - 0xa9, 0xdb, 0xe1, 0x28, 0x54, 0x99, 0x17, 0x17, 0x7b, 0x3d, 0xa0, 0x88, 0x54, 0xe1, 0xab, 0xb7, - 0x8a, 0xa4, 0x5f, 0xe1, 0x6b, 0x2e, 0x0c, 0xff, 0x0c, 0xda, 0xed, 0x3a, 0x42, 0xdb, 0x24, 0x7e, - 0x61, 0x9b, 0x21, 0x7f, 0xaf, 0xcb, 0x44, 0x85, 0xb2, 0x02, 0x26, 0xc3, 0x9e, 0x11, 0xbd, 0x60, - 0x95, 0x9b, 0x27, 0x5d, 0x45, 0x0e, 0x4b, 0x3c, 0x3a, 0x84, 0xb1, 0xf6, 0x82, 0x62, 0x15, 0x96, - 0x19, 0x56, 0x61, 0x46, 0x98, 0xcb, 0x08, 0x53, 0xdb, 0xc3, 0x68, 0x69, 0x78, 0xc5, 0xdc, 0x16, - 0x2e, 0xcf, 0xc6, 0x73, 0x39, 0x54, 0x87, 0xfa, 0x94, 0xbf, 0x51, 0x15, 0xeb, 0x33, 0x04, 0x65, - 0x11, 0xd4, 0x11, 0x09, 0xca, 0xa7, 0x08, 0xca, 0x3c, 0xb2, 0x2b, 0x29, 0x23, 0x1b, 0x55, 0xc7, - 0xc8, 0x11, 0x8e, 0x95, 0x55, 0xe6, 0x3d, 0x96, 0x55, 0xf6, 0xb2, 0xcb, 0x2a, 0xd6, 0x50, 0x73, - 0xc3, 0x1a, 0xea, 0xf2, 0x7f, 0x00, 0x64, 0xeb, 0xd4, 0x92, 0x3d, 0x20, 0x0f, 0x1a, 0x06, 0xd4, - 0xb3, 0xff, 0x8a, 0xa8, 0x03, 0x67, 0xee, 0x42, 0xf9, 0xc2, 0xa2, 0xd1, 0xcd, 0x1c, 0x80, 0x1b, - 0x03, 0x67, 0xf3, 0xfb, 0x43, 0xa1, 0x7a, 0xc2, 0x85, 0x87, 0x29, 0x84, 0x93, 0x98, 0xa3, 0x19, - 0xf8, 0x22, 0xcc, 0xa1, 0xf0, 0x85, 0x98, 0xcf, 0x4c, 0xab, 0x3f, 0x4b, 0xe0, 0xce, 0xf0, 0x59, - 0x7c, 0x35, 0x85, 0x53, 0x7d, 0x9a, 0x85, 0xb5, 0x51, 0x35, 0x23, 0x0b, 0x5f, 0x4a, 0x60, 0x2e, - 0x79, 0x66, 0x5e, 0x4a, 0xc0, 0x4f, 0xd4, 0x28, 0xac, 0xa6, 0xd5, 0x88, 0x2c, 0xf9, 0x4d, 0x02, - 0x8b, 0xa9, 0x66, 0xd7, 0x8d, 0x04, 0xaa, 0x34, 0x20, 0x85, 0x6f, 0x2e, 0x01, 0x24, 0x72, 0xe1, - 0x27, 0x30, 0x3b, 0x78, 0x02, 0x5c, 0x4c, 0x60, 0x19, 0x28, 0x5d, 0xf8, 0x22, 0x8d, 0x74, 0x44, - 0xfe, 0x8f, 0x04, 0x1e, 0x8d, 0x36, 0xbe, 0x6d, 0x26, 0xf2, 0x8d, 0x80, 0x56, 0xd8, 0xbe, 0x4c, - 0xb4, 0xbe, 0xec, 0x48, 0xf5, 0xd4, 0x26, 0x65, 0x47, 0x1a, 0x90, 0xc4, 0xec, 0x18, 0xe5, 0x69, - 0xaa, 0x6c, 0xbd, 0x3e, 0x2a, 0x4a, 0x6f, 0x8e, 0x8a, 0xd2, 0xbf, 0x47, 0x45, 0xe9, 0xd5, 0x71, - 0x71, 0xec, 0xcd, 0x71, 0x71, 0xec, 0xaf, 0xe3, 0xe2, 0xd8, 0xb3, 0xc7, 0xb1, 0x67, 0x40, 0x10, - 0x3e, 0x70, 0xcc, 0x06, 0x0d, 0x17, 0xda, 0x7e, 0xf9, 0x91, 0x76, 0xd0, 0xf7, 0xeb, 0x11, 0x7f, - 0x1a, 0x1a, 0xe3, 0xfe, 0xf8, 0xf2, 0xf0, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x23, 0xf9, - 0xff, 0x60, 0x12, 0x00, 0x00, + // 1163 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0x41, 0x4f, 0x1b, 0xc7, + 0x17, 0x67, 0x6d, 0xff, 0x21, 0x19, 0xfe, 0x21, 0x61, 0x1b, 0x12, 0xe3, 0x26, 0x5e, 0x67, 0x5a, + 0x45, 0x54, 0x21, 0xbb, 0x98, 0x34, 0x01, 0xf5, 0x04, 0xc6, 0x6a, 0xe5, 0x14, 0xab, 0x68, 0x03, + 0xaa, 0x14, 0xa9, 0xb2, 0xd6, 0x9e, 0xc9, 0xb2, 0x65, 0xbd, 0x63, 0x76, 0xc6, 0x14, 0xd4, 0x0f, + 0x90, 0x6b, 0xbe, 0x41, 0xef, 0x3d, 0xf4, 0x33, 0xf4, 0xd6, 0xa8, 0xa7, 0xdc, 0x5a, 0xb5, 0x92, + 0x53, 0xc1, 0xbd, 0x07, 0x8e, 0x3d, 0x55, 0xb3, 0x3b, 0x3b, 0xb6, 0xc1, 0x8b, 0x59, 0xc7, 0x39, + 0xf4, 0xc4, 0xce, 0xcc, 0x7b, 0xbf, 0xf7, 0x7b, 0x6f, 0xde, 0x7b, 0xf3, 0x30, 0xf8, 0x90, 0xd0, + 0x26, 0xa1, 0x0e, 0x35, 0x68, 0xbb, 0x85, 0xfd, 0x17, 0x6e, 0xdb, 0x41, 0x06, 0x3b, 0xd4, 0x5b, + 0x3e, 0x61, 0x44, 0x55, 0xc5, 0xa1, 0xde, 0x3d, 0xcc, 0xdd, 0xb4, 0x89, 0x4d, 0x82, 0x63, 0x83, + 0x7f, 0x85, 0x92, 0xb9, 0xbc, 0x4d, 0x88, 0xed, 0x62, 0x23, 0x58, 0xd5, 0xdb, 0x2f, 0x0c, 0xd4, + 0xf6, 0x2d, 0xe6, 0x10, 0x2f, 0x3a, 0x6f, 0x04, 0x50, 0x46, 0xdd, 0xa2, 0xd8, 0x38, 0x28, 0xd6, + 0x31, 0xb3, 0x8a, 0x46, 0x83, 0x38, 0xd1, 0xb9, 0x76, 0x56, 0x9f, 0x39, 0x4d, 0x4c, 0x99, 0xd5, + 0x6c, 0x09, 0x81, 0x8f, 0x06, 0xf0, 0xec, 0x7e, 0x86, 0x42, 0xf0, 0x00, 0xcc, 0x55, 0xa9, 0xfd, + 0x4c, 0x6e, 0x97, 0xb1, 0x8b, 0x6d, 0x8b, 0x61, 0xf5, 0x13, 0x30, 0x49, 0xb1, 0x87, 0xb0, 0x9f, + 0x55, 0x0a, 0xca, 0xc2, 0xd5, 0xd2, 0xec, 0x69, 0x47, 0xbb, 0x76, 0x64, 0x35, 0xdd, 0xcf, 0x60, + 0xb8, 0x0f, 0x4d, 0x21, 0xa0, 0xde, 0x06, 0x53, 0x2e, 0x69, 0xec, 0xd5, 0x1c, 0x94, 0x4d, 0x15, + 0x94, 0x85, 0x8c, 0x39, 0xc9, 0x97, 0x15, 0xa4, 0xce, 0x83, 0x2b, 0x07, 0x96, 0x5b, 0xb3, 0x10, + 0xf2, 0xb3, 0x69, 0x8e, 0x62, 0x4e, 0x1d, 0x58, 0xee, 0x3a, 0x42, 0x3e, 0xd4, 0xc0, 0xdd, 0x81, + 0x76, 0x4d, 0x4c, 0x5b, 0xc4, 0xa3, 0x18, 0x7e, 0x03, 0x6e, 0xf7, 0x09, 0xec, 0x78, 0x68, 0x8c, + 0xd4, 0xe0, 0x3d, 0xa0, 0xc5, 0xc0, 0x5f, 0xc0, 0xa0, 0x4e, 0x3c, 0xb4, 0x49, 0x1a, 0x7b, 0xef, + 0x89, 0x41, 0x04, 0x2f, 0x19, 0xfc, 0xa4, 0x80, 0x8f, 0x63, 0x58, 0xae, 0x7b, 0x63, 0xe6, 0xa3, + 0x96, 0x40, 0x86, 0x67, 0x57, 0x70, 0x51, 0xd3, 0xcb, 0xf3, 0x7a, 0x98, 0x7e, 0x3a, 0x4f, 0x3f, + 0x5d, 0xa4, 0x9f, 0xbe, 0x41, 0x1c, 0xaf, 0xf4, 0xc1, 0xeb, 0x8e, 0x36, 0x71, 0xda, 0xd1, 0xa6, + 0x43, 0x03, 0x5c, 0x09, 0x9a, 0x81, 0x2e, 0xfc, 0x02, 0x2c, 0x5e, 0x86, 0x6f, 0xe4, 0x60, 0x2f, + 0x19, 0xa5, 0x2f, 0x38, 0xbf, 0x28, 0xe0, 0x4e, 0x95, 0xda, 0x5c, 0x78, 0xdd, 0x43, 0xef, 0x96, + 0x9e, 0x16, 0xf8, 0x1f, 0x27, 0x47, 0xb3, 0xa9, 0x42, 0xfa, 0x62, 0xcf, 0x96, 0xb8, 0x67, 0x3f, + 0xbe, 0xd5, 0x16, 0x6c, 0x87, 0xed, 0xb6, 0xeb, 0x7a, 0x83, 0x34, 0x0d, 0x51, 0x85, 0xe1, 0x9f, + 0x87, 0x14, 0xed, 0x19, 0xec, 0xa8, 0x85, 0x69, 0xa0, 0x40, 0xcd, 0x10, 0xf9, 0xa2, 0x44, 0x7f, + 0x12, 0x5c, 0x61, 0xac, 0x23, 0x32, 0x14, 0x33, 0x20, 0x55, 0x29, 0x8b, 0x28, 0xa4, 0x2a, 0x65, + 0xf8, 0x32, 0x05, 0x8c, 0x2a, 0xb5, 0x37, 0x7c, 0x6c, 0x31, 0xfc, 0x79, 0xdb, 0x75, 0x4d, 0xcb, + 0xb3, 0xf1, 0x16, 0xa1, 0x0e, 0xef, 0x11, 0xff, 0xed, 0xa0, 0xa8, 0x0f, 0xc0, 0x54, 0x8b, 0x10, + 0x97, 0xdf, 0x7b, 0x86, 0x7b, 0x5c, 0x52, 0x4f, 0x3b, 0xda, 0x4c, 0xc8, 0x54, 0x1c, 0x40, 0x73, + 0x92, 0x7f, 0x55, 0x10, 0xdc, 0x07, 0x2b, 0x09, 0x03, 0x21, 0x83, 0x7a, 0x0b, 0x84, 0x09, 0x55, + 0xee, 0x4b, 0xaf, 0xb2, 0x9a, 0x07, 0xa0, 0x25, 0x00, 0x2a, 0x65, 0x51, 0x07, 0x3d, 0x3b, 0xd0, + 0x07, 0xd9, 0x2a, 0xb5, 0x77, 0xbc, 0x2d, 0x42, 0xdc, 0xaf, 0x77, 0x1d, 0x86, 0x5d, 0x87, 0x32, + 0x8c, 0xf8, 0x32, 0x49, 0x90, 0x7b, 0xdc, 0x4c, 0x0d, 0x75, 0xf3, 0x29, 0x28, 0xc4, 0xd9, 0x94, + 0xfe, 0xdc, 0x07, 0xd7, 0xf1, 0xa1, 0xc3, 0x30, 0xaa, 0x89, 0xb2, 0xa1, 0x59, 0xa5, 0x90, 0x5e, + 0xc8, 0x98, 0xd7, 0xc2, 0xed, 0xcd, 0xa0, 0x7a, 0x28, 0xfc, 0x3b, 0x05, 0x56, 0x03, 0x30, 0x37, + 0xcc, 0xbb, 0xaa, 0x63, 0xfb, 0x16, 0xc3, 0xcf, 0x76, 0x2d, 0x1f, 0xd3, 0x6d, 0x22, 0xa3, 0xb8, + 0x41, 0xbc, 0x06, 0xf6, 0x18, 0x3f, 0x43, 0x51, 0x44, 0x13, 0x3a, 0xd8, 0xd7, 0x4c, 0x7a, 0x1d, + 0x14, 0x07, 0x50, 0x36, 0x18, 0x1b, 0xcc, 0xd2, 0x80, 0x40, 0x8d, 0x91, 0x5a, 0x33, 0x64, 0x34, + 0xbc, 0xdb, 0x14, 0x44, 0xb7, 0xc9, 0x0a, 0x06, 0x67, 0x11, 0xa0, 0x79, 0x9d, 0x0a, 0xb7, 0x84, + 0x97, 0xea, 0x3e, 0x98, 0x61, 0x64, 0x0f, 0x7b, 0x35, 0xd2, 0x66, 0xb5, 0x26, 0x4f, 0xf2, 0xcc, + 0xf8, 0x93, 0xfc, 0xff, 0x81, 0x89, 0xaf, 0xda, 0xac, 0xea, 0x78, 0x14, 0xfe, 0x9a, 0x06, 0x6b, + 0xa3, 0x06, 0x5c, 0xde, 0xee, 0x73, 0x30, 0x65, 0x35, 0x49, 0xdb, 0x63, 0x4b, 0x22, 0xf2, 0x6b, + 0x9c, 0xd5, 0x1f, 0x1d, 0xed, 0xfe, 0x25, 0x58, 0x55, 0x3c, 0xd6, 0x8d, 0xbd, 0x80, 0x81, 0x66, + 0x04, 0xd8, 0xc5, 0x2e, 0x06, 0x37, 0xf5, 0xce, 0xd8, 0x45, 0x89, 0x5d, 0x54, 0xbf, 0x03, 0xb3, + 0xae, 0xb3, 0xdf, 0x76, 0x90, 0xc3, 0x8e, 0x6a, 0x8d, 0xa0, 0x4e, 0x51, 0x58, 0xf1, 0xa5, 0xa7, + 0x09, 0xac, 0x94, 0x71, 0xa3, 0x7b, 0xcf, 0xe7, 0x00, 0xa1, 0x79, 0x43, 0xee, 0x85, 0xbd, 0x00, + 0xa9, 0x3b, 0xe0, 0xea, 0xb7, 0xc4, 0xf1, 0x6a, 0x7c, 0xf2, 0x09, 0x1a, 0xc9, 0xf4, 0x72, 0x4e, + 0x0f, 0xc7, 0x22, 0x3d, 0x1a, 0x8b, 0xf4, 0xed, 0x68, 0x2c, 0x2a, 0xdd, 0x11, 0xa9, 0x74, 0x23, + 0x34, 0x21, 0x55, 0xe1, 0xab, 0xb7, 0x9a, 0x62, 0x5e, 0xe1, 0x6b, 0x2e, 0x0c, 0x7f, 0x0b, 0x5b, + 0xef, 0x3a, 0x42, 0xdb, 0xa4, 0xf7, 0xc2, 0x36, 0x23, 0xfb, 0xdd, 0x8e, 0x23, 0x8b, 0x66, 0x05, + 0x4c, 0x47, 0xfd, 0x43, 0xbe, 0x66, 0xa5, 0x5b, 0xa7, 0x1d, 0x4d, 0x8d, 0xca, 0x5d, 0x1e, 0xc2, + 0x9e, 0x56, 0x83, 0x7a, 0xaa, 0x2d, 0x35, 0xac, 0xda, 0x6a, 0x51, 0x5e, 0x23, 0x4c, 0x1d, 0x1f, + 0xa3, 0xa5, 0xe1, 0xd5, 0x73, 0x57, 0xb8, 0x3c, 0x17, 0x22, 0xf6, 0xab, 0x43, 0xf3, 0x5a, 0xb0, + 0x51, 0x16, 0xeb, 0x73, 0x06, 0x8a, 0x22, 0xa8, 0x23, 0x1a, 0x28, 0x9e, 0x31, 0x50, 0xe4, 0x91, + 0x5d, 0x49, 0x18, 0x59, 0x59, 0x1d, 0x23, 0x47, 0xb8, 0xa7, 0xac, 0x52, 0xef, 0xb1, 0xac, 0xd2, + 0xe3, 0x2e, 0xab, 0x9e, 0xe6, 0x9a, 0x19, 0xd6, 0x5c, 0x97, 0xff, 0x01, 0x20, 0x5d, 0xa5, 0xb6, + 0xea, 0x03, 0x75, 0xd0, 0x60, 0xa0, 0x9f, 0xff, 0xb7, 0x44, 0x1f, 0x38, 0x7f, 0xe7, 0x8a, 0x97, + 0x16, 0x95, 0x37, 0x73, 0x08, 0x6e, 0x0e, 0x9c, 0xd3, 0x1f, 0x0c, 0x85, 0xea, 0x0a, 0xe7, 0x1e, + 0x25, 0x10, 0x8e, 0xb3, 0x2c, 0xe7, 0xe1, 0xcb, 0x58, 0x8e, 0x84, 0x2f, 0x65, 0xf9, 0xdc, 0xe4, + 0xfa, 0x83, 0x02, 0xee, 0x0d, 0x9f, 0xcb, 0x57, 0x13, 0x38, 0xd5, 0xa7, 0x99, 0x5b, 0x1b, 0x55, + 0x53, 0x32, 0x7c, 0xa9, 0x80, 0xf9, 0xf8, 0xf9, 0x79, 0x29, 0x06, 0x3f, 0x56, 0x23, 0xb7, 0x9a, + 0x54, 0x43, 0x32, 0xf9, 0x59, 0x01, 0x8b, 0x89, 0xe6, 0xd8, 0x8d, 0x18, 0x53, 0x49, 0x40, 0x72, + 0x5f, 0x8e, 0x01, 0x44, 0xba, 0xf0, 0x3d, 0x98, 0x1b, 0x3c, 0x0d, 0x2e, 0xc6, 0x58, 0x19, 0x28, + 0x9d, 0xfb, 0x34, 0x89, 0xb4, 0x34, 0xfe, 0xa7, 0x02, 0x1e, 0x8f, 0x36, 0xca, 0x6d, 0xc6, 0xda, + 0x1b, 0x01, 0x2d, 0xb7, 0x3d, 0x4e, 0xb4, 0xbe, 0xec, 0x48, 0xf4, 0xd4, 0xc6, 0x65, 0x47, 0x12, + 0x90, 0xd8, 0xec, 0x18, 0xe5, 0x69, 0x2a, 0x6d, 0xbd, 0x3e, 0xce, 0x2b, 0x6f, 0x8e, 0xf3, 0xca, + 0x5f, 0xc7, 0x79, 0xe5, 0xd5, 0x49, 0x7e, 0xe2, 0xcd, 0x49, 0x7e, 0xe2, 0xf7, 0x93, 0xfc, 0xc4, + 0xf3, 0x27, 0x3d, 0xcf, 0x80, 0x30, 0xf8, 0xd0, 0xb5, 0xea, 0x34, 0x5a, 0x18, 0x07, 0xc5, 0xc7, + 0xc6, 0x61, 0xdf, 0x2f, 0x49, 0xfc, 0x69, 0xa8, 0x4f, 0x06, 0xe3, 0xcb, 0xa3, 0x7f, 0x03, 0x00, + 0x00, 0xff, 0xff, 0x7d, 0x9f, 0xd6, 0x5f, 0x6c, 0x12, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. From 02b0341979c9e86f4d2413f2d4f8c9618bcb5a8b Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Thu, 11 May 2023 10:26:00 -0500 Subject: [PATCH 02/15] update readme --- x/superfluid/README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/x/superfluid/README.md b/x/superfluid/README.md index 8ff1f497b9a..b283ef7e493 100644 --- a/x/superfluid/README.md +++ b/x/superfluid/README.md @@ -376,6 +376,41 @@ It then mints concentrated liquidity shares and locks them up for the staking duration. From there, the normal superfluid delegation logic is executed. +### Create Full Range Position and Superfluid Delegate + +```{.go} +type MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition struct { + Sender string + LockId uint64 + SharesToMigrate sdk.Coin + TokenOutMins sdk.Coins +} +``` + +Upon completion, the following response is given: + +```{.go} +type MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse struct { + Amount0 string + Amount1 string + LiquidityCreated sdk.Dec + JoinTime time.Time +} +``` + +The message starts by determining which migration method to use. +- If underlying lock is superfluid bonded + - `migrateSuperfluidBondedBalancerToConcentrated` +- If underlying lock is superfluid unbonding + - `migrateSuperfluidUnbondingBalancerToConcentrated` +- If underlying lock is not superfluid bonded (vanilla lock) + - `migrateNonSuperfluidLockBalancerToConcentrated` + +It then routes to that migration message, which will migrate the gamm lock from +the previous state it was in, to the same state but in the concentrated pool. If +the sharesToMigrate is zero, then the entire lock is migrated. If only a subset +of the shares are migrated, then the remaining shares are left in the gamm pool. + ## Epochs Overall Epoch sequence From 01e44693805dbcd4dc27810286b64ed5b9290dfd Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Sat, 13 May 2023 04:01:32 +0200 Subject: [PATCH 03/15] Speedup accumulator prefix keys (#5174) --- osmoutils/accum/prefix.go | 13 +++---------- x/concentrated-liquidity/swaps.go | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/osmoutils/accum/prefix.go b/osmoutils/accum/prefix.go index 4dea6af8861..c207d215b6f 100644 --- a/osmoutils/accum/prefix.go +++ b/osmoutils/accum/prefix.go @@ -8,23 +8,16 @@ const ( positionPrefix = "pos" ) -// formatAccumPrefix returns the key prefix used for any -// accum module values to be stored in the KVStore. -// Returns "accum/{key}" as bytes. -func formatModulePrefixKey(key string) []byte { - return []byte(fmt.Sprintf("%s/%s", modulePrefix, key)) -} - // formatAccumPrefix returns the key prefix used // specifically for accumulator values in the KVStore. // Returns "accum/acc/{name}" as bytes. func formatAccumPrefixKey(name string) []byte { - return formatModulePrefixKey(fmt.Sprintf("%s/%s", accumulatorPrefix, name)) + return []byte(fmt.Sprintf(modulePrefix+"/"+accumulatorPrefix+"/%s", name)) } // FormatPositionPrefixKey returns the key prefix used // specifically for position values in the KVStore. -// Returns "accum/pos/{accumName}/{name}" as bytes. +// Returns "accum/acc/pos/{accumName}/{name}" as bytes. func FormatPositionPrefixKey(accumName, name string) []byte { - return formatAccumPrefixKey(fmt.Sprintf("%s/%s/%s", positionPrefix, accumName, name)) + return []byte(fmt.Sprintf(modulePrefix+"/"+accumulatorPrefix+"/"+positionPrefix+"/%s/%s", accumName, name)) } diff --git a/x/concentrated-liquidity/swaps.go b/x/concentrated-liquidity/swaps.go index 262cc45e2a9..17250d66cad 100644 --- a/x/concentrated-liquidity/swaps.go +++ b/x/concentrated-liquidity/swaps.go @@ -66,7 +66,7 @@ func (ss *SwapState) updateFeeGrowthGlobal(feeChargeTotal sdk.Dec) { // We round down here since we want to avoid overdistributing (the "fee charge" refers to // the total fees that will be accrued to the fee accumulator) feesAccruedPerUnitOfLiquidity := feeChargeTotal.QuoTruncate(ss.liquidity) - ss.feeGrowthGlobal = ss.feeGrowthGlobal.Add(feesAccruedPerUnitOfLiquidity) + ss.feeGrowthGlobal.AddMut(feesAccruedPerUnitOfLiquidity) return } } From f60b062569c048ac04b19f62163f4b4484d62a2f Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Sat, 13 May 2023 04:09:20 +0200 Subject: [PATCH 04/15] Speedup some of the swap logic by doing more pre-computation in tick math (#5171) --- x/concentrated-liquidity/math/precompute.go | 44 +++++++++++++++++++++ x/concentrated-liquidity/math/tick.go | 27 ++++++------- 2 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 x/concentrated-liquidity/math/precompute.go diff --git a/x/concentrated-liquidity/math/precompute.go b/x/concentrated-liquidity/math/precompute.go new file mode 100644 index 00000000000..7996ba3058e --- /dev/null +++ b/x/concentrated-liquidity/math/precompute.go @@ -0,0 +1,44 @@ +package math + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/osmosis/osmomath" +) + +var ( + sdkOneInt = sdk.OneInt() + sdkOneDec = sdk.NewDec(1) + sdkNineDec = sdk.NewDec(9) + sdkTenDec = sdk.NewDec(10) + powersOfTen []sdk.Dec + negPowersOfTen []sdk.Dec + + osmomathBigOneDec = osmomath.NewBigDec(1) + osmomathBigTenDec = osmomath.NewBigDec(10) + bigPowersOfTen []osmomath.BigDec + bigNegPowersOfTen []osmomath.BigDec +) + +// Set precision multipliers +func init() { + negPowersOfTen = make([]sdk.Dec, sdk.Precision+1) + for i := 0; i <= sdk.Precision; i++ { + negPowersOfTen[i] = sdkOneDec.Quo(sdkTenDec.Power(uint64(i))) + } + // 10^77 < sdk.MaxInt < 10^78 + powersOfTen = make([]sdk.Dec, 78) + for i := 0; i <= 77; i++ { + powersOfTen[i] = sdkTenDec.Power(uint64(i)) + } + + bigNegPowersOfTen = make([]osmomath.BigDec, osmomath.Precision+1) + for i := 0; i <= osmomath.Precision; i++ { + bigNegPowersOfTen[i] = osmomathBigOneDec.Quo(osmomathBigTenDec.PowerInteger(uint64(i))) + } + // 10^308 < osmomath.MaxInt < 10^309 + bigPowersOfTen = make([]osmomath.BigDec, 309) + for i := 0; i <= 308; i++ { + bigPowersOfTen[i] = osmomathBigTenDec.PowerInteger(uint64(i)) + } +} diff --git a/x/concentrated-liquidity/math/tick.go b/x/concentrated-liquidity/math/tick.go index d995211daed..5a1138a7243 100644 --- a/x/concentrated-liquidity/math/tick.go +++ b/x/concentrated-liquidity/math/tick.go @@ -9,11 +9,6 @@ import ( "github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/types" ) -var ( - sdkNineDec = sdk.NewDec(9) - sdkTenDec = sdk.NewDec(10) -) - // TicksToSqrtPrice returns the sqrtPrice for the lower and upper ticks by // individually calling `TickToSqrtPrice` method. // Returns error if fails to calculate price. @@ -77,7 +72,7 @@ func TickToPrice(tickIndex sdk.Int) (price sdk.Dec, err error) { if tickIndex.IsNegative() { // We must decrement the exponentAtCurrentTick when entering the negative tick range in order to constantly step up in precision when going further down in ticks // Otherwise, from tick 0 to tick -(geometricExponentIncrementDistanceInTicks), we would use the same exponent as the exponentAtPriceOne - exponentAtCurrentTick = exponentAtCurrentTick.Sub(sdk.OneInt()) + exponentAtCurrentTick = exponentAtCurrentTick.Sub(sdkOneInt) } // Knowing what our exponentAtCurrentTick is, we can then figure out what power of 10 this exponent corresponds to @@ -146,16 +141,16 @@ func PriceToTickRoundDown(price sdk.Dec, tickSpacing uint64) (sdk.Int, error) { // This is because the sdk.Dec.Power function does not support negative exponents func PowTenInternal(exponent sdk.Int) sdk.Dec { if exponent.GTE(sdk.ZeroInt()) { - return sdkTenDec.Power(exponent.Uint64()) + return powersOfTen[exponent.Int64()] } - return sdk.OneDec().Quo(sdkTenDec.Power(exponent.Abs().Uint64())) + return negPowersOfTen[-exponent.Int64()] } func powTenBigDec(exponent sdk.Int) osmomath.BigDec { if exponent.GTE(sdk.ZeroInt()) { - return osmomath.NewBigDec(10).PowerInteger(exponent.Uint64()) + return bigPowersOfTen[exponent.Int64()] } - return osmomath.OneDec().Quo(osmomath.NewBigDec(10).PowerInteger(exponent.Abs().Uint64())) + return bigNegPowersOfTen[-exponent.Int64()] } // CalculatePriceToTick takes in a price and returns the corresponding tick index. @@ -180,23 +175,23 @@ func CalculatePriceToTick(price sdk.Dec) (tickIndex sdk.Int) { // as well as how many ticks that corresponds to // In the opposite direction (price < 1), we do the same thing (just decrement the geometric exponent instead of incrementing). // The only difference is we must reduce the increment distance by a factor of 10. - if price.GT(sdk.OneDec()) { + if price.GT(sdkOneDec) { for currentPrice.LT(price) { currentAdditiveIncrementInTicks = powTenBigDec(exponentAtCurrentTick) maxPriceForCurrentAdditiveIncrementInTicks := osmomath.BigDecFromSDKDec(geometricExponentIncrementDistanceInTicks).Mul(currentAdditiveIncrementInTicks) - currentPrice = currentPrice.Add(maxPriceForCurrentAdditiveIncrementInTicks.SDKDec()) - exponentAtCurrentTick = exponentAtCurrentTick.Add(sdk.OneInt()) + currentPrice.AddMut(maxPriceForCurrentAdditiveIncrementInTicks.SDKDec()) + exponentAtCurrentTick = exponentAtCurrentTick.Add(sdkOneInt) ticksPassed = ticksPassed.Add(geometricExponentIncrementDistanceInTicks.TruncateInt()) } } else { // We must decrement the exponentAtCurrentTick by one when traversing negative ticks in order to constantly step up in precision when going further down in ticks // Otherwise, from tick 0 to tick -(geometricExponentIncrementDistanceInTicks), we would use the same exponent as the exponentAtPriceOne - exponentAtCurrentTick := exponentAtPriceOne.Sub(sdk.OneInt()) + exponentAtCurrentTick := exponentAtPriceOne.Sub(sdkOneInt) for currentPrice.GT(price) { currentAdditiveIncrementInTicks = powTenBigDec(exponentAtCurrentTick) maxPriceForCurrentAdditiveIncrementInTicks := osmomath.BigDecFromSDKDec(geometricExponentIncrementDistanceInTicks).Mul(currentAdditiveIncrementInTicks) - currentPrice = currentPrice.Sub(maxPriceForCurrentAdditiveIncrementInTicks.SDKDec()) - exponentAtCurrentTick = exponentAtCurrentTick.Sub(sdk.OneInt()) + currentPrice.SubMut(maxPriceForCurrentAdditiveIncrementInTicks.SDKDec()) + exponentAtCurrentTick = exponentAtCurrentTick.Sub(sdkOneInt) ticksPassed = ticksPassed.Sub(geometricExponentIncrementDistanceInTicks.TruncateInt()) } } From 13c81d83ef0d2cbb3d191a14b196569283c5d532 Mon Sep 17 00:00:00 2001 From: Ruslan Akhtariev <46343690+pysel@users.noreply.github.com> Date: Sat, 13 May 2023 12:07:46 +0700 Subject: [PATCH 05/15] osmomath: disable thelper for some helper functions (#5164) * mark helper functions * disable thelper warnings --- osmomath/decimal.go | 4 ++++ osmomath/int.go | 2 ++ 2 files changed, 6 insertions(+) diff --git a/osmomath/decimal.go b/osmomath/decimal.go index 03b6fea77bf..1b8e21ce001 100644 --- a/osmomath/decimal.go +++ b/osmomath/decimal.go @@ -887,12 +887,16 @@ func MaxDec(d1, d2 BigDec) BigDec { // DecEq returns true if two given decimals are equal. // Intended to be used with require/assert: require.True(t, DecEq(...)) +// +//nolint:thelper func DecEq(t *testing.T, exp, got BigDec) (*testing.T, bool, string, string, string) { return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String() } // DecApproxEq returns true if the differences between two given decimals are smaller than the tolerance range. // Intended to be used with require/assert: require.True(t, DecEq(...)) +// +//nolint:thelper func DecApproxEq(t *testing.T, d1 BigDec, d2 BigDec, tol BigDec) (*testing.T, bool, string, string, string) { diff := d1.Sub(d2).Abs() return t, diff.LTE(tol), "expected |d1 - d2| <:\t%v\ngot |d1 - d2| = \t\t%v", tol.String(), diff.String() diff --git a/osmomath/int.go b/osmomath/int.go index c3ae4ef288e..6b2fe80025d 100644 --- a/osmomath/int.go +++ b/osmomath/int.go @@ -435,6 +435,8 @@ func (i BigInt) MarshalAmino() ([]byte, error) { return i.Marshal() } func (i *BigInt) UnmarshalAmino(bz []byte) error { return i.Unmarshal(bz) } // intended to be used with require/assert: require.True(IntEq(...)) +// +//nolint:thelper func IntEq(t *testing.T, exp, got BigInt) (*testing.T, bool, string, string, string) { return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String() } From ca34bb85fdcb8556afb56face687428d2893b303 Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Sat, 13 May 2023 22:13:41 -0500 Subject: [PATCH 06/15] initial push audit changes --- go.sum | 2 - proto/osmosis/superfluid/tx.proto | 4 +- x/gamm/types/key.go | 5 +- x/superfluid/README.md | 35 ++++ x/superfluid/client/cli/tx.go | 9 ++ x/superfluid/keeper/concentrated_liquidity.go | 2 +- x/superfluid/keeper/export_test.go | 13 ++ x/superfluid/keeper/migrate.go | 9 +- x/superfluid/keeper/msg_server.go | 64 ++++---- x/superfluid/keeper/msg_server_test.go | 16 +- x/superfluid/keeper/stake.go | 6 +- x/superfluid/keeper/unpool.go | 42 +++-- x/superfluid/keeper/unpool_test.go | 146 +++++++++++++++++ x/superfluid/types/errors.go | 52 ++++++ x/superfluid/types/tx.pb.go | 153 +++++++++--------- 15 files changed, 408 insertions(+), 150 deletions(-) diff --git a/go.sum b/go.sum index b7dd8f85989..181ec5f621c 100644 --- a/go.sum +++ b/go.sum @@ -938,8 +938,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.20230503232557-ba905586c111 h1:1ahWbf9iF9sxDOjxrHWFaBGLE0nWFdpiX1pqObUaJO8= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230503232557-ba905586c111/go.mod h1:a7lhiXRpn8QJ21OhFpaEnUNErTSIafaYpp02q6uI/Dk= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230504190933-b174397f0bc5 h1:fBzTtgZHxvZkpwlg6YtAsNaexEHYaFZDXsYfPQWu9GE= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230504190933-b174397f0bc5/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae h1:I1Cy+GpTPWbVi0lBw9+bS1w42YfQjvXNK9bW4YbHhcs= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A= 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 a30df7a5299..f7860e388ec 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -150,8 +150,8 @@ message MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition { ]; // token_out_mins indicates minimum token to exit Balancer pool with. repeated cosmos.base.v1beta1.Coin token_out_mins = 4 [ - (gogoproto.moretags) = "yaml:\"token_out_min_amounts\"", - (gogoproto.nullable) = false + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" ]; } diff --git a/x/gamm/types/key.go b/x/gamm/types/key.go index ebfea87e42f..36a00421380 100644 --- a/x/gamm/types/key.go +++ b/x/gamm/types/key.go @@ -33,12 +33,11 @@ var ( ) func MustGetPoolIdFromShareDenom(denom string) uint64 { - numberStr := strings.TrimLeft(denom, GAMMTokenPrefix) - number, err := strconv.Atoi(numberStr) + number, err := GetPoolIdFromShareDenom(denom) if err != nil { panic(err) } - return uint64(number) + return number } func GetPoolIdFromShareDenom(denom string) (uint64, error) { diff --git a/x/superfluid/README.md b/x/superfluid/README.md index 8ff1f497b9a..b283ef7e493 100644 --- a/x/superfluid/README.md +++ b/x/superfluid/README.md @@ -376,6 +376,41 @@ It then mints concentrated liquidity shares and locks them up for the staking duration. From there, the normal superfluid delegation logic is executed. +### Create Full Range Position and Superfluid Delegate + +```{.go} +type MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition struct { + Sender string + LockId uint64 + SharesToMigrate sdk.Coin + TokenOutMins sdk.Coins +} +``` + +Upon completion, the following response is given: + +```{.go} +type MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse struct { + Amount0 string + Amount1 string + LiquidityCreated sdk.Dec + JoinTime time.Time +} +``` + +The message starts by determining which migration method to use. +- If underlying lock is superfluid bonded + - `migrateSuperfluidBondedBalancerToConcentrated` +- If underlying lock is superfluid unbonding + - `migrateSuperfluidUnbondingBalancerToConcentrated` +- If underlying lock is not superfluid bonded (vanilla lock) + - `migrateNonSuperfluidLockBalancerToConcentrated` + +It then routes to that migration message, which will migrate the gamm lock from +the previous state it was in, to the same state but in the concentrated pool. If +the sharesToMigrate is zero, then the entire lock is migrated. If only a subset +of the shares are migrated, then the remaining shares are left in the gamm pool. + ## Epochs Overall Epoch sequence diff --git a/x/superfluid/client/cli/tx.go b/x/superfluid/client/cli/tx.go index 6f7e38137dd..bb3f0de6496 100644 --- a/x/superfluid/client/cli/tx.go +++ b/x/superfluid/client/cli/tx.go @@ -37,6 +37,7 @@ func GetTxCmd() *cobra.Command { ) osmocli.AddTxCmd(cmd, NewCreateFullRangePositionAndSuperfluidDelegateCmd) osmocli.AddTxCmd(cmd, NewAddToConcentratedLiquiditySuperfluidPositionCmd) + osmocli.AddTxCmd(cmd, NewUnlockAndMigrateSharesToFullRangeConcentratedPositionCmd) return cmd } @@ -414,3 +415,11 @@ func NewAddToConcentratedLiquiditySuperfluidPositionCmd() (*osmocli.TxCliDesc, * Example: "add-to-superfluid-cl-position 10 1000000000uosmo 10000000uion", }, &types.MsgAddToConcentratedLiquiditySuperfluidPosition{} } + +func NewUnlockAndMigrateSharesToFullRangeConcentratedPositionCmd() (*osmocli.TxCliDesc, *types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) { + return &osmocli.TxCliDesc{ + Use: "unlock-and-migrate-to-cl [lock-id] [shares-to-migrate] [token-out-mins]", + Short: "unlock and migrate gamm shares to full range concentrated position", + Example: "unlock-and-migrate-cl 10 25000000000gamm/pool/2 1000000000uosmo,10000000uion", + }, &types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition{} +} diff --git a/x/superfluid/keeper/concentrated_liquidity.go b/x/superfluid/keeper/concentrated_liquidity.go index 0a2085831c8..bdba4278db9 100644 --- a/x/superfluid/keeper/concentrated_liquidity.go +++ b/x/superfluid/keeper/concentrated_liquidity.go @@ -69,7 +69,7 @@ func (k Keeper) addToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, ow // Superfluid undelegate the superfluid delegated position. // This deletes the connection between the lock and the intermediate account, deletes the synthetic lock, and burns the synthetic osmo. - intermediateAccount, err := k.SuperfluidUndelegateToConcentratedPosition(ctx, owner.String(), lockId) + intermediateAccount, err := k.superfluidUndelegateToConcentratedPosition(ctx, owner.String(), lockId) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err } diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 87c821c7cb5..37de1b46316 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -7,6 +7,7 @@ import ( cltypes "github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/types" lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types" + "github.com/osmosis-labs/osmosis/v15/x/superfluid/types" ) var ( @@ -45,3 +46,15 @@ func (k Keeper) PrepareMigration(ctx sdk.Context, sender sdk.AccAddress, lockId func (k Keeper) AddToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, owner sdk.AccAddress, positionId uint64, amount0Added, amount1Added sdk.Int) (uint64, sdk.Int, sdk.Int, sdk.Dec, uint64, error) { return k.addToConcentratedLiquiditySuperfluidPosition(ctx, owner, positionId, amount0Added, amount1Added) } + +func (k Keeper) SuperfluidUndelegateToConcentratedPosition(ctx sdk.Context, sender string, gammLockID uint64) (types.SuperfluidIntermediaryAccount, error) { + return k.superfluidUndelegateToConcentratedPosition(ctx, sender, gammLockID) +} + +func (k Keeper) ValidateGammLockForSuperfluid(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { + return k.validateGammLockForSuperfluid(ctx, sender, poolId, lockId) +} + +func (k Keeper) GetExistingLockRemainingDuration(ctx sdk.Context, lock *lockuptypes.PeriodLock) (time.Duration, error) { + return k.getExistingLockRemainingDuration(ctx, lock) +} diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index b9a5ea10f60..a529fdce987 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -66,7 +66,7 @@ func (k Keeper) migrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, // Superfluid undelegate the superfluid delegated position. // This deletes the connection between the lock and the intermediate account, deletes the synthetic lock, and burns the synthetic osmo. - intermediateAccount, err := k.SuperfluidUndelegateToConcentratedPosition(ctx, sender.String(), lockId) + intermediateAccount, err := k.superfluidUndelegateToConcentratedPosition(ctx, sender.String(), lockId) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err } @@ -286,13 +286,16 @@ func (k Keeper) prepareMigration(ctx sdk.Context, sender sdk.AccAddress, lockId } // Check that lockID corresponds to sender, and contains correct denomination of LP shares. - preMigrationLock, err = k.validateLockForUnpool(ctx, sender, poolIdLeaving, lockId) + preMigrationLock, err = k.validateGammLockForSuperfluid(ctx, sender, poolIdLeaving, lockId) if err != nil { return 0, 0, nil, &lockuptypes.PeriodLock{}, 0, nil, false, false, err } // Before we break the lock, we must note the time remaining on the lock. - remainingLockTime = k.getExistingLockRemainingDuration(ctx, preMigrationLock) + remainingLockTime, err = k.getExistingLockRemainingDuration(ctx, preMigrationLock) + if err != nil { + return 0, 0, nil, &lockuptypes.PeriodLock{}, 0, nil, false, false, err + } // Check if the lock has a corresponding synthetic lock. // Synthetic lock existence implies that the lock is superfluid delegated or undelegating. diff --git a/x/superfluid/keeper/msg_server.go b/x/superfluid/keeper/msg_server.go index 7b9a8339ebf..cded0c8eab4 100644 --- a/x/superfluid/keeper/msg_server.go +++ b/x/superfluid/keeper/msg_server.go @@ -132,38 +132,6 @@ func (server msgServer) LockAndSuperfluidDelegate(goCtx context.Context, msg *ty }, err } -func (server msgServer) CreateFullRangePositionAndSuperfluidDelegate(goCtx context.Context, msg *types.MsgCreateFullRangePositionAndSuperfluidDelegate) (*types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - address, err := sdk.AccAddressFromBech32(msg.Sender) - if err != nil { - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err - } - positionId, _, _, _, _, lockId, err := server.keeper.clk.CreateFullRangePositionLocked(ctx, msg.PoolId, address, msg.Coins, server.keeper.sk.GetParams(ctx).UnbondingTime) - if err != nil { - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err - } - - superfluidDelegateMsg := types.MsgSuperfluidDelegate{ - Sender: msg.Sender, - LockId: lockId, - ValAddr: msg.ValAddr, - } - - _, err = server.SuperfluidDelegate(goCtx, &superfluidDelegateMsg) - - if err != nil { - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err - } - - events.EmitCreateFullRangePositionAndSuperfluidDelegateEvent(ctx, lockId, positionId, msg.ValAddr) - - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{ - LockID: lockId, - PositionID: positionId, - }, nil -} - func (server msgServer) UnPoolWhitelistedPool(goCtx context.Context, msg *types.MsgUnPoolWhitelistedPool) (*types.MsgUnPoolWhitelistedPoolResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) @@ -197,6 +165,38 @@ func (server msgServer) UnPoolWhitelistedPool(goCtx context.Context, msg *types. return &types.MsgUnPoolWhitelistedPoolResponse{ExitedLockIds: allExitedLockIDs}, nil } +func (server msgServer) CreateFullRangePositionAndSuperfluidDelegate(goCtx context.Context, msg *types.MsgCreateFullRangePositionAndSuperfluidDelegate) (*types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + address, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err + } + positionId, _, _, _, _, lockId, err := server.keeper.clk.CreateFullRangePositionLocked(ctx, msg.PoolId, address, msg.Coins, server.keeper.sk.GetParams(ctx).UnbondingTime) + if err != nil { + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err + } + + superfluidDelegateMsg := types.MsgSuperfluidDelegate{ + Sender: msg.Sender, + LockId: lockId, + ValAddr: msg.ValAddr, + } + + _, err = server.SuperfluidDelegate(goCtx, &superfluidDelegateMsg) + + if err != nil { + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err + } + + events.EmitCreateFullRangePositionAndSuperfluidDelegateEvent(ctx, lockId, positionId, msg.ValAddr) + + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{ + LockID: lockId, + PositionID: positionId, + }, nil +} + func (server msgServer) UnlockAndMigrateSharesToFullRangeConcentratedPosition(goCtx context.Context, msg *types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) (*types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) diff --git a/x/superfluid/keeper/msg_server_test.go b/x/superfluid/keeper/msg_server_test.go index e7e329ee876..3f1da11de32 100644 --- a/x/superfluid/keeper/msg_server_test.go +++ b/x/superfluid/keeper/msg_server_test.go @@ -473,15 +473,6 @@ func (suite *KeeperTestSuite) TestMsgUnPoolWhitelistedPool_Event() { func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedPosition_Event() { suite.SetupTest() - const ( - token0Denom = "token0" - ) - - // Update authorized quote denoms with the quote denom relied on by the test - concentratedLiquidityParams := suite.App.ConcentratedLiquidityKeeper.GetParams(suite.Ctx) - concentratedLiquidityParams.AuthorizedQuoteDenoms = append(concentratedLiquidityParams.AuthorizedQuoteDenoms, token0Denom) - suite.App.ConcentratedLiquidityKeeper.SetParams(suite.Ctx, concentratedLiquidityParams) - msgServer := keeper.NewMsgServerImpl(suite.App.SuperfluidKeeper) suite.FundAcc(suite.TestAccs[0], defaultAcctFunds) fullRangeCoins := sdk.NewCoins(defaultPoolAssets[0].Token, defaultPoolAssets[1].Token) @@ -489,7 +480,7 @@ func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedP // Set validators valAddrs := suite.SetupValidators([]stakingtypes.BondStatus{stakingtypes.Bonded}) - // Set balancer pool and make its respective gamm share an authorized superfluid asset + // Set balancer pool (foo and stake) and make its respective gamm share an authorized superfluid asset msg := balancer.NewMsgCreateBalancerPool(suite.TestAccs[0], balancer.PoolParams{ SwapFee: sdk.NewDecWithPrec(1, 2), ExitFee: sdk.NewDec(0), @@ -505,7 +496,7 @@ func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedP }) suite.Require().NoError(err) - // Set concentrated pool with the same denoms as the balancer pool + // Set concentrated pool with the same denoms as the balancer pool (foo and stake) clPool := suite.PrepareCustomConcentratedPool(suite.TestAccs[0], defaultPoolAssets[0].Token.Denom, defaultPoolAssets[1].Token.Denom, 1, sdk.ZeroDec()) // Set migration link between the balancer and concentrated pool @@ -529,7 +520,8 @@ func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedP suite.Require().NoError(err) // Execute UnlockAndMigrateSharesToFullRangeConcentratedPosition message - sender, _ := sdk.AccAddressFromBech32(locks[0].Owner) + sender, err := sdk.AccAddressFromBech32(locks[0].Owner) + suite.Require().NoError(err) _, err = msgServer.UnlockAndMigrateSharesToFullRangeConcentratedPosition(sdk.WrapSDKContext(suite.Ctx), types.NewMsgUnlockAndMigrateSharesToFullRangeConcentratedPosition(sender, locks[0].ID, locks[0].Coins[0])) suite.Require().NoError(err) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index d643ce94c9d..64dfcb276fc 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -242,7 +242,7 @@ func (k Keeper) SuperfluidDelegate(ctx sdk.Context, sender string, lockID uint64 return k.mintOsmoTokensAndDelegate(ctx, amount, acc) } -// undelegateCommon is a helper function for SuperfluidUndelegate and SuperfluidUndelegateToConcentratedPosition. +// undelegateCommon is a helper function for SuperfluidUndelegate and superfluidUndelegateToConcentratedPosition. // It performs the following tasks: // - checks that the lock is valid for superfluid staking // - gets the intermediary account associated with the lock id @@ -300,10 +300,10 @@ func (k Keeper) SuperfluidUndelegate(ctx sdk.Context, sender string, lockID uint return k.createSyntheticLockup(ctx, lockID, intermediaryAcc, unlockingStatus) } -// SuperfluidUndelegateToConcentratedPosition starts undelegating superfluid delegated position for the given lock. It behaves similarly to SuperfluidUndelegate, +// superfluidUndelegateToConcentratedPosition starts undelegating superfluid delegated position for the given lock. It behaves similarly to SuperfluidUndelegate, // however it does not create a new synthetic lockup representing the unstaking side. This is because at the time this function is called, the new concentrated liquidity side // lock has not yet been created. Once the new cl side lock is created, the synthetic lockup representing the unstaking side is created. -func (k Keeper) SuperfluidUndelegateToConcentratedPosition(ctx sdk.Context, sender string, gammLockID uint64) (types.SuperfluidIntermediaryAccount, error) { +func (k Keeper) superfluidUndelegateToConcentratedPosition(ctx sdk.Context, sender string, gammLockID uint64) (types.SuperfluidIntermediaryAccount, error) { return k.undelegateCommon(ctx, sender, gammLockID) } diff --git a/x/superfluid/keeper/unpool.go b/x/superfluid/keeper/unpool.go index 0f3dfadc4b5..9f07c7d9284 100644 --- a/x/superfluid/keeper/unpool.go +++ b/x/superfluid/keeper/unpool.go @@ -33,14 +33,17 @@ func (k Keeper) UnpoolAllowedPools(ctx sdk.Context, sender sdk.AccAddress, poolI // 2) Consistency check that lockID corresponds to sender, and contains correct LP shares. // These are expected to be true by the caller, but good to double check // TODO: Try to minimize dependence on lock here - lock, err := k.validateLockForUnpool(ctx, sender, poolId, lockId) + lock, err := k.validateGammLockForSuperfluid(ctx, sender, poolId, lockId) if err != nil { return []uint64{}, err } gammSharesInLock := lock.Coins[0] // 3) Get remaining duration on the lock. Handle if the lock was unbonding. - lockRemainingDuration := k.getExistingLockRemainingDuration(ctx, lock) + lockRemainingDuration, err := k.getExistingLockRemainingDuration(ctx, lock) + if err != nil { + return []uint64{}, err + } // 4) If superfluid delegated, superfluid undelegate err = k.unbondSuperfluidIfExists(ctx, sender, lockId) @@ -99,39 +102,48 @@ func (k Keeper) checkUnpoolWhitelisted(ctx sdk.Context, poolId uint64) error { return types.ErrPoolNotWhitelisted } -// check if pool is whitelisted for unpool -func (k Keeper) validateLockForUnpool(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { +// validateGammLockForSuperfluid checks if the provided lock: +// 1) is owned by the provided sender +// 2) contains only 1 coin +// 3) contains the gamm LP shares associated with the provided poolId +func (k Keeper) validateGammLockForSuperfluid(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { lock, err := k.lk.GetLockByID(ctx, lockId) if err != nil { - return lock, err + return &lockuptypes.PeriodLock{}, err } - // consistency check: validate lock owner - // However, we expect this to be guaranteed by caller though. + // Validate lock owner. + // We expect this to be guaranteed by caller, though. if lock.Owner != sender.String() { - return lock, lockuptypes.ErrNotLockOwner + return &lockuptypes.PeriodLock{}, lockuptypes.ErrNotLockOwner } if lock.Coins.Len() != 1 { - return lock, types.ErrMultipleCoinsLockupNotSupported + return &lockuptypes.PeriodLock{}, types.ErrMultipleCoinsLockupNotSupported } gammShare := lock.Coins[0] if gammShare.Denom != gammtypes.GetPoolShareDenom(poolId) { - return lock, types.ErrLockUnpoolNotAllowed + return &lockuptypes.PeriodLock{}, types.UnexpectedDenomError{ExpectedDenom: gammtypes.GetPoolShareDenom(poolId), ProvidedDenom: gammShare.Denom} } return lock, nil } -func (k Keeper) getExistingLockRemainingDuration(ctx sdk.Context, lock *lockuptypes.PeriodLock) time.Duration { +// getExistingLockRemainingDuration returns the time remaining until the lock is finished unlocking. +// If the lock is not unlocking, then the duration field of the lock is returned. +func (k Keeper) getExistingLockRemainingDuration(ctx sdk.Context, lock *lockuptypes.PeriodLock) (time.Duration, error) { if lock.IsUnlocking() { - // lock is unlocking, so remaining duration equals lock.EndTime - ctx.BlockTime + // Lock is unlocking, so remaining duration equals lock.EndTime - ctx.BlockTime. remainingDuration := lock.EndTime.Sub(ctx.BlockTime()) - return remainingDuration + // Defense in depth, ensure the duration is not negative. + if remainingDuration < 0 { + return 0, types.NegativeDurationError{Duration: remainingDuration} + } + return remainingDuration, nil } - // lock is bonded, thus the time it should take to unlock is lock.Duration - return lock.Duration + // Lock is not unlocking, thus the time it should take to unlock is the locks duration. + return lock.Duration, nil } // TODO: Review this in more depth diff --git a/x/superfluid/keeper/unpool_test.go b/x/superfluid/keeper/unpool_test.go index 19433dab95f..a6d8a58cd33 100644 --- a/x/superfluid/keeper/unpool_test.go +++ b/x/superfluid/keeper/unpool_test.go @@ -1,14 +1,17 @@ package keeper_test import ( + "fmt" "time" + 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" "github.com/osmosis-labs/osmosis/v15/x/gamm/pool-models/balancer" gammtypes "github.com/osmosis-labs/osmosis/v15/x/gamm/types" + lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types" "github.com/osmosis-labs/osmosis/v15/x/superfluid/keeper" "github.com/osmosis-labs/osmosis/v15/x/superfluid/types" ) @@ -280,3 +283,146 @@ func (suite *KeeperTestSuite) TestUnpoolAllowedPools_WhiteList() { suite.Error(err) suite.Require().ErrorIs(err, types.ErrPoolNotWhitelisted) } + +func (suite *KeeperTestSuite) TestValidateGammLockForSuperfluid() { + lockCreator := suite.TestAccs[0] + nonLockCreator := suite.TestAccs[1] + type sendTest struct { + fundsToLock sdk.Coins + accountToValidate sdk.AccAddress + poolIdToValidate uint64 + lockIdToValidate uint64 + expectedError error + } + testCases := map[string]sendTest{ + "happy path": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + }, + "error: non-existent lock ID": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 2, + expectedError: errorsmod.Wrap(lockuptypes.ErrLockupNotFound, fmt.Sprintf("lock with ID %d does not exist", 2)), + }, + "error: mismatched owner": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: nonLockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + expectedError: lockuptypes.ErrNotLockOwner, + }, + "error: more than one coin in lock": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100)), + sdk.NewCoin("gamm/pool/2", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + expectedError: types.ErrMultipleCoinsLockupNotSupported, + }, + "error: wrong pool ID provided when compared to lock denom": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 2, + lockIdToValidate: 1, + expectedError: types.UnexpectedDenomError{ExpectedDenom: gammtypes.GetPoolShareDenom(2), ProvidedDenom: "gamm/pool/1"}, + }, + "error: right pool ID provided but not gamm/pool/ prefix": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("cl/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + expectedError: types.UnexpectedDenomError{ExpectedDenom: gammtypes.GetPoolShareDenom(1), ProvidedDenom: "cl/pool/1"}, + }, + } + + for name, tc := range testCases { + suite.Run(name, func() { + suite.SetupTest() + + ctx := suite.Ctx + superfluidKeeper := suite.App.SuperfluidKeeper + + suite.FundAcc(lockCreator, tc.fundsToLock) + _, err := suite.App.LockupKeeper.CreateLock(ctx, lockCreator, tc.fundsToLock, time.Hour) + suite.Require().NoError(err) + + // System under test + _, err = superfluidKeeper.ValidateGammLockForSuperfluid(ctx, tc.accountToValidate, tc.poolIdToValidate, tc.lockIdToValidate) + if tc.expectedError != nil { + suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.expectedError.Error()) + return + } + suite.Require().NoError(err) + }) + } +} + +func (suite *KeeperTestSuite) TestGetExistingLockRemainingDuration() { + defaultJoinTime := suite.Ctx.BlockTime() + lockCreator := suite.TestAccs[0] + type sendTest struct { + isUnlocking bool + lockDuration time.Duration + timePassed time.Duration + expectedRemainingDuration time.Duration + expectedError error + } + testCases := map[string]sendTest{ + "lock that is not unlocking": { + isUnlocking: false, + lockDuration: time.Hour, + timePassed: time.Hour, + expectedRemainingDuration: time.Hour, + }, + "lock that is unlocking": { + isUnlocking: true, + lockDuration: time.Hour, + timePassed: time.Minute, + expectedRemainingDuration: time.Hour - time.Minute, + }, + "error: negative duration": { + isUnlocking: true, + lockDuration: time.Hour, + timePassed: time.Hour + time.Minute, + expectedError: types.NegativeDurationError{Duration: -time.Minute}, + }, + } + + for name, tc := range testCases { + suite.Run(name, func() { + suite.SetupTest() + ctx := suite.Ctx.WithBlockTime(defaultJoinTime) + + superfluidKeeper := suite.App.SuperfluidKeeper + + suite.FundAcc(lockCreator, defaultAcctFunds) + lock, err := suite.App.LockupKeeper.CreateLock(ctx, lockCreator, defaultAcctFunds, tc.lockDuration) + suite.Require().NoError(err) + + if tc.isUnlocking { + _, err = suite.App.LockupKeeper.BeginUnlock(ctx, lock.ID, defaultAcctFunds) + suite.Require().NoError(err) + } + + ctx = ctx.WithBlockTime(defaultJoinTime.Add(tc.timePassed)) + + lockAfterTime, err := suite.App.LockupKeeper.GetLockByID(ctx, lock.ID) + suite.Require().NoError(err) + + // System under test + remainingDuration, err := superfluidKeeper.GetExistingLockRemainingDuration(ctx, lockAfterTime) + if tc.expectedError != nil { + suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.expectedError.Error()) + return + } + suite.Require().NoError(err) + suite.Require().Equal(tc.expectedRemainingDuration, remainingDuration) + }) + } +} diff --git a/x/superfluid/types/errors.go b/x/superfluid/types/errors.go index 45f8c886c8a..b15c59108c8 100644 --- a/x/superfluid/types/errors.go +++ b/x/superfluid/types/errors.go @@ -2,6 +2,7 @@ package types import ( fmt "fmt" + "time" errorsmod "cosmossdk.io/errors" ) @@ -51,3 +52,54 @@ type LockOwnerMismatchError struct { func (e LockOwnerMismatchError) Error() string { return fmt.Sprintf("lock ID %d owner %s does not match provided owner %s.", e.LockId, e.LockOwner, e.ProvidedOwner) } + +type SharesToMigrateDenomPrefixError struct { + Denom string + ExpectedDenomPrefix string +} + +func (e SharesToMigrateDenomPrefixError) Error() string { + return fmt.Sprintf("shares to migrate denom %s does not have expected prefix %s.", e.Denom, e.ExpectedDenomPrefix) +} + +type LockBothSuperfluidBondedAndUnbondingError struct { + LockId uint64 +} + +func (e LockBothSuperfluidBondedAndUnbondingError) Error() string { + return fmt.Sprintf("lock ID %d is both superfluid bonded and unbonding.", e.LockId) +} + +type NegativeDurationError struct { + Duration time.Duration +} + +func (e NegativeDurationError) Error() string { + return fmt.Sprintf("duration cannot be negative (%s)", e.Duration) +} + +type MigrateMoreSharesThanLockHasError struct { + SharesToMigrate string + SharesInLock string +} + +func (e MigrateMoreSharesThanLockHasError) Error() string { + return fmt.Sprintf("cannot migrate more shares (%s) than lock has (%s)", e.SharesToMigrate, e.SharesInLock) +} + +type TwoTokenBalancerPoolError struct { + NumberOfTokens int +} + +func (e TwoTokenBalancerPoolError) Error() string { + return fmt.Sprintf("balancer pool must have two tokens, got %d tokens", e.NumberOfTokens) +} + +type UnexpectedDenomError struct { + ExpectedDenom string + ProvidedDenom string +} + +func (e UnexpectedDenomError) Error() string { + return fmt.Sprintf("provided denom (%s) was expected to be formatted as follows: %s", e.ProvidedDenom, e.ExpectedDenom) +} diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index 786cc740f9b..ec958f97c3c 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -766,7 +766,7 @@ type MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition struct { LockId uint64 `protobuf:"varint,2,opt,name=lock_id,json=lockId,proto3" json:"lock_id,omitempty" yaml:"lock_id"` SharesToMigrate types.Coin `protobuf:"bytes,3,opt,name=shares_to_migrate,json=sharesToMigrate,proto3" json:"shares_to_migrate" yaml:"shares_to_migrate"` // token_out_mins indicates minimum token to exit Balancer pool with. - TokenOutMins []types.Coin `protobuf:"bytes,4,rep,name=token_out_mins,json=tokenOutMins,proto3" json:"token_out_mins" yaml:"token_out_min_amounts"` + TokenOutMins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=token_out_mins,json=tokenOutMins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"token_out_mins"` } func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) Reset() { @@ -827,7 +827,7 @@ func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) GetSharesToMi return types.Coin{} } -func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) GetTokenOutMins() []types.Coin { +func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) GetTokenOutMins() github_com_cosmos_cosmos_sdk_types.Coins { if m != nil { return m.TokenOutMins } @@ -1040,81 +1040,80 @@ func init() { func init() { proto.RegisterFile("osmosis/superfluid/tx.proto", fileDescriptor_55b645f187d22814) } var fileDescriptor_55b645f187d22814 = []byte{ - // 1173 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0x41, 0x4f, 0x1b, 0x47, - 0x14, 0x66, 0x6d, 0x17, 0x92, 0xa1, 0x90, 0xb0, 0x0d, 0x89, 0x71, 0x89, 0xd7, 0x99, 0x46, 0x11, - 0x55, 0xc8, 0x2e, 0x26, 0x4d, 0x40, 0x3d, 0x81, 0xb1, 0x5a, 0x39, 0xc5, 0x2a, 0xda, 0x80, 0x2a, - 0x45, 0xaa, 0x56, 0x6b, 0xcf, 0x64, 0xd9, 0xb2, 0xde, 0x71, 0x76, 0xc6, 0x14, 0xd4, 0x1f, 0x90, - 0x6b, 0xfe, 0x41, 0xef, 0x3d, 0xf4, 0x37, 0xf4, 0xd6, 0xa8, 0xa7, 0xdc, 0x5a, 0xb5, 0x92, 0x53, - 0xc1, 0x3f, 0xe0, 0xd8, 0x53, 0x35, 0xbb, 0xb3, 0xeb, 0x35, 0x78, 0x31, 0xeb, 0x90, 0x43, 0x4e, - 0xec, 0xcc, 0xbc, 0xf7, 0x7d, 0xef, 0xbd, 0x79, 0xef, 0xcd, 0xc3, 0xe0, 0x53, 0x42, 0x5b, 0x84, - 0xda, 0x54, 0xa3, 0x9d, 0x36, 0xf6, 0x9e, 0x3b, 0x1d, 0x1b, 0x69, 0xec, 0x40, 0x6d, 0x7b, 0x84, - 0x11, 0x59, 0x16, 0x87, 0x6a, 0xef, 0xb0, 0x70, 0xc3, 0x22, 0x16, 0xf1, 0x8f, 0x35, 0xfe, 0x15, - 0x48, 0x16, 0x8a, 0x16, 0x21, 0x96, 0x83, 0x35, 0x7f, 0xd5, 0xe8, 0x3c, 0xd7, 0x50, 0xc7, 0x33, - 0x99, 0x4d, 0xdc, 0xf0, 0xbc, 0xe9, 0x43, 0x69, 0x0d, 0x93, 0x62, 0x6d, 0xbf, 0xdc, 0xc0, 0xcc, - 0x2c, 0x6b, 0x4d, 0x62, 0x87, 0xe7, 0xca, 0x69, 0x7d, 0x66, 0xb7, 0x30, 0x65, 0x66, 0xab, 0x2d, - 0x04, 0x3e, 0x1b, 0x60, 0x67, 0xef, 0x33, 0x10, 0x82, 0xfb, 0x60, 0xb6, 0x4e, 0xad, 0xa7, 0xd1, - 0x76, 0x15, 0x3b, 0xd8, 0x32, 0x19, 0x96, 0x3f, 0x07, 0xe3, 0x14, 0xbb, 0x08, 0x7b, 0x79, 0xa9, - 0x24, 0x2d, 0x5c, 0xad, 0xcc, 0x9c, 0x74, 0x95, 0xa9, 0x43, 0xb3, 0xe5, 0x7c, 0x09, 0x83, 0x7d, - 0xa8, 0x0b, 0x01, 0xf9, 0x16, 0x98, 0x70, 0x48, 0x73, 0xcf, 0xb0, 0x51, 0x3e, 0x53, 0x92, 0x16, - 0x72, 0xfa, 0x38, 0x5f, 0xd6, 0x90, 0x3c, 0x07, 0xae, 0xec, 0x9b, 0x8e, 0x61, 0x22, 0xe4, 0xe5, - 0xb3, 0x1c, 0x45, 0x9f, 0xd8, 0x37, 0x9d, 0x75, 0x84, 0x3c, 0xa8, 0x80, 0xdb, 0x03, 0x79, 0x75, - 0x4c, 0xdb, 0xc4, 0xa5, 0x18, 0x7e, 0x0f, 0x6e, 0xf5, 0x09, 0xec, 0xb8, 0xe8, 0x12, 0x4d, 0x83, - 0x77, 0x80, 0x92, 0x00, 0x7f, 0x8e, 0x05, 0x0d, 0xe2, 0xa2, 0x4d, 0xd2, 0xdc, 0x7b, 0x4f, 0x16, - 0x84, 0xf0, 0x91, 0x05, 0xbf, 0x4a, 0xe0, 0x6e, 0x82, 0x95, 0xeb, 0xee, 0x25, 0xdb, 0x23, 0x57, - 0x40, 0x8e, 0x67, 0x97, 0x7f, 0x51, 0x93, 0xcb, 0x73, 0x6a, 0x90, 0x7e, 0x2a, 0x4f, 0x3f, 0x55, - 0xa4, 0x9f, 0xba, 0x41, 0x6c, 0xb7, 0xf2, 0xc9, 0xeb, 0xae, 0x32, 0x76, 0xd2, 0x55, 0x26, 0x03, - 0x02, 0xae, 0x04, 0x75, 0x5f, 0x17, 0x7e, 0x0d, 0x16, 0x2f, 0x62, 0x6f, 0xe8, 0x60, 0xdc, 0x18, - 0xa9, 0x2f, 0x38, 0xbf, 0x4b, 0x60, 0xbe, 0x4e, 0x2d, 0x2e, 0xbc, 0xee, 0xa2, 0x77, 0x4b, 0x4f, - 0x13, 0x7c, 0xc4, 0x8d, 0xa3, 0xf9, 0x4c, 0x29, 0x7b, 0xbe, 0x67, 0x4b, 0xdc, 0xb3, 0x5f, 0xde, - 0x2a, 0x0b, 0x96, 0xcd, 0x76, 0x3b, 0x0d, 0xb5, 0x49, 0x5a, 0x9a, 0xa8, 0xc2, 0xe0, 0xcf, 0x03, - 0x8a, 0xf6, 0x34, 0x76, 0xd8, 0xc6, 0xd4, 0x57, 0xa0, 0x7a, 0x80, 0x7c, 0x5e, 0xa2, 0x3f, 0xf6, - 0xaf, 0x30, 0xd1, 0x91, 0x28, 0x14, 0xd3, 0x20, 0x53, 0xab, 0x8a, 0x28, 0x64, 0x6a, 0x55, 0xf8, - 0x32, 0x03, 0xb4, 0x3a, 0xb5, 0x36, 0x3c, 0x6c, 0x32, 0xfc, 0x55, 0xc7, 0x71, 0x74, 0xd3, 0xb5, - 0xf0, 0x16, 0xa1, 0x36, 0xef, 0x11, 0x1f, 0x76, 0x50, 0xe4, 0xfb, 0x60, 0xa2, 0x4d, 0x88, 0xc3, - 0xef, 0x3d, 0xc7, 0x3d, 0xae, 0xc8, 0x27, 0x5d, 0x65, 0x3a, 0xb0, 0x54, 0x1c, 0x40, 0x7d, 0x9c, - 0x7f, 0xd5, 0x10, 0x7c, 0x01, 0x56, 0x52, 0x06, 0x22, 0x0a, 0xea, 0x4d, 0x10, 0x24, 0x54, 0xb5, - 0x2f, 0xbd, 0xaa, 0x72, 0x11, 0x80, 0xb6, 0x00, 0xa8, 0x55, 0x45, 0x1d, 0xc4, 0x76, 0xa0, 0x07, - 0xf2, 0x75, 0x6a, 0xed, 0xb8, 0x5b, 0x84, 0x38, 0xdf, 0xed, 0xda, 0x0c, 0x3b, 0x36, 0x65, 0x18, - 0xf1, 0x65, 0x9a, 0x20, 0xc7, 0xdc, 0xcc, 0x0c, 0x75, 0xf3, 0x09, 0x28, 0x25, 0x71, 0x46, 0xfe, - 0xdc, 0x03, 0xd7, 0xf0, 0x81, 0xcd, 0x30, 0x32, 0x44, 0xd9, 0xd0, 0xbc, 0x54, 0xca, 0x2e, 0xe4, - 0xf4, 0xa9, 0x60, 0x7b, 0xd3, 0xaf, 0x1e, 0x0a, 0x8f, 0x32, 0x60, 0xd5, 0x07, 0x73, 0x82, 0xbc, - 0xab, 0xdb, 0x96, 0x67, 0x32, 0xfc, 0x74, 0xd7, 0xf4, 0x30, 0xdd, 0x26, 0x51, 0x14, 0x37, 0x88, - 0xdb, 0xc4, 0x2e, 0xe3, 0x67, 0x28, 0x8c, 0x68, 0x4a, 0x07, 0xfb, 0x9a, 0x49, 0xdc, 0x41, 0x71, - 0x00, 0xa3, 0x06, 0x63, 0x81, 0x19, 0xea, 0x1b, 0x60, 0x30, 0x62, 0xb4, 0x02, 0x8b, 0x86, 0x77, - 0x9b, 0x92, 0xe8, 0x36, 0x79, 0x61, 0xc1, 0x69, 0x04, 0xa8, 0x5f, 0xa3, 0xc2, 0x2d, 0xe1, 0xa5, - 0x8c, 0xc1, 0x34, 0x23, 0x7b, 0xd8, 0x35, 0x48, 0x87, 0x19, 0x2d, 0x9e, 0xe4, 0xb9, 0x61, 0x49, - 0x7e, 0x57, 0xb0, 0xcc, 0x07, 0x2c, 0x7d, 0xea, 0x86, 0xd9, 0x22, 0x1d, 0x97, 0x51, 0xa8, 0x7f, - 0xec, 0xef, 0x7f, 0xdb, 0x61, 0x75, 0xdb, 0xa5, 0xf0, 0x8f, 0x2c, 0x58, 0x1b, 0x35, 0xc8, 0xd1, - 0x8d, 0x3e, 0x03, 0x13, 0x01, 0xfc, 0x92, 0x88, 0xf6, 0x1a, 0xb7, 0xe4, 0xef, 0xae, 0x72, 0xef, - 0x02, 0xe5, 0x56, 0x73, 0x59, 0x2f, 0xde, 0x02, 0x06, 0xea, 0x21, 0x60, 0x0f, 0xbb, 0xec, 0xdf, - 0xce, 0x3b, 0x63, 0x97, 0x23, 0xec, 0xb2, 0xfc, 0x23, 0x98, 0x71, 0xec, 0x17, 0x1d, 0x1b, 0xd9, - 0xec, 0xd0, 0x68, 0xfa, 0xb5, 0x89, 0x82, 0x2a, 0xaf, 0x3c, 0x49, 0xc1, 0x52, 0xc5, 0xcd, 0xde, - 0xdd, 0x9e, 0x01, 0x84, 0xfa, 0xf5, 0x68, 0x2f, 0xa8, 0x7f, 0x24, 0xef, 0x80, 0xab, 0x3f, 0x10, - 0xdb, 0x35, 0xf8, 0xb4, 0xe3, 0x37, 0x8f, 0xc9, 0xe5, 0x82, 0x1a, 0x8c, 0x42, 0x6a, 0x38, 0x0a, - 0xa9, 0xdb, 0xe1, 0x28, 0x54, 0x99, 0x17, 0x17, 0x7b, 0x3d, 0xa0, 0x88, 0x54, 0xe1, 0xab, 0xb7, - 0x8a, 0xa4, 0x5f, 0xe1, 0x6b, 0x2e, 0x0c, 0xff, 0x0c, 0xda, 0xed, 0x3a, 0x42, 0xdb, 0x24, 0x7e, - 0x61, 0x9b, 0x21, 0x7f, 0xaf, 0xcb, 0x44, 0x85, 0xb2, 0x02, 0x26, 0xc3, 0x9e, 0x11, 0xbd, 0x60, - 0x95, 0x9b, 0x27, 0x5d, 0x45, 0x0e, 0x4b, 0x3c, 0x3a, 0x84, 0xb1, 0xf6, 0x82, 0x62, 0x15, 0x96, - 0x19, 0x56, 0x61, 0x46, 0x98, 0xcb, 0x08, 0x53, 0xdb, 0xc3, 0x68, 0x69, 0x78, 0xc5, 0xdc, 0x16, - 0x2e, 0xcf, 0xc6, 0x73, 0x39, 0x54, 0x87, 0xfa, 0x94, 0xbf, 0x51, 0x15, 0xeb, 0x33, 0x04, 0x65, - 0x11, 0xd4, 0x11, 0x09, 0xca, 0xa7, 0x08, 0xca, 0x3c, 0xb2, 0x2b, 0x29, 0x23, 0x1b, 0x55, 0xc7, - 0xc8, 0x11, 0x8e, 0x95, 0x55, 0xe6, 0x3d, 0x96, 0x55, 0xf6, 0xb2, 0xcb, 0x2a, 0xd6, 0x50, 0x73, - 0xc3, 0x1a, 0xea, 0xf2, 0x7f, 0x00, 0x64, 0xeb, 0xd4, 0x92, 0x3d, 0x20, 0x0f, 0x1a, 0x06, 0xd4, - 0xb3, 0xff, 0x8a, 0xa8, 0x03, 0x67, 0xee, 0x42, 0xf9, 0xc2, 0xa2, 0xd1, 0xcd, 0x1c, 0x80, 0x1b, - 0x03, 0x67, 0xf3, 0xfb, 0x43, 0xa1, 0x7a, 0xc2, 0x85, 0x87, 0x29, 0x84, 0x93, 0x98, 0xa3, 0x19, - 0xf8, 0x22, 0xcc, 0xa1, 0xf0, 0x85, 0x98, 0xcf, 0x4c, 0xab, 0x3f, 0x4b, 0xe0, 0xce, 0xf0, 0x59, - 0x7c, 0x35, 0x85, 0x53, 0x7d, 0x9a, 0x85, 0xb5, 0x51, 0x35, 0x23, 0x0b, 0x5f, 0x4a, 0x60, 0x2e, - 0x79, 0x66, 0x5e, 0x4a, 0xc0, 0x4f, 0xd4, 0x28, 0xac, 0xa6, 0xd5, 0x88, 0x2c, 0xf9, 0x4d, 0x02, - 0x8b, 0xa9, 0x66, 0xd7, 0x8d, 0x04, 0xaa, 0x34, 0x20, 0x85, 0x6f, 0x2e, 0x01, 0x24, 0x72, 0xe1, - 0x27, 0x30, 0x3b, 0x78, 0x02, 0x5c, 0x4c, 0x60, 0x19, 0x28, 0x5d, 0xf8, 0x22, 0x8d, 0x74, 0x44, - 0xfe, 0x8f, 0x04, 0x1e, 0x8d, 0x36, 0xbe, 0x6d, 0x26, 0xf2, 0x8d, 0x80, 0x56, 0xd8, 0xbe, 0x4c, - 0xb4, 0xbe, 0xec, 0x48, 0xf5, 0xd4, 0x26, 0x65, 0x47, 0x1a, 0x90, 0xc4, 0xec, 0x18, 0xe5, 0x69, - 0xaa, 0x6c, 0xbd, 0x3e, 0x2a, 0x4a, 0x6f, 0x8e, 0x8a, 0xd2, 0xbf, 0x47, 0x45, 0xe9, 0xd5, 0x71, - 0x71, 0xec, 0xcd, 0x71, 0x71, 0xec, 0xaf, 0xe3, 0xe2, 0xd8, 0xb3, 0xc7, 0xb1, 0x67, 0x40, 0x10, - 0x3e, 0x70, 0xcc, 0x06, 0x0d, 0x17, 0xda, 0x7e, 0xf9, 0x91, 0x76, 0xd0, 0xf7, 0xeb, 0x11, 0x7f, - 0x1a, 0x1a, 0xe3, 0xfe, 0xf8, 0xf2, 0xf0, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x23, 0xf9, - 0xff, 0x60, 0x12, 0x00, 0x00, + // 1163 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0x41, 0x4f, 0x1b, 0xc7, + 0x17, 0x67, 0x6d, 0xff, 0x21, 0x19, 0xfe, 0x21, 0x61, 0x1b, 0x12, 0xe3, 0x26, 0x5e, 0x67, 0x5a, + 0x45, 0x54, 0x21, 0xbb, 0x98, 0x34, 0x01, 0xf5, 0x04, 0xc6, 0x6a, 0xe5, 0x14, 0xab, 0x68, 0x03, + 0xaa, 0x14, 0xa9, 0xb2, 0xd6, 0x9e, 0xc9, 0xb2, 0x65, 0xbd, 0x63, 0x76, 0xc6, 0x14, 0xd4, 0x0f, + 0x90, 0x6b, 0xbe, 0x41, 0xef, 0x3d, 0xf4, 0x33, 0xf4, 0xd6, 0xa8, 0xa7, 0xdc, 0x5a, 0xb5, 0x92, + 0x53, 0xc1, 0xbd, 0x07, 0x8e, 0x3d, 0x55, 0xb3, 0x3b, 0x3b, 0xb6, 0xc1, 0x8b, 0x59, 0xc7, 0x39, + 0xf4, 0xc4, 0xce, 0xcc, 0x7b, 0xbf, 0xf7, 0x7b, 0x6f, 0xde, 0x7b, 0xf3, 0x30, 0xf8, 0x90, 0xd0, + 0x26, 0xa1, 0x0e, 0x35, 0x68, 0xbb, 0x85, 0xfd, 0x17, 0x6e, 0xdb, 0x41, 0x06, 0x3b, 0xd4, 0x5b, + 0x3e, 0x61, 0x44, 0x55, 0xc5, 0xa1, 0xde, 0x3d, 0xcc, 0xdd, 0xb4, 0x89, 0x4d, 0x82, 0x63, 0x83, + 0x7f, 0x85, 0x92, 0xb9, 0xbc, 0x4d, 0x88, 0xed, 0x62, 0x23, 0x58, 0xd5, 0xdb, 0x2f, 0x0c, 0xd4, + 0xf6, 0x2d, 0xe6, 0x10, 0x2f, 0x3a, 0x6f, 0x04, 0x50, 0x46, 0xdd, 0xa2, 0xd8, 0x38, 0x28, 0xd6, + 0x31, 0xb3, 0x8a, 0x46, 0x83, 0x38, 0xd1, 0xb9, 0x76, 0x56, 0x9f, 0x39, 0x4d, 0x4c, 0x99, 0xd5, + 0x6c, 0x09, 0x81, 0x8f, 0x06, 0xf0, 0xec, 0x7e, 0x86, 0x42, 0xf0, 0x00, 0xcc, 0x55, 0xa9, 0xfd, + 0x4c, 0x6e, 0x97, 0xb1, 0x8b, 0x6d, 0x8b, 0x61, 0xf5, 0x13, 0x30, 0x49, 0xb1, 0x87, 0xb0, 0x9f, + 0x55, 0x0a, 0xca, 0xc2, 0xd5, 0xd2, 0xec, 0x69, 0x47, 0xbb, 0x76, 0x64, 0x35, 0xdd, 0xcf, 0x60, + 0xb8, 0x0f, 0x4d, 0x21, 0xa0, 0xde, 0x06, 0x53, 0x2e, 0x69, 0xec, 0xd5, 0x1c, 0x94, 0x4d, 0x15, + 0x94, 0x85, 0x8c, 0x39, 0xc9, 0x97, 0x15, 0xa4, 0xce, 0x83, 0x2b, 0x07, 0x96, 0x5b, 0xb3, 0x10, + 0xf2, 0xb3, 0x69, 0x8e, 0x62, 0x4e, 0x1d, 0x58, 0xee, 0x3a, 0x42, 0x3e, 0xd4, 0xc0, 0xdd, 0x81, + 0x76, 0x4d, 0x4c, 0x5b, 0xc4, 0xa3, 0x18, 0x7e, 0x03, 0x6e, 0xf7, 0x09, 0xec, 0x78, 0x68, 0x8c, + 0xd4, 0xe0, 0x3d, 0xa0, 0xc5, 0xc0, 0x5f, 0xc0, 0xa0, 0x4e, 0x3c, 0xb4, 0x49, 0x1a, 0x7b, 0xef, + 0x89, 0x41, 0x04, 0x2f, 0x19, 0xfc, 0xa4, 0x80, 0x8f, 0x63, 0x58, 0xae, 0x7b, 0x63, 0xe6, 0xa3, + 0x96, 0x40, 0x86, 0x67, 0x57, 0x70, 0x51, 0xd3, 0xcb, 0xf3, 0x7a, 0x98, 0x7e, 0x3a, 0x4f, 0x3f, + 0x5d, 0xa4, 0x9f, 0xbe, 0x41, 0x1c, 0xaf, 0xf4, 0xc1, 0xeb, 0x8e, 0x36, 0x71, 0xda, 0xd1, 0xa6, + 0x43, 0x03, 0x5c, 0x09, 0x9a, 0x81, 0x2e, 0xfc, 0x02, 0x2c, 0x5e, 0x86, 0x6f, 0xe4, 0x60, 0x2f, + 0x19, 0xa5, 0x2f, 0x38, 0xbf, 0x28, 0xe0, 0x4e, 0x95, 0xda, 0x5c, 0x78, 0xdd, 0x43, 0xef, 0x96, + 0x9e, 0x16, 0xf8, 0x1f, 0x27, 0x47, 0xb3, 0xa9, 0x42, 0xfa, 0x62, 0xcf, 0x96, 0xb8, 0x67, 0x3f, + 0xbe, 0xd5, 0x16, 0x6c, 0x87, 0xed, 0xb6, 0xeb, 0x7a, 0x83, 0x34, 0x0d, 0x51, 0x85, 0xe1, 0x9f, + 0x87, 0x14, 0xed, 0x19, 0xec, 0xa8, 0x85, 0x69, 0xa0, 0x40, 0xcd, 0x10, 0xf9, 0xa2, 0x44, 0x7f, + 0x12, 0x5c, 0x61, 0xac, 0x23, 0x32, 0x14, 0x33, 0x20, 0x55, 0x29, 0x8b, 0x28, 0xa4, 0x2a, 0x65, + 0xf8, 0x32, 0x05, 0x8c, 0x2a, 0xb5, 0x37, 0x7c, 0x6c, 0x31, 0xfc, 0x79, 0xdb, 0x75, 0x4d, 0xcb, + 0xb3, 0xf1, 0x16, 0xa1, 0x0e, 0xef, 0x11, 0xff, 0xed, 0xa0, 0xa8, 0x0f, 0xc0, 0x54, 0x8b, 0x10, + 0x97, 0xdf, 0x7b, 0x86, 0x7b, 0x5c, 0x52, 0x4f, 0x3b, 0xda, 0x4c, 0xc8, 0x54, 0x1c, 0x40, 0x73, + 0x92, 0x7f, 0x55, 0x10, 0xdc, 0x07, 0x2b, 0x09, 0x03, 0x21, 0x83, 0x7a, 0x0b, 0x84, 0x09, 0x55, + 0xee, 0x4b, 0xaf, 0xb2, 0x9a, 0x07, 0xa0, 0x25, 0x00, 0x2a, 0x65, 0x51, 0x07, 0x3d, 0x3b, 0xd0, + 0x07, 0xd9, 0x2a, 0xb5, 0x77, 0xbc, 0x2d, 0x42, 0xdc, 0xaf, 0x77, 0x1d, 0x86, 0x5d, 0x87, 0x32, + 0x8c, 0xf8, 0x32, 0x49, 0x90, 0x7b, 0xdc, 0x4c, 0x0d, 0x75, 0xf3, 0x29, 0x28, 0xc4, 0xd9, 0x94, + 0xfe, 0xdc, 0x07, 0xd7, 0xf1, 0xa1, 0xc3, 0x30, 0xaa, 0x89, 0xb2, 0xa1, 0x59, 0xa5, 0x90, 0x5e, + 0xc8, 0x98, 0xd7, 0xc2, 0xed, 0xcd, 0xa0, 0x7a, 0x28, 0xfc, 0x3b, 0x05, 0x56, 0x03, 0x30, 0x37, + 0xcc, 0xbb, 0xaa, 0x63, 0xfb, 0x16, 0xc3, 0xcf, 0x76, 0x2d, 0x1f, 0xd3, 0x6d, 0x22, 0xa3, 0xb8, + 0x41, 0xbc, 0x06, 0xf6, 0x18, 0x3f, 0x43, 0x51, 0x44, 0x13, 0x3a, 0xd8, 0xd7, 0x4c, 0x7a, 0x1d, + 0x14, 0x07, 0x50, 0x36, 0x18, 0x1b, 0xcc, 0xd2, 0x80, 0x40, 0x8d, 0x91, 0x5a, 0x33, 0x64, 0x34, + 0xbc, 0xdb, 0x14, 0x44, 0xb7, 0xc9, 0x0a, 0x06, 0x67, 0x11, 0xa0, 0x79, 0x9d, 0x0a, 0xb7, 0x84, + 0x97, 0xea, 0x3e, 0x98, 0x61, 0x64, 0x0f, 0x7b, 0x35, 0xd2, 0x66, 0xb5, 0x26, 0x4f, 0xf2, 0xcc, + 0xf8, 0x93, 0xfc, 0xff, 0x81, 0x89, 0xaf, 0xda, 0xac, 0xea, 0x78, 0x14, 0xfe, 0x9a, 0x06, 0x6b, + 0xa3, 0x06, 0x5c, 0xde, 0xee, 0x73, 0x30, 0x65, 0x35, 0x49, 0xdb, 0x63, 0x4b, 0x22, 0xf2, 0x6b, + 0x9c, 0xd5, 0x1f, 0x1d, 0xed, 0xfe, 0x25, 0x58, 0x55, 0x3c, 0xd6, 0x8d, 0xbd, 0x80, 0x81, 0x66, + 0x04, 0xd8, 0xc5, 0x2e, 0x06, 0x37, 0xf5, 0xce, 0xd8, 0x45, 0x89, 0x5d, 0x54, 0xbf, 0x03, 0xb3, + 0xae, 0xb3, 0xdf, 0x76, 0x90, 0xc3, 0x8e, 0x6a, 0x8d, 0xa0, 0x4e, 0x51, 0x58, 0xf1, 0xa5, 0xa7, + 0x09, 0xac, 0x94, 0x71, 0xa3, 0x7b, 0xcf, 0xe7, 0x00, 0xa1, 0x79, 0x43, 0xee, 0x85, 0xbd, 0x00, + 0xa9, 0x3b, 0xe0, 0xea, 0xb7, 0xc4, 0xf1, 0x6a, 0x7c, 0xf2, 0x09, 0x1a, 0xc9, 0xf4, 0x72, 0x4e, + 0x0f, 0xc7, 0x22, 0x3d, 0x1a, 0x8b, 0xf4, 0xed, 0x68, 0x2c, 0x2a, 0xdd, 0x11, 0xa9, 0x74, 0x23, + 0x34, 0x21, 0x55, 0xe1, 0xab, 0xb7, 0x9a, 0x62, 0x5e, 0xe1, 0x6b, 0x2e, 0x0c, 0x7f, 0x0b, 0x5b, + 0xef, 0x3a, 0x42, 0xdb, 0xa4, 0xf7, 0xc2, 0x36, 0x23, 0xfb, 0xdd, 0x8e, 0x23, 0x8b, 0x66, 0x05, + 0x4c, 0x47, 0xfd, 0x43, 0xbe, 0x66, 0xa5, 0x5b, 0xa7, 0x1d, 0x4d, 0x8d, 0xca, 0x5d, 0x1e, 0xc2, + 0x9e, 0x56, 0x83, 0x7a, 0xaa, 0x2d, 0x35, 0xac, 0xda, 0x6a, 0x51, 0x5e, 0x23, 0x4c, 0x1d, 0x1f, + 0xa3, 0xa5, 0xe1, 0xd5, 0x73, 0x57, 0xb8, 0x3c, 0x17, 0x22, 0xf6, 0xab, 0x43, 0xf3, 0x5a, 0xb0, + 0x51, 0x16, 0xeb, 0x73, 0x06, 0x8a, 0x22, 0xa8, 0x23, 0x1a, 0x28, 0x9e, 0x31, 0x50, 0xe4, 0x91, + 0x5d, 0x49, 0x18, 0x59, 0x59, 0x1d, 0x23, 0x47, 0xb8, 0xa7, 0xac, 0x52, 0xef, 0xb1, 0xac, 0xd2, + 0xe3, 0x2e, 0xab, 0x9e, 0xe6, 0x9a, 0x19, 0xd6, 0x5c, 0x97, 0xff, 0x01, 0x20, 0x5d, 0xa5, 0xb6, + 0xea, 0x03, 0x75, 0xd0, 0x60, 0xa0, 0x9f, 0xff, 0xb7, 0x44, 0x1f, 0x38, 0x7f, 0xe7, 0x8a, 0x97, + 0x16, 0x95, 0x37, 0x73, 0x08, 0x6e, 0x0e, 0x9c, 0xd3, 0x1f, 0x0c, 0x85, 0xea, 0x0a, 0xe7, 0x1e, + 0x25, 0x10, 0x8e, 0xb3, 0x2c, 0xe7, 0xe1, 0xcb, 0x58, 0x8e, 0x84, 0x2f, 0x65, 0xf9, 0xdc, 0xe4, + 0xfa, 0x83, 0x02, 0xee, 0x0d, 0x9f, 0xcb, 0x57, 0x13, 0x38, 0xd5, 0xa7, 0x99, 0x5b, 0x1b, 0x55, + 0x53, 0x32, 0x7c, 0xa9, 0x80, 0xf9, 0xf8, 0xf9, 0x79, 0x29, 0x06, 0x3f, 0x56, 0x23, 0xb7, 0x9a, + 0x54, 0x43, 0x32, 0xf9, 0x59, 0x01, 0x8b, 0x89, 0xe6, 0xd8, 0x8d, 0x18, 0x53, 0x49, 0x40, 0x72, + 0x5f, 0x8e, 0x01, 0x44, 0xba, 0xf0, 0x3d, 0x98, 0x1b, 0x3c, 0x0d, 0x2e, 0xc6, 0x58, 0x19, 0x28, + 0x9d, 0xfb, 0x34, 0x89, 0xb4, 0x34, 0xfe, 0xa7, 0x02, 0x1e, 0x8f, 0x36, 0xca, 0x6d, 0xc6, 0xda, + 0x1b, 0x01, 0x2d, 0xb7, 0x3d, 0x4e, 0xb4, 0xbe, 0xec, 0x48, 0xf4, 0xd4, 0xc6, 0x65, 0x47, 0x12, + 0x90, 0xd8, 0xec, 0x18, 0xe5, 0x69, 0x2a, 0x6d, 0xbd, 0x3e, 0xce, 0x2b, 0x6f, 0x8e, 0xf3, 0xca, + 0x5f, 0xc7, 0x79, 0xe5, 0xd5, 0x49, 0x7e, 0xe2, 0xcd, 0x49, 0x7e, 0xe2, 0xf7, 0x93, 0xfc, 0xc4, + 0xf3, 0x27, 0x3d, 0xcf, 0x80, 0x30, 0xf8, 0xd0, 0xb5, 0xea, 0x34, 0x5a, 0x18, 0x07, 0xc5, 0xc7, + 0xc6, 0x61, 0xdf, 0x2f, 0x49, 0xfc, 0x69, 0xa8, 0x4f, 0x06, 0xe3, 0xcb, 0xa3, 0x7f, 0x03, 0x00, + 0x00, 0xff, 0xff, 0x7d, 0x9f, 0xd6, 0x5f, 0x6c, 0x12, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. From f7509a89417808f95397df0599685fe05d79c6ae Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Mon, 15 May 2023 01:01:01 -0500 Subject: [PATCH 07/15] use enum for migration type --- x/superfluid/keeper/export_test.go | 2 +- x/superfluid/keeper/migrate.go | 45 ++++++++++++++--------------- x/superfluid/keeper/migrate_test.go | 15 ++++------ 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 01e4a3975bd..ccf7d1a16ee 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -38,7 +38,7 @@ func (k Keeper) ValidateSharesToMigrateUnlockAndExitBalancerPool(ctx sdk.Context return k.validateSharesToMigrateUnlockAndExitBalancerPool(ctx, sender, poolIdLeaving, lock, sharesToMigrate, tokenOutMins, remainingLockTime) } -func (k Keeper) RouteMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (synthLocksBeforeMigration []lockuptypes.SyntheticLock, isSuperfluidBonded, isSuperfluidUnbonding bool, err error) { +func (k Keeper) RouteMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (synthLocksBeforeMigration []lockuptypes.SyntheticLock, migrationType MigrationType, err error) { return k.routeMigration(ctx, sender, lockId, sharesToMigrate) } diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index 3b77aedf1e0..a545009f3a2 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -13,31 +13,37 @@ import ( "github.com/osmosis-labs/osmosis/v15/x/superfluid/types" ) +type MigrationType int + +const ( + SuperfluidBonded MigrationType = iota + SuperfluidUnbonding + NonSuperfluid + Unsupported +) + // RouteLockedBalancerToConcentratedMigration routes the provided lock to the proper migration function based on the lock status. // If the lock is superfluid delegated, it will instantly undelegate the superfluid position and redelegate it as a concentrated liquidity position. // If the lock is superfluid undelegating, it will instantly undelegate the superfluid position and redelegate it as a concentrated liquidity position, but continue to unlock where it left off. // If the lock is locked or unlocking but not superfluid delegated/undelegating, it will migrate the position and either start unlocking or continue unlocking where it left off. // 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, lockId uint64, sharesToMigrate sdk.Coin, tokenOutMins sdk.Coins) (positionId uint64, amount0, amount1 sdk.Int, liquidity sdk.Dec, joinTime time.Time, poolIdLeaving, poolIdEntering, gammLockId, concentratedLockId uint64, err error) { - // Validate and retrieve pertinent data required for migration - synthLocksBeforeMigration, isSuperfluidBonded, isSuperfluidUnbonding, err := k.routeMigration(ctx, sender, lockId, sharesToMigrate) + synthLocksBeforeMigration, migrationType, err := k.routeMigration(ctx, sender, lockId, sharesToMigrate) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, err } - if isSuperfluidBonded { - // Migration logic for superfluid bonded locks + switch migrationType { + case SuperfluidBonded: positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, err = k.migrateSuperfluidBondedBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthLocksBeforeMigration[0].SynthDenom, tokenOutMins) - } else if isSuperfluidUnbonding { - // Migration logic for superfluid unbonding locks + case SuperfluidUnbonding: positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, err = k.migrateSuperfluidUnbondingBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, synthLocksBeforeMigration[0].SynthDenom, tokenOutMins) - } else if !isSuperfluidBonded && !isSuperfluidUnbonding && len(synthLocksBeforeMigration) == 0 { - // Migration logic for non-superfluid locks + case NonSuperfluid: positionId, amount0, amount1, liquidity, joinTime, gammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, err = k.migrateNonSuperfluidLockBalancerToConcentrated(ctx, sender, lockId, sharesToMigrate, tokenOutMins) - } else { - // Unsupported migration - return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, fmt.Errorf("unexpected synth lock state for lock %d", lockId) + default: + return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, 0, 0, fmt.Errorf("unsupported migration type") } + return positionId, amount0, amount1, liquidity, joinTime, poolIdLeaving, poolIdEntering, gammLockId, concentratedLockId, err } @@ -234,27 +240,20 @@ 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, lockId uint64, sharesToMigrate sdk.Coin) (synthLocksBeforeMigration []lockuptypes.SyntheticLock, isSuperfluidBonded, isSuperfluidUnbonding bool, err error) { - // Check if the lock has any corresponding synthetic locks. - // Synthetic lock existence implies that the lock is superfluid delegated or undelegating. +func (k Keeper) routeMigration(ctx sdk.Context, sender sdk.AccAddress, lockId uint64, sharesToMigrate sdk.Coin) (synthLocksBeforeMigration []lockuptypes.SyntheticLock, migrationType MigrationType, err error) { synthLocksBeforeMigration = k.lk.GetAllSyntheticLockupsByLockup(ctx, lockId) + migrationType = NonSuperfluid for _, synthLockBeforeMigration := range synthLocksBeforeMigration { - // Check if the synthetic lock is superfluid delegated or undelegating. if strings.Contains(synthLockBeforeMigration.SynthDenom, "superbonding") { - isSuperfluidBonded = true + migrationType = SuperfluidBonded } if strings.Contains(synthLockBeforeMigration.SynthDenom, "superunbonding") { - isSuperfluidUnbonding = true + migrationType = SuperfluidUnbonding } } - if isSuperfluidBonded && isSuperfluidUnbonding { - // This should never happen, but if it does, we don't support it. - return []lockuptypes.SyntheticLock{}, false, false, types.LockBothSuperfluidBondedAndUnbondingError{LockId: lockId} - } - - return synthLocksBeforeMigration, isSuperfluidBonded, isSuperfluidUnbonding, nil + return synthLocksBeforeMigration, migrationType, nil } // validateMigration performs validation for the migration of gamm LP tokens from a Balancer pool to the canonical Concentrated pool. It performs the following steps: diff --git a/x/superfluid/keeper/migrate_test.go b/x/superfluid/keeper/migrate_test.go index 6d9a3735bc7..42174516d32 100644 --- a/x/superfluid/keeper/migrate_test.go +++ b/x/superfluid/keeper/migrate_test.go @@ -241,10 +241,9 @@ func (suite *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, isSuperfluidBonded, isSuperfluidUnbonding, err := superfluidKeeper.RouteMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) + synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) suite.Require().NoError(err) - suite.Require().True(isSuperfluidBonded) - suite.Require().False(isSuperfluidUnbonding) + suite.Require().Equal(migrationType, keeper.SuperfluidBonded) // Modify migration inputs if necessary @@ -362,10 +361,9 @@ func (suite *KeeperTestSuite) TestMigrateSuperfluidUnbondingBalancerToConcentrat 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, isSuperfluidBonded, isSuperfluidUnbonding, err := superfluidKeeper.RouteMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) + synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) suite.Require().NoError(err) - suite.Require().False(isSuperfluidBonded) - suite.Require().True(isSuperfluidUnbonding) + suite.Require().Equal(migrationType, keeper.SuperfluidUnbonding) // Modify migration inputs if necessary @@ -454,11 +452,10 @@ func (suite *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, isSuperfluidBonded, isSuperfluidUnbonding, err := superfluidKeeper.RouteMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) + synthLockBeforeMigration, migrationType, err := superfluidKeeper.RouteMigration(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate) suite.Require().NoError(err) suite.Require().Equal(0, len(synthLockBeforeMigration)) - suite.Require().False(isSuperfluidBonded) - suite.Require().False(isSuperfluidUnbonding) + suite.Require().Equal(migrationType, keeper.NonSuperfluid) // System under test. positionId, amount0, amount1, _, _, newGammLockId, concentratedLockId, poolIdLeaving, poolIdEntering, err := superfluidKeeper.MigrateNonSuperfluidLockBalancerToConcentrated(ctx, poolJoinAcc, originalGammLockId, coinsToMigrate, tc.tokenOutMins) From c673324dbd855a9790cbc31174aac317d5b10dfb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 18:01:02 +0900 Subject: [PATCH 08/15] chore(deps): bump lycheeverse/lychee-action from 1.7.0 to 1.8.0 (#5180) Bumps [lycheeverse/lychee-action](https://github.com/lycheeverse/lychee-action) from 1.7.0 to 1.8.0. - [Release notes](https://github.com/lycheeverse/lychee-action/releases) - [Commits](https://github.com/lycheeverse/lychee-action/compare/v1.7.0...v1.8.0) --- updated-dependencies: - dependency-name: lycheeverse/lychee-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-broken-links.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-broken-links.yml b/.github/workflows/check-broken-links.yml index 10a5165aa8f..ecb5f74e790 100644 --- a/.github/workflows/check-broken-links.yml +++ b/.github/workflows/check-broken-links.yml @@ -13,7 +13,7 @@ jobs: - name: Link Checker id: lychee - uses: lycheeverse/lychee-action@v1.7.0 + uses: lycheeverse/lychee-action@v1.8.0 with: args: --exclude-loopback --verbose --no-progress './**/*.md' './**/*.html' env: From 1c031ddad947f0e732b6bb53100fde20174606d3 Mon Sep 17 00:00:00 2001 From: Sishir Giri Date: Mon, 15 May 2023 09:43:49 -0700 Subject: [PATCH 09/15] fix localosmosis (#5183) --- go.sum | 2 -- tests/cl-genesis-positions/go.mod | 4 ++-- tests/cl-genesis-positions/go.sum | 10 ++++------ tests/cl-go-client/go.mod | 2 +- tests/cl-go-client/go.sum | 3 --- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/go.sum b/go.sum index b7dd8f85989..181ec5f621c 100644 --- a/go.sum +++ b/go.sum @@ -938,8 +938,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.20230503232557-ba905586c111 h1:1ahWbf9iF9sxDOjxrHWFaBGLE0nWFdpiX1pqObUaJO8= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230503232557-ba905586c111/go.mod h1:a7lhiXRpn8QJ21OhFpaEnUNErTSIafaYpp02q6uI/Dk= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230504190933-b174397f0bc5 h1:fBzTtgZHxvZkpwlg6YtAsNaexEHYaFZDXsYfPQWu9GE= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230504190933-b174397f0bc5/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae h1:I1Cy+GpTPWbVi0lBw9+bS1w42YfQjvXNK9bW4YbHhcs= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A= github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304 h1:RIrWLzIiZN5Xd2JOfSOtGZaf6V3qEQYg6EaDTAkMnCo= diff --git a/tests/cl-genesis-positions/go.mod b/tests/cl-genesis-positions/go.mod index efb9515b252..0a68fbc9d82 100644 --- a/tests/cl-genesis-positions/go.mod +++ b/tests/cl-genesis-positions/go.mod @@ -6,7 +6,7 @@ require ( github.com/cosmos/cosmos-sdk v0.47.2 github.com/ignite/cli v0.23.0 github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230503232557-ba905586c111 - github.com/osmosis-labs/osmosis/v15 v15.0.0-20230504143153-c7d6a52cd9f5 + github.com/osmosis-labs/osmosis/v15 v15.0.0-20230513050746-13c81d83ef0d github.com/tendermint/tendermint v0.34.26 ) @@ -101,7 +101,7 @@ require ( github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.15.0 // indirect + github.com/prometheus/client_golang v1.15.1 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect diff --git a/tests/cl-genesis-positions/go.sum b/tests/cl-genesis-positions/go.sum index 2f77fd7ff16..4d8ef90bc93 100644 --- a/tests/cl-genesis-positions/go.sum +++ b/tests/cl-genesis-positions/go.sum @@ -695,12 +695,10 @@ github.com/osmosis-labs/cosmos-sdk v0.45.1-0.20230326212251-7a2cf2993434 h1:RetE github.com/osmosis-labs/cosmos-sdk v0.45.1-0.20230326212251-7a2cf2993434/go.mod h1:ss3tUfPTwaa6NsoPZrCR7xOqLqCK0LwoNbc2Q8Zs5/Y= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230503232557-ba905586c111 h1:1ahWbf9iF9sxDOjxrHWFaBGLE0nWFdpiX1pqObUaJO8= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230503232557-ba905586c111/go.mod h1:a7lhiXRpn8QJ21OhFpaEnUNErTSIafaYpp02q6uI/Dk= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230504001814-1dbcc2079de1 h1:1yzJYsP1bWOX/8/aGA8Mk/UFiU9z/h6LRAr2OezyYQ8= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230504001814-1dbcc2079de1/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae h1:I1Cy+GpTPWbVi0lBw9+bS1w42YfQjvXNK9bW4YbHhcs= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A= -github.com/osmosis-labs/osmosis/v15 v15.0.0-20230504143153-c7d6a52cd9f5 h1:M4fG/zxok1+9y5SWas1sBzDZWfSbGs7vezE1NF7niCk= -github.com/osmosis-labs/osmosis/v15 v15.0.0-20230504143153-c7d6a52cd9f5/go.mod h1:NEoCQ+jkE0o6CUorEUhRdwdbvXYl5nt4oZeevaEz29o= +github.com/osmosis-labs/osmosis/v15 v15.0.0-20230513050746-13c81d83ef0d h1:8lJNaOABRchMwMO+DoHEGrlrBXlhDloms1GKs/ti8HA= +github.com/osmosis-labs/osmosis/v15 v15.0.0-20230513050746-13c81d83ef0d/go.mod h1:mdvXaHvcLi1Loo2sUF1FPV3RynReBpexO3g3ktEWQA4= 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.0-20230331072320-5d6f6cfa2627 h1:A0SwZgp4bmJFbivYJc8mmVhMjrr3EdUZluBYFke11+w= @@ -744,8 +742,8 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= diff --git a/tests/cl-go-client/go.mod b/tests/cl-go-client/go.mod index 5d209c66e3f..9a94e464cf0 100644 --- a/tests/cl-go-client/go.mod +++ b/tests/cl-go-client/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/cosmos/cosmos-sdk v0.47.2 github.com/ignite/cli v0.23.0 - github.com/osmosis-labs/osmosis/v15 v15.0.0-20230502194055-e465f0b40c14 + github.com/osmosis-labs/osmosis/v15 v15.0.0-20230502194055-13c81d83ef0d ) diff --git a/tests/cl-go-client/go.sum b/tests/cl-go-client/go.sum index f6d58cd00e3..12dcffdbbb9 100644 --- a/tests/cl-go-client/go.sum +++ b/tests/cl-go-client/go.sum @@ -688,7 +688,6 @@ github.com/osmosis-labs/cosmos-sdk v0.45.1-0.20230326212251-7a2cf2993434 h1:RetE github.com/osmosis-labs/cosmos-sdk v0.45.1-0.20230326212251-7a2cf2993434/go.mod h1:ss3tUfPTwaa6NsoPZrCR7xOqLqCK0LwoNbc2Q8Zs5/Y= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230503232557-ba905586c111 h1:1ahWbf9iF9sxDOjxrHWFaBGLE0nWFdpiX1pqObUaJO8= github.com/osmosis-labs/osmosis/osmomath v0.0.3-dev.0.20230503232557-ba905586c111/go.mod h1:a7lhiXRpn8QJ21OhFpaEnUNErTSIafaYpp02q6uI/Dk= -github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230504190933-b174397f0bc5 h1:fBzTtgZHxvZkpwlg6YtAsNaexEHYaFZDXsYfPQWu9GE= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae h1:I1Cy+GpTPWbVi0lBw9+bS1w42YfQjvXNK9bW4YbHhcs= github.com/osmosis-labs/osmosis/osmoutils v0.0.0-20230510161551-8bf252f26bae/go.mod h1:hk/o9/kmTSZmZqwXcSrPuwj/gpRMCqbE/d3vj6teL2A= github.com/osmosis-labs/osmosis/x/epochs v0.0.0-20230328024000-175ec88e4304 h1:RIrWLzIiZN5Xd2JOfSOtGZaf6V3qEQYg6EaDTAkMnCo= @@ -731,8 +730,6 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM= -github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= From 5c82ca3ee9001e9aa00eb0cdc13f580f48593f83 Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Mon, 15 May 2023 12:41:39 -0500 Subject: [PATCH 10/15] add back proto tag --- proto/osmosis/superfluid/tx.proto | 1 + x/superfluid/types/tx.pb.go | 150 +++++++++++++++--------------- 2 files changed, 76 insertions(+), 75 deletions(-) diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index f7860e388ec..26375d43ff9 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -150,6 +150,7 @@ message MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition { ]; // token_out_mins indicates minimum token to exit Balancer pool with. repeated cosmos.base.v1beta1.Coin token_out_mins = 4 [ + (gogoproto.moretags) = "yaml:\"token_out_mins\"", (gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" ]; diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index ec958f97c3c..ecd48822493 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -766,7 +766,7 @@ type MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition struct { LockId uint64 `protobuf:"varint,2,opt,name=lock_id,json=lockId,proto3" json:"lock_id,omitempty" yaml:"lock_id"` SharesToMigrate types.Coin `protobuf:"bytes,3,opt,name=shares_to_migrate,json=sharesToMigrate,proto3" json:"shares_to_migrate" yaml:"shares_to_migrate"` // token_out_mins indicates minimum token to exit Balancer pool with. - TokenOutMins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=token_out_mins,json=tokenOutMins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"token_out_mins"` + TokenOutMins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=token_out_mins,json=tokenOutMins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"token_out_mins" yaml:"token_out_mins"` } func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) Reset() { @@ -1040,80 +1040,80 @@ func init() { func init() { proto.RegisterFile("osmosis/superfluid/tx.proto", fileDescriptor_55b645f187d22814) } var fileDescriptor_55b645f187d22814 = []byte{ - // 1163 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0x41, 0x4f, 0x1b, 0xc7, - 0x17, 0x67, 0x6d, 0xff, 0x21, 0x19, 0xfe, 0x21, 0x61, 0x1b, 0x12, 0xe3, 0x26, 0x5e, 0x67, 0x5a, - 0x45, 0x54, 0x21, 0xbb, 0x98, 0x34, 0x01, 0xf5, 0x04, 0xc6, 0x6a, 0xe5, 0x14, 0xab, 0x68, 0x03, - 0xaa, 0x14, 0xa9, 0xb2, 0xd6, 0x9e, 0xc9, 0xb2, 0x65, 0xbd, 0x63, 0x76, 0xc6, 0x14, 0xd4, 0x0f, - 0x90, 0x6b, 0xbe, 0x41, 0xef, 0x3d, 0xf4, 0x33, 0xf4, 0xd6, 0xa8, 0xa7, 0xdc, 0x5a, 0xb5, 0x92, - 0x53, 0xc1, 0xbd, 0x07, 0x8e, 0x3d, 0x55, 0xb3, 0x3b, 0x3b, 0xb6, 0xc1, 0x8b, 0x59, 0xc7, 0x39, - 0xf4, 0xc4, 0xce, 0xcc, 0x7b, 0xbf, 0xf7, 0x7b, 0x6f, 0xde, 0x7b, 0xf3, 0x30, 0xf8, 0x90, 0xd0, - 0x26, 0xa1, 0x0e, 0x35, 0x68, 0xbb, 0x85, 0xfd, 0x17, 0x6e, 0xdb, 0x41, 0x06, 0x3b, 0xd4, 0x5b, - 0x3e, 0x61, 0x44, 0x55, 0xc5, 0xa1, 0xde, 0x3d, 0xcc, 0xdd, 0xb4, 0x89, 0x4d, 0x82, 0x63, 0x83, - 0x7f, 0x85, 0x92, 0xb9, 0xbc, 0x4d, 0x88, 0xed, 0x62, 0x23, 0x58, 0xd5, 0xdb, 0x2f, 0x0c, 0xd4, - 0xf6, 0x2d, 0xe6, 0x10, 0x2f, 0x3a, 0x6f, 0x04, 0x50, 0x46, 0xdd, 0xa2, 0xd8, 0x38, 0x28, 0xd6, - 0x31, 0xb3, 0x8a, 0x46, 0x83, 0x38, 0xd1, 0xb9, 0x76, 0x56, 0x9f, 0x39, 0x4d, 0x4c, 0x99, 0xd5, - 0x6c, 0x09, 0x81, 0x8f, 0x06, 0xf0, 0xec, 0x7e, 0x86, 0x42, 0xf0, 0x00, 0xcc, 0x55, 0xa9, 0xfd, - 0x4c, 0x6e, 0x97, 0xb1, 0x8b, 0x6d, 0x8b, 0x61, 0xf5, 0x13, 0x30, 0x49, 0xb1, 0x87, 0xb0, 0x9f, - 0x55, 0x0a, 0xca, 0xc2, 0xd5, 0xd2, 0xec, 0x69, 0x47, 0xbb, 0x76, 0x64, 0x35, 0xdd, 0xcf, 0x60, - 0xb8, 0x0f, 0x4d, 0x21, 0xa0, 0xde, 0x06, 0x53, 0x2e, 0x69, 0xec, 0xd5, 0x1c, 0x94, 0x4d, 0x15, - 0x94, 0x85, 0x8c, 0x39, 0xc9, 0x97, 0x15, 0xa4, 0xce, 0x83, 0x2b, 0x07, 0x96, 0x5b, 0xb3, 0x10, - 0xf2, 0xb3, 0x69, 0x8e, 0x62, 0x4e, 0x1d, 0x58, 0xee, 0x3a, 0x42, 0x3e, 0xd4, 0xc0, 0xdd, 0x81, - 0x76, 0x4d, 0x4c, 0x5b, 0xc4, 0xa3, 0x18, 0x7e, 0x03, 0x6e, 0xf7, 0x09, 0xec, 0x78, 0x68, 0x8c, - 0xd4, 0xe0, 0x3d, 0xa0, 0xc5, 0xc0, 0x5f, 0xc0, 0xa0, 0x4e, 0x3c, 0xb4, 0x49, 0x1a, 0x7b, 0xef, - 0x89, 0x41, 0x04, 0x2f, 0x19, 0xfc, 0xa4, 0x80, 0x8f, 0x63, 0x58, 0xae, 0x7b, 0x63, 0xe6, 0xa3, - 0x96, 0x40, 0x86, 0x67, 0x57, 0x70, 0x51, 0xd3, 0xcb, 0xf3, 0x7a, 0x98, 0x7e, 0x3a, 0x4f, 0x3f, - 0x5d, 0xa4, 0x9f, 0xbe, 0x41, 0x1c, 0xaf, 0xf4, 0xc1, 0xeb, 0x8e, 0x36, 0x71, 0xda, 0xd1, 0xa6, - 0x43, 0x03, 0x5c, 0x09, 0x9a, 0x81, 0x2e, 0xfc, 0x02, 0x2c, 0x5e, 0x86, 0x6f, 0xe4, 0x60, 0x2f, - 0x19, 0xa5, 0x2f, 0x38, 0xbf, 0x28, 0xe0, 0x4e, 0x95, 0xda, 0x5c, 0x78, 0xdd, 0x43, 0xef, 0x96, - 0x9e, 0x16, 0xf8, 0x1f, 0x27, 0x47, 0xb3, 0xa9, 0x42, 0xfa, 0x62, 0xcf, 0x96, 0xb8, 0x67, 0x3f, - 0xbe, 0xd5, 0x16, 0x6c, 0x87, 0xed, 0xb6, 0xeb, 0x7a, 0x83, 0x34, 0x0d, 0x51, 0x85, 0xe1, 0x9f, - 0x87, 0x14, 0xed, 0x19, 0xec, 0xa8, 0x85, 0x69, 0xa0, 0x40, 0xcd, 0x10, 0xf9, 0xa2, 0x44, 0x7f, - 0x12, 0x5c, 0x61, 0xac, 0x23, 0x32, 0x14, 0x33, 0x20, 0x55, 0x29, 0x8b, 0x28, 0xa4, 0x2a, 0x65, - 0xf8, 0x32, 0x05, 0x8c, 0x2a, 0xb5, 0x37, 0x7c, 0x6c, 0x31, 0xfc, 0x79, 0xdb, 0x75, 0x4d, 0xcb, - 0xb3, 0xf1, 0x16, 0xa1, 0x0e, 0xef, 0x11, 0xff, 0xed, 0xa0, 0xa8, 0x0f, 0xc0, 0x54, 0x8b, 0x10, - 0x97, 0xdf, 0x7b, 0x86, 0x7b, 0x5c, 0x52, 0x4f, 0x3b, 0xda, 0x4c, 0xc8, 0x54, 0x1c, 0x40, 0x73, - 0x92, 0x7f, 0x55, 0x10, 0xdc, 0x07, 0x2b, 0x09, 0x03, 0x21, 0x83, 0x7a, 0x0b, 0x84, 0x09, 0x55, - 0xee, 0x4b, 0xaf, 0xb2, 0x9a, 0x07, 0xa0, 0x25, 0x00, 0x2a, 0x65, 0x51, 0x07, 0x3d, 0x3b, 0xd0, - 0x07, 0xd9, 0x2a, 0xb5, 0x77, 0xbc, 0x2d, 0x42, 0xdc, 0xaf, 0x77, 0x1d, 0x86, 0x5d, 0x87, 0x32, - 0x8c, 0xf8, 0x32, 0x49, 0x90, 0x7b, 0xdc, 0x4c, 0x0d, 0x75, 0xf3, 0x29, 0x28, 0xc4, 0xd9, 0x94, - 0xfe, 0xdc, 0x07, 0xd7, 0xf1, 0xa1, 0xc3, 0x30, 0xaa, 0x89, 0xb2, 0xa1, 0x59, 0xa5, 0x90, 0x5e, - 0xc8, 0x98, 0xd7, 0xc2, 0xed, 0xcd, 0xa0, 0x7a, 0x28, 0xfc, 0x3b, 0x05, 0x56, 0x03, 0x30, 0x37, - 0xcc, 0xbb, 0xaa, 0x63, 0xfb, 0x16, 0xc3, 0xcf, 0x76, 0x2d, 0x1f, 0xd3, 0x6d, 0x22, 0xa3, 0xb8, - 0x41, 0xbc, 0x06, 0xf6, 0x18, 0x3f, 0x43, 0x51, 0x44, 0x13, 0x3a, 0xd8, 0xd7, 0x4c, 0x7a, 0x1d, - 0x14, 0x07, 0x50, 0x36, 0x18, 0x1b, 0xcc, 0xd2, 0x80, 0x40, 0x8d, 0x91, 0x5a, 0x33, 0x64, 0x34, - 0xbc, 0xdb, 0x14, 0x44, 0xb7, 0xc9, 0x0a, 0x06, 0x67, 0x11, 0xa0, 0x79, 0x9d, 0x0a, 0xb7, 0x84, - 0x97, 0xea, 0x3e, 0x98, 0x61, 0x64, 0x0f, 0x7b, 0x35, 0xd2, 0x66, 0xb5, 0x26, 0x4f, 0xf2, 0xcc, - 0xf8, 0x93, 0xfc, 0xff, 0x81, 0x89, 0xaf, 0xda, 0xac, 0xea, 0x78, 0x14, 0xfe, 0x9a, 0x06, 0x6b, - 0xa3, 0x06, 0x5c, 0xde, 0xee, 0x73, 0x30, 0x65, 0x35, 0x49, 0xdb, 0x63, 0x4b, 0x22, 0xf2, 0x6b, - 0x9c, 0xd5, 0x1f, 0x1d, 0xed, 0xfe, 0x25, 0x58, 0x55, 0x3c, 0xd6, 0x8d, 0xbd, 0x80, 0x81, 0x66, - 0x04, 0xd8, 0xc5, 0x2e, 0x06, 0x37, 0xf5, 0xce, 0xd8, 0x45, 0x89, 0x5d, 0x54, 0xbf, 0x03, 0xb3, - 0xae, 0xb3, 0xdf, 0x76, 0x90, 0xc3, 0x8e, 0x6a, 0x8d, 0xa0, 0x4e, 0x51, 0x58, 0xf1, 0xa5, 0xa7, - 0x09, 0xac, 0x94, 0x71, 0xa3, 0x7b, 0xcf, 0xe7, 0x00, 0xa1, 0x79, 0x43, 0xee, 0x85, 0xbd, 0x00, - 0xa9, 0x3b, 0xe0, 0xea, 0xb7, 0xc4, 0xf1, 0x6a, 0x7c, 0xf2, 0x09, 0x1a, 0xc9, 0xf4, 0x72, 0x4e, - 0x0f, 0xc7, 0x22, 0x3d, 0x1a, 0x8b, 0xf4, 0xed, 0x68, 0x2c, 0x2a, 0xdd, 0x11, 0xa9, 0x74, 0x23, - 0x34, 0x21, 0x55, 0xe1, 0xab, 0xb7, 0x9a, 0x62, 0x5e, 0xe1, 0x6b, 0x2e, 0x0c, 0x7f, 0x0b, 0x5b, - 0xef, 0x3a, 0x42, 0xdb, 0xa4, 0xf7, 0xc2, 0x36, 0x23, 0xfb, 0xdd, 0x8e, 0x23, 0x8b, 0x66, 0x05, - 0x4c, 0x47, 0xfd, 0x43, 0xbe, 0x66, 0xa5, 0x5b, 0xa7, 0x1d, 0x4d, 0x8d, 0xca, 0x5d, 0x1e, 0xc2, - 0x9e, 0x56, 0x83, 0x7a, 0xaa, 0x2d, 0x35, 0xac, 0xda, 0x6a, 0x51, 0x5e, 0x23, 0x4c, 0x1d, 0x1f, - 0xa3, 0xa5, 0xe1, 0xd5, 0x73, 0x57, 0xb8, 0x3c, 0x17, 0x22, 0xf6, 0xab, 0x43, 0xf3, 0x5a, 0xb0, - 0x51, 0x16, 0xeb, 0x73, 0x06, 0x8a, 0x22, 0xa8, 0x23, 0x1a, 0x28, 0x9e, 0x31, 0x50, 0xe4, 0x91, - 0x5d, 0x49, 0x18, 0x59, 0x59, 0x1d, 0x23, 0x47, 0xb8, 0xa7, 0xac, 0x52, 0xef, 0xb1, 0xac, 0xd2, - 0xe3, 0x2e, 0xab, 0x9e, 0xe6, 0x9a, 0x19, 0xd6, 0x5c, 0x97, 0xff, 0x01, 0x20, 0x5d, 0xa5, 0xb6, - 0xea, 0x03, 0x75, 0xd0, 0x60, 0xa0, 0x9f, 0xff, 0xb7, 0x44, 0x1f, 0x38, 0x7f, 0xe7, 0x8a, 0x97, - 0x16, 0x95, 0x37, 0x73, 0x08, 0x6e, 0x0e, 0x9c, 0xd3, 0x1f, 0x0c, 0x85, 0xea, 0x0a, 0xe7, 0x1e, - 0x25, 0x10, 0x8e, 0xb3, 0x2c, 0xe7, 0xe1, 0xcb, 0x58, 0x8e, 0x84, 0x2f, 0x65, 0xf9, 0xdc, 0xe4, - 0xfa, 0x83, 0x02, 0xee, 0x0d, 0x9f, 0xcb, 0x57, 0x13, 0x38, 0xd5, 0xa7, 0x99, 0x5b, 0x1b, 0x55, - 0x53, 0x32, 0x7c, 0xa9, 0x80, 0xf9, 0xf8, 0xf9, 0x79, 0x29, 0x06, 0x3f, 0x56, 0x23, 0xb7, 0x9a, - 0x54, 0x43, 0x32, 0xf9, 0x59, 0x01, 0x8b, 0x89, 0xe6, 0xd8, 0x8d, 0x18, 0x53, 0x49, 0x40, 0x72, - 0x5f, 0x8e, 0x01, 0x44, 0xba, 0xf0, 0x3d, 0x98, 0x1b, 0x3c, 0x0d, 0x2e, 0xc6, 0x58, 0x19, 0x28, - 0x9d, 0xfb, 0x34, 0x89, 0xb4, 0x34, 0xfe, 0xa7, 0x02, 0x1e, 0x8f, 0x36, 0xca, 0x6d, 0xc6, 0xda, - 0x1b, 0x01, 0x2d, 0xb7, 0x3d, 0x4e, 0xb4, 0xbe, 0xec, 0x48, 0xf4, 0xd4, 0xc6, 0x65, 0x47, 0x12, - 0x90, 0xd8, 0xec, 0x18, 0xe5, 0x69, 0x2a, 0x6d, 0xbd, 0x3e, 0xce, 0x2b, 0x6f, 0x8e, 0xf3, 0xca, - 0x5f, 0xc7, 0x79, 0xe5, 0xd5, 0x49, 0x7e, 0xe2, 0xcd, 0x49, 0x7e, 0xe2, 0xf7, 0x93, 0xfc, 0xc4, - 0xf3, 0x27, 0x3d, 0xcf, 0x80, 0x30, 0xf8, 0xd0, 0xb5, 0xea, 0x34, 0x5a, 0x18, 0x07, 0xc5, 0xc7, - 0xc6, 0x61, 0xdf, 0x2f, 0x49, 0xfc, 0x69, 0xa8, 0x4f, 0x06, 0xe3, 0xcb, 0xa3, 0x7f, 0x03, 0x00, - 0x00, 0xff, 0xff, 0x7d, 0x9f, 0xd6, 0x5f, 0x6c, 0x12, 0x00, 0x00, + // 1168 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0x4f, 0x4f, 0x1b, 0x47, + 0x14, 0x67, 0x6d, 0x17, 0x92, 0xa1, 0x21, 0x61, 0x1b, 0x12, 0xe3, 0x26, 0x5e, 0x67, 0x5a, 0x45, + 0x54, 0x21, 0xbb, 0x98, 0x34, 0x01, 0xf5, 0x04, 0xc6, 0x6a, 0xe5, 0x14, 0xab, 0x68, 0x03, 0xaa, + 0x14, 0xa9, 0xb2, 0xd6, 0x9e, 0xc9, 0xb2, 0x65, 0xbd, 0xe3, 0xec, 0x8c, 0x29, 0xa8, 0x1f, 0x20, + 0x55, 0x4f, 0xf9, 0x06, 0xbd, 0xf7, 0xd0, 0xcf, 0xd0, 0x5b, 0xa3, 0x9e, 0x72, 0x6b, 0xd5, 0x4a, + 0x4e, 0x05, 0xdf, 0x80, 0x63, 0x4f, 0xd5, 0xec, 0xce, 0x8e, 0x6d, 0xf0, 0x62, 0xd6, 0x71, 0x0e, + 0x3d, 0xb1, 0x33, 0xef, 0xbd, 0xdf, 0xfb, 0xff, 0xe6, 0x61, 0xf0, 0x21, 0xa1, 0x4d, 0x42, 0x1d, + 0x6a, 0xd0, 0x76, 0x0b, 0xfb, 0xcf, 0xdc, 0xb6, 0x83, 0x0c, 0x76, 0xa0, 0xb7, 0x7c, 0xc2, 0x88, + 0xaa, 0x0a, 0xa2, 0xde, 0x25, 0xe6, 0xae, 0xdb, 0xc4, 0x26, 0x01, 0xd9, 0xe0, 0x5f, 0x21, 0x67, + 0x2e, 0x6f, 0x13, 0x62, 0xbb, 0xd8, 0x08, 0x4e, 0xf5, 0xf6, 0x33, 0x03, 0xb5, 0x7d, 0x8b, 0x39, + 0xc4, 0x8b, 0xe8, 0x8d, 0x00, 0xca, 0xa8, 0x5b, 0x14, 0x1b, 0xfb, 0xc5, 0x3a, 0x66, 0x56, 0xd1, + 0x68, 0x10, 0x27, 0xa2, 0x6b, 0xa7, 0xe5, 0x99, 0xd3, 0xc4, 0x94, 0x59, 0xcd, 0x96, 0x60, 0xf8, + 0x68, 0x80, 0x9d, 0xdd, 0xcf, 0x90, 0x09, 0xee, 0x83, 0xb9, 0x2a, 0xb5, 0x9f, 0xc8, 0xeb, 0x32, + 0x76, 0xb1, 0x6d, 0x31, 0xac, 0x7e, 0x02, 0x26, 0x29, 0xf6, 0x10, 0xf6, 0xb3, 0x4a, 0x41, 0x59, + 0xb8, 0x5c, 0x9a, 0x3d, 0xe9, 0x68, 0x57, 0x0e, 0xad, 0xa6, 0xfb, 0x19, 0x0c, 0xef, 0xa1, 0x29, + 0x18, 0xd4, 0x9b, 0x60, 0xca, 0x25, 0x8d, 0xbd, 0x9a, 0x83, 0xb2, 0xa9, 0x82, 0xb2, 0x90, 0x31, + 0x27, 0xf9, 0xb1, 0x82, 0xd4, 0x79, 0x70, 0x69, 0xdf, 0x72, 0x6b, 0x16, 0x42, 0x7e, 0x36, 0xcd, + 0x51, 0xcc, 0xa9, 0x7d, 0xcb, 0x5d, 0x47, 0xc8, 0x87, 0x1a, 0xb8, 0x3d, 0x50, 0xaf, 0x89, 0x69, + 0x8b, 0x78, 0x14, 0xc3, 0x6f, 0xc0, 0xcd, 0x3e, 0x86, 0x1d, 0x0f, 0x8d, 0xd1, 0x34, 0x78, 0x07, + 0x68, 0x31, 0xf0, 0xe7, 0x58, 0x50, 0x27, 0x1e, 0xda, 0x24, 0x8d, 0xbd, 0x77, 0x64, 0x41, 0x04, + 0x2f, 0x2d, 0xf8, 0x45, 0x01, 0x1f, 0xc7, 0x58, 0xb9, 0xee, 0x8d, 0xd9, 0x1e, 0xb5, 0x04, 0x32, + 0xbc, 0xba, 0x82, 0x44, 0x4d, 0x2f, 0xcf, 0xeb, 0x61, 0xf9, 0xe9, 0xbc, 0xfc, 0x74, 0x51, 0x7e, + 0xfa, 0x06, 0x71, 0xbc, 0xd2, 0x07, 0xaf, 0x3a, 0xda, 0xc4, 0x49, 0x47, 0x9b, 0x0e, 0x15, 0x70, + 0x21, 0x68, 0x06, 0xb2, 0xf0, 0x0b, 0xb0, 0x78, 0x11, 0x7b, 0x23, 0x07, 0x7b, 0x8d, 0x51, 0xfa, + 0x82, 0xf3, 0x9b, 0x02, 0x6e, 0x55, 0xa9, 0xcd, 0x99, 0xd7, 0x3d, 0xf4, 0x76, 0xe5, 0x69, 0x81, + 0xf7, 0xb8, 0x71, 0x34, 0x9b, 0x2a, 0xa4, 0xcf, 0xf7, 0x6c, 0x89, 0x7b, 0xf6, 0xf3, 0x1b, 0x6d, + 0xc1, 0x76, 0xd8, 0x6e, 0xbb, 0xae, 0x37, 0x48, 0xd3, 0x10, 0x5d, 0x18, 0xfe, 0xb9, 0x4f, 0xd1, + 0x9e, 0xc1, 0x0e, 0x5b, 0x98, 0x06, 0x02, 0xd4, 0x0c, 0x91, 0xcf, 0x2b, 0xf4, 0x47, 0x41, 0x0a, + 0x63, 0x1d, 0x91, 0xa1, 0x98, 0x01, 0xa9, 0x4a, 0x59, 0x44, 0x21, 0x55, 0x29, 0xc3, 0x17, 0x29, + 0x60, 0x54, 0xa9, 0xbd, 0xe1, 0x63, 0x8b, 0xe1, 0xcf, 0xdb, 0xae, 0x6b, 0x5a, 0x9e, 0x8d, 0xb7, + 0x08, 0x75, 0xf8, 0x8c, 0xf8, 0x7f, 0x07, 0x45, 0xbd, 0x07, 0xa6, 0x5a, 0x84, 0xb8, 0x3c, 0xef, + 0x19, 0xee, 0x71, 0x49, 0x3d, 0xe9, 0x68, 0x33, 0xa1, 0xa5, 0x82, 0x00, 0xcd, 0x49, 0xfe, 0x55, + 0x41, 0xf0, 0x39, 0x58, 0x49, 0x18, 0x08, 0x19, 0xd4, 0x1b, 0x20, 0x2c, 0xa8, 0x72, 0x5f, 0x79, + 0x95, 0xd5, 0x3c, 0x00, 0x2d, 0x01, 0x50, 0x29, 0x8b, 0x3e, 0xe8, 0xb9, 0x81, 0x3e, 0xc8, 0x56, + 0xa9, 0xbd, 0xe3, 0x6d, 0x11, 0xe2, 0x7e, 0xbd, 0xeb, 0x30, 0xec, 0x3a, 0x94, 0x61, 0xc4, 0x8f, + 0x49, 0x82, 0xdc, 0xe3, 0x66, 0x6a, 0xa8, 0x9b, 0x8f, 0x41, 0x21, 0x4e, 0xa7, 0xf4, 0xe7, 0x2e, + 0xb8, 0x8a, 0x0f, 0x1c, 0x86, 0x51, 0x4d, 0xb4, 0x0d, 0xcd, 0x2a, 0x85, 0xf4, 0x42, 0xc6, 0xbc, + 0x12, 0x5e, 0x6f, 0x06, 0xdd, 0x43, 0xe1, 0x0f, 0x69, 0xb0, 0x1a, 0x80, 0xb9, 0x61, 0xdd, 0x55, + 0x1d, 0xdb, 0xb7, 0x18, 0x7e, 0xb2, 0x6b, 0xf9, 0x98, 0x6e, 0x13, 0x19, 0xc5, 0x0d, 0xe2, 0x35, + 0xb0, 0xc7, 0x38, 0x0d, 0x45, 0x11, 0x4d, 0xe8, 0x60, 0xdf, 0x30, 0xe9, 0x75, 0x50, 0x10, 0xa0, + 0x1c, 0x30, 0x36, 0x98, 0xa5, 0x81, 0x01, 0x35, 0x46, 0x6a, 0xcd, 0xd0, 0xa2, 0xe1, 0xd3, 0xa6, + 0x20, 0xa6, 0x4d, 0x56, 0x58, 0x70, 0x1a, 0x01, 0x9a, 0x57, 0xa9, 0x70, 0x4b, 0x78, 0xa9, 0xfe, + 0xa8, 0x80, 0x19, 0x46, 0xf6, 0xb0, 0x57, 0x23, 0x6d, 0x56, 0x6b, 0xf2, 0x2a, 0xcf, 0x0c, 0xab, + 0xf2, 0x8a, 0x50, 0x33, 0x17, 0xaa, 0xe9, 0x17, 0x87, 0x89, 0xca, 0xff, 0xfd, 0x40, 0xf8, 0xab, + 0x36, 0xab, 0x72, 0xd1, 0xdf, 0xd3, 0x60, 0x6d, 0xd4, 0x54, 0xc8, 0xbc, 0x3f, 0x05, 0x53, 0x56, + 0x93, 0xb4, 0x3d, 0xb6, 0x24, 0x72, 0xb2, 0xc6, 0xcd, 0xfd, 0xab, 0xa3, 0xdd, 0xbd, 0x80, 0x55, + 0x15, 0x8f, 0x75, 0xb3, 0x22, 0x60, 0xa0, 0x19, 0x01, 0x76, 0xb1, 0x8b, 0x41, 0x0e, 0xdf, 0x1a, + 0xbb, 0x28, 0xb1, 0x8b, 0xea, 0x77, 0x60, 0xd6, 0x75, 0x9e, 0xb7, 0x1d, 0xe4, 0xb0, 0xc3, 0x5a, + 0x23, 0xe8, 0x60, 0x14, 0xce, 0x82, 0xd2, 0xe3, 0x04, 0x5a, 0xca, 0xb8, 0xd1, 0xad, 0x80, 0x33, + 0x80, 0xd0, 0xbc, 0x26, 0xef, 0xc2, 0x29, 0x81, 0xd4, 0x1d, 0x70, 0xf9, 0x5b, 0xe2, 0x78, 0x35, + 0xbe, 0x13, 0x05, 0x23, 0x66, 0x7a, 0x39, 0xa7, 0x87, 0x0b, 0x93, 0x1e, 0x2d, 0x4c, 0xfa, 0x76, + 0xb4, 0x30, 0x95, 0x6e, 0x89, 0xec, 0x5f, 0x0b, 0x55, 0x48, 0x51, 0xf8, 0xf2, 0x8d, 0xa6, 0x98, + 0x97, 0xf8, 0x99, 0x33, 0xc3, 0x3f, 0xc2, 0xa1, 0xbc, 0x8e, 0xd0, 0x36, 0xe9, 0x4d, 0xd8, 0x66, + 0xa4, 0xbf, 0x3b, 0x8b, 0x64, 0x3b, 0xad, 0x80, 0xe9, 0x68, 0xb2, 0xc8, 0x77, 0xae, 0x74, 0xe3, + 0xa4, 0xa3, 0xa9, 0xd1, 0x20, 0x90, 0x44, 0xd8, 0x33, 0x84, 0x50, 0x4f, 0x1f, 0xa6, 0x86, 0xf5, + 0x61, 0x2d, 0x2a, 0x78, 0x84, 0xa9, 0xe3, 0x63, 0xb4, 0x34, 0xbc, 0xaf, 0x6e, 0x0f, 0x2a, 0xf8, + 0x48, 0x1c, 0x9a, 0x57, 0x82, 0x8b, 0xb2, 0x38, 0x9f, 0x51, 0x50, 0x14, 0x41, 0x1d, 0x51, 0x41, + 0xf1, 0x94, 0x82, 0x22, 0x8f, 0xec, 0x4a, 0xc2, 0xc8, 0xca, 0xee, 0x18, 0x39, 0xc2, 0x3d, 0x6d, + 0x95, 0x7a, 0x87, 0x6d, 0x95, 0x1e, 0x77, 0x5b, 0xf5, 0x8c, 0xdd, 0xcc, 0xb0, 0xb1, 0xbb, 0xfc, + 0x2f, 0x00, 0xe9, 0x2a, 0xb5, 0x55, 0x1f, 0xa8, 0x83, 0x56, 0x06, 0xfd, 0xec, 0x3f, 0x2c, 0xfa, + 0xc0, 0xcd, 0x3c, 0x57, 0xbc, 0x30, 0xab, 0xcc, 0xcc, 0x01, 0xb8, 0x3e, 0x70, 0x83, 0xbf, 0x37, + 0x14, 0xaa, 0xcb, 0x9c, 0x7b, 0x90, 0x80, 0x39, 0x4e, 0xb3, 0xdc, 0x94, 0x2f, 0xa2, 0x39, 0x62, + 0xbe, 0x90, 0xe6, 0x33, 0x3b, 0xed, 0x4f, 0x0a, 0xb8, 0x33, 0x7c, 0x63, 0x5f, 0x4d, 0xe0, 0x54, + 0x9f, 0x64, 0x6e, 0x6d, 0x54, 0x49, 0x69, 0xe1, 0x0b, 0x05, 0xcc, 0xc7, 0x6f, 0xd6, 0x4b, 0x31, + 0xf8, 0xb1, 0x12, 0xb9, 0xd5, 0xa4, 0x12, 0xd2, 0x92, 0x5f, 0x15, 0xb0, 0x98, 0x68, 0xc3, 0xdd, + 0x88, 0x51, 0x95, 0x04, 0x24, 0xf7, 0xe5, 0x18, 0x40, 0xa4, 0x0b, 0xdf, 0x83, 0xb9, 0xc1, 0x7b, + 0xe2, 0x62, 0x8c, 0x96, 0x81, 0xdc, 0xb9, 0x4f, 0x93, 0x70, 0x4b, 0xe5, 0x7f, 0x2b, 0xe0, 0xe1, + 0x68, 0x4b, 0xde, 0x66, 0xac, 0xbe, 0x11, 0xd0, 0x72, 0xdb, 0xe3, 0x44, 0xeb, 0xab, 0x8e, 0x44, + 0x4f, 0x6d, 0x5c, 0x75, 0x24, 0x01, 0x89, 0xad, 0x8e, 0x51, 0x9e, 0xa6, 0xd2, 0xd6, 0xab, 0xa3, + 0xbc, 0xf2, 0xfa, 0x28, 0xaf, 0xfc, 0x73, 0x94, 0x57, 0x5e, 0x1e, 0xe7, 0x27, 0x5e, 0x1f, 0xe7, + 0x27, 0xfe, 0x3c, 0xce, 0x4f, 0x3c, 0x7d, 0xd4, 0xf3, 0x0c, 0x08, 0x85, 0xf7, 0x5d, 0xab, 0x4e, + 0xa3, 0x83, 0xb1, 0x5f, 0x7c, 0x68, 0x1c, 0xf4, 0xfd, 0xc6, 0xc4, 0x9f, 0x86, 0xfa, 0x64, 0xb0, + 0xbe, 0x3c, 0xf8, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x8b, 0x8c, 0x15, 0x86, 0x12, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. From f8756cd1bd107867fb03005adf907ad55c2f9573 Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Mon, 15 May 2023 12:43:35 -0500 Subject: [PATCH 11/15] rename to validateGammLockForSuperfluidStaking --- x/superfluid/keeper/export_test.go | 4 ++-- x/superfluid/keeper/migrate.go | 2 +- x/superfluid/keeper/unpool.go | 6 +++--- x/superfluid/keeper/unpool_test.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 37de1b46316..8e060af977a 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -51,8 +51,8 @@ func (k Keeper) SuperfluidUndelegateToConcentratedPosition(ctx sdk.Context, send return k.superfluidUndelegateToConcentratedPosition(ctx, sender, gammLockID) } -func (k Keeper) ValidateGammLockForSuperfluid(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { - return k.validateGammLockForSuperfluid(ctx, sender, poolId, lockId) +func (k Keeper) ValidateGammLockForSuperfluidStaking(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { + return k.validateGammLockForSuperfluidStaking(ctx, sender, poolId, lockId) } func (k Keeper) GetExistingLockRemainingDuration(ctx sdk.Context, lock *lockuptypes.PeriodLock) (time.Duration, error) { diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index a529fdce987..a505605d4d3 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -286,7 +286,7 @@ func (k Keeper) prepareMigration(ctx sdk.Context, sender sdk.AccAddress, lockId } // Check that lockID corresponds to sender, and contains correct denomination of LP shares. - preMigrationLock, err = k.validateGammLockForSuperfluid(ctx, sender, poolIdLeaving, lockId) + preMigrationLock, err = k.validateGammLockForSuperfluidStaking(ctx, sender, poolIdLeaving, lockId) if err != nil { return 0, 0, nil, &lockuptypes.PeriodLock{}, 0, nil, false, false, err } diff --git a/x/superfluid/keeper/unpool.go b/x/superfluid/keeper/unpool.go index 9f07c7d9284..f2be67e163c 100644 --- a/x/superfluid/keeper/unpool.go +++ b/x/superfluid/keeper/unpool.go @@ -33,7 +33,7 @@ func (k Keeper) UnpoolAllowedPools(ctx sdk.Context, sender sdk.AccAddress, poolI // 2) Consistency check that lockID corresponds to sender, and contains correct LP shares. // These are expected to be true by the caller, but good to double check // TODO: Try to minimize dependence on lock here - lock, err := k.validateGammLockForSuperfluid(ctx, sender, poolId, lockId) + lock, err := k.validateGammLockForSuperfluidStaking(ctx, sender, poolId, lockId) if err != nil { return []uint64{}, err } @@ -102,11 +102,11 @@ func (k Keeper) checkUnpoolWhitelisted(ctx sdk.Context, poolId uint64) error { return types.ErrPoolNotWhitelisted } -// validateGammLockForSuperfluid checks if the provided lock: +// validateGammLockForSuperfluidStaking checks if the provided lock: // 1) is owned by the provided sender // 2) contains only 1 coin // 3) contains the gamm LP shares associated with the provided poolId -func (k Keeper) validateGammLockForSuperfluid(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { +func (k Keeper) validateGammLockForSuperfluidStaking(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { lock, err := k.lk.GetLockByID(ctx, lockId) if err != nil { return &lockuptypes.PeriodLock{}, err diff --git a/x/superfluid/keeper/unpool_test.go b/x/superfluid/keeper/unpool_test.go index a6d8a58cd33..86d78dd9e4c 100644 --- a/x/superfluid/keeper/unpool_test.go +++ b/x/superfluid/keeper/unpool_test.go @@ -351,7 +351,7 @@ func (suite *KeeperTestSuite) TestValidateGammLockForSuperfluid() { suite.Require().NoError(err) // System under test - _, err = superfluidKeeper.ValidateGammLockForSuperfluid(ctx, tc.accountToValidate, tc.poolIdToValidate, tc.lockIdToValidate) + _, err = superfluidKeeper.ValidateGammLockForSuperfluidStaking(ctx, tc.accountToValidate, tc.poolIdToValidate, tc.lockIdToValidate) if tc.expectedError != nil { suite.Require().Error(err) suite.Require().ErrorContains(err, tc.expectedError.Error()) From a9d0c319463b5161d4114b4cab66f485ddf0c249 Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Mon, 15 May 2023 13:02:55 -0500 Subject: [PATCH 12/15] remove unused errors --- x/superfluid/types/errors.go | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/x/superfluid/types/errors.go b/x/superfluid/types/errors.go index b15c59108c8..27db450f7d5 100644 --- a/x/superfluid/types/errors.go +++ b/x/superfluid/types/errors.go @@ -53,23 +53,6 @@ func (e LockOwnerMismatchError) Error() string { return fmt.Sprintf("lock ID %d owner %s does not match provided owner %s.", e.LockId, e.LockOwner, e.ProvidedOwner) } -type SharesToMigrateDenomPrefixError struct { - Denom string - ExpectedDenomPrefix string -} - -func (e SharesToMigrateDenomPrefixError) Error() string { - return fmt.Sprintf("shares to migrate denom %s does not have expected prefix %s.", e.Denom, e.ExpectedDenomPrefix) -} - -type LockBothSuperfluidBondedAndUnbondingError struct { - LockId uint64 -} - -func (e LockBothSuperfluidBondedAndUnbondingError) Error() string { - return fmt.Sprintf("lock ID %d is both superfluid bonded and unbonding.", e.LockId) -} - type NegativeDurationError struct { Duration time.Duration } @@ -78,23 +61,6 @@ func (e NegativeDurationError) Error() string { return fmt.Sprintf("duration cannot be negative (%s)", e.Duration) } -type MigrateMoreSharesThanLockHasError struct { - SharesToMigrate string - SharesInLock string -} - -func (e MigrateMoreSharesThanLockHasError) Error() string { - return fmt.Sprintf("cannot migrate more shares (%s) than lock has (%s)", e.SharesToMigrate, e.SharesInLock) -} - -type TwoTokenBalancerPoolError struct { - NumberOfTokens int -} - -func (e TwoTokenBalancerPoolError) Error() string { - return fmt.Sprintf("balancer pool must have two tokens, got %d tokens", e.NumberOfTokens) -} - type UnexpectedDenomError struct { ExpectedDenom string ProvidedDenom string From c9a54ac736a6aaa293918f6b40d428ac2fcc7da7 Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Mon, 15 May 2023 13:25:03 -0500 Subject: [PATCH 13/15] [CL Message Audit]: MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition [1/2] (#5178) * initial push audit changes * add back proto tag * rename to validateGammLockForSuperfluidStaking * remove unused errors --- proto/osmosis/superfluid/tx.proto | 5 +- x/gamm/types/key.go | 5 +- x/superfluid/README.md | 35 ++++ x/superfluid/client/cli/tx.go | 9 ++ x/superfluid/keeper/concentrated_liquidity.go | 2 +- x/superfluid/keeper/export_test.go | 13 ++ x/superfluid/keeper/migrate.go | 9 +- x/superfluid/keeper/msg_server.go | 64 ++++---- x/superfluid/keeper/msg_server_test.go | 16 +- x/superfluid/keeper/stake.go | 6 +- x/superfluid/keeper/unpool.go | 42 +++-- x/superfluid/keeper/unpool_test.go | 146 +++++++++++++++++ x/superfluid/types/errors.go | 18 +++ x/superfluid/types/tx.pb.go | 153 +++++++++--------- 14 files changed, 375 insertions(+), 148 deletions(-) diff --git a/proto/osmosis/superfluid/tx.proto b/proto/osmosis/superfluid/tx.proto index a30df7a5299..26375d43ff9 100644 --- a/proto/osmosis/superfluid/tx.proto +++ b/proto/osmosis/superfluid/tx.proto @@ -150,8 +150,9 @@ message MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition { ]; // token_out_mins indicates minimum token to exit Balancer pool with. repeated cosmos.base.v1beta1.Coin token_out_mins = 4 [ - (gogoproto.moretags) = "yaml:\"token_out_min_amounts\"", - (gogoproto.nullable) = false + (gogoproto.moretags) = "yaml:\"token_out_mins\"", + (gogoproto.nullable) = false, + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" ]; } diff --git a/x/gamm/types/key.go b/x/gamm/types/key.go index ebfea87e42f..36a00421380 100644 --- a/x/gamm/types/key.go +++ b/x/gamm/types/key.go @@ -33,12 +33,11 @@ var ( ) func MustGetPoolIdFromShareDenom(denom string) uint64 { - numberStr := strings.TrimLeft(denom, GAMMTokenPrefix) - number, err := strconv.Atoi(numberStr) + number, err := GetPoolIdFromShareDenom(denom) if err != nil { panic(err) } - return uint64(number) + return number } func GetPoolIdFromShareDenom(denom string) (uint64, error) { diff --git a/x/superfluid/README.md b/x/superfluid/README.md index 8ff1f497b9a..b283ef7e493 100644 --- a/x/superfluid/README.md +++ b/x/superfluid/README.md @@ -376,6 +376,41 @@ It then mints concentrated liquidity shares and locks them up for the staking duration. From there, the normal superfluid delegation logic is executed. +### Create Full Range Position and Superfluid Delegate + +```{.go} +type MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition struct { + Sender string + LockId uint64 + SharesToMigrate sdk.Coin + TokenOutMins sdk.Coins +} +``` + +Upon completion, the following response is given: + +```{.go} +type MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse struct { + Amount0 string + Amount1 string + LiquidityCreated sdk.Dec + JoinTime time.Time +} +``` + +The message starts by determining which migration method to use. +- If underlying lock is superfluid bonded + - `migrateSuperfluidBondedBalancerToConcentrated` +- If underlying lock is superfluid unbonding + - `migrateSuperfluidUnbondingBalancerToConcentrated` +- If underlying lock is not superfluid bonded (vanilla lock) + - `migrateNonSuperfluidLockBalancerToConcentrated` + +It then routes to that migration message, which will migrate the gamm lock from +the previous state it was in, to the same state but in the concentrated pool. If +the sharesToMigrate is zero, then the entire lock is migrated. If only a subset +of the shares are migrated, then the remaining shares are left in the gamm pool. + ## Epochs Overall Epoch sequence diff --git a/x/superfluid/client/cli/tx.go b/x/superfluid/client/cli/tx.go index 6f7e38137dd..bb3f0de6496 100644 --- a/x/superfluid/client/cli/tx.go +++ b/x/superfluid/client/cli/tx.go @@ -37,6 +37,7 @@ func GetTxCmd() *cobra.Command { ) osmocli.AddTxCmd(cmd, NewCreateFullRangePositionAndSuperfluidDelegateCmd) osmocli.AddTxCmd(cmd, NewAddToConcentratedLiquiditySuperfluidPositionCmd) + osmocli.AddTxCmd(cmd, NewUnlockAndMigrateSharesToFullRangeConcentratedPositionCmd) return cmd } @@ -414,3 +415,11 @@ func NewAddToConcentratedLiquiditySuperfluidPositionCmd() (*osmocli.TxCliDesc, * Example: "add-to-superfluid-cl-position 10 1000000000uosmo 10000000uion", }, &types.MsgAddToConcentratedLiquiditySuperfluidPosition{} } + +func NewUnlockAndMigrateSharesToFullRangeConcentratedPositionCmd() (*osmocli.TxCliDesc, *types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) { + return &osmocli.TxCliDesc{ + Use: "unlock-and-migrate-to-cl [lock-id] [shares-to-migrate] [token-out-mins]", + Short: "unlock and migrate gamm shares to full range concentrated position", + Example: "unlock-and-migrate-cl 10 25000000000gamm/pool/2 1000000000uosmo,10000000uion", + }, &types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition{} +} diff --git a/x/superfluid/keeper/concentrated_liquidity.go b/x/superfluid/keeper/concentrated_liquidity.go index 0a2085831c8..bdba4278db9 100644 --- a/x/superfluid/keeper/concentrated_liquidity.go +++ b/x/superfluid/keeper/concentrated_liquidity.go @@ -69,7 +69,7 @@ func (k Keeper) addToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, ow // Superfluid undelegate the superfluid delegated position. // This deletes the connection between the lock and the intermediate account, deletes the synthetic lock, and burns the synthetic osmo. - intermediateAccount, err := k.SuperfluidUndelegateToConcentratedPosition(ctx, owner.String(), lockId) + intermediateAccount, err := k.superfluidUndelegateToConcentratedPosition(ctx, owner.String(), lockId) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, 0, err } diff --git a/x/superfluid/keeper/export_test.go b/x/superfluid/keeper/export_test.go index 87c821c7cb5..8e060af977a 100644 --- a/x/superfluid/keeper/export_test.go +++ b/x/superfluid/keeper/export_test.go @@ -7,6 +7,7 @@ import ( cltypes "github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/types" lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types" + "github.com/osmosis-labs/osmosis/v15/x/superfluid/types" ) var ( @@ -45,3 +46,15 @@ func (k Keeper) PrepareMigration(ctx sdk.Context, sender sdk.AccAddress, lockId func (k Keeper) AddToConcentratedLiquiditySuperfluidPosition(ctx sdk.Context, owner sdk.AccAddress, positionId uint64, amount0Added, amount1Added sdk.Int) (uint64, sdk.Int, sdk.Int, sdk.Dec, uint64, error) { return k.addToConcentratedLiquiditySuperfluidPosition(ctx, owner, positionId, amount0Added, amount1Added) } + +func (k Keeper) SuperfluidUndelegateToConcentratedPosition(ctx sdk.Context, sender string, gammLockID uint64) (types.SuperfluidIntermediaryAccount, error) { + return k.superfluidUndelegateToConcentratedPosition(ctx, sender, gammLockID) +} + +func (k Keeper) ValidateGammLockForSuperfluidStaking(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { + return k.validateGammLockForSuperfluidStaking(ctx, sender, poolId, lockId) +} + +func (k Keeper) GetExistingLockRemainingDuration(ctx sdk.Context, lock *lockuptypes.PeriodLock) (time.Duration, error) { + return k.getExistingLockRemainingDuration(ctx, lock) +} diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index b9a5ea10f60..a505605d4d3 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -66,7 +66,7 @@ func (k Keeper) migrateSuperfluidBondedBalancerToConcentrated(ctx sdk.Context, // Superfluid undelegate the superfluid delegated position. // This deletes the connection between the lock and the intermediate account, deletes the synthetic lock, and burns the synthetic osmo. - intermediateAccount, err := k.SuperfluidUndelegateToConcentratedPosition(ctx, sender.String(), lockId) + intermediateAccount, err := k.superfluidUndelegateToConcentratedPosition(ctx, sender.String(), lockId) if err != nil { return 0, sdk.Int{}, sdk.Int{}, sdk.Dec{}, time.Time{}, 0, 0, err } @@ -286,13 +286,16 @@ func (k Keeper) prepareMigration(ctx sdk.Context, sender sdk.AccAddress, lockId } // Check that lockID corresponds to sender, and contains correct denomination of LP shares. - preMigrationLock, err = k.validateLockForUnpool(ctx, sender, poolIdLeaving, lockId) + preMigrationLock, err = k.validateGammLockForSuperfluidStaking(ctx, sender, poolIdLeaving, lockId) if err != nil { return 0, 0, nil, &lockuptypes.PeriodLock{}, 0, nil, false, false, err } // Before we break the lock, we must note the time remaining on the lock. - remainingLockTime = k.getExistingLockRemainingDuration(ctx, preMigrationLock) + remainingLockTime, err = k.getExistingLockRemainingDuration(ctx, preMigrationLock) + if err != nil { + return 0, 0, nil, &lockuptypes.PeriodLock{}, 0, nil, false, false, err + } // Check if the lock has a corresponding synthetic lock. // Synthetic lock existence implies that the lock is superfluid delegated or undelegating. diff --git a/x/superfluid/keeper/msg_server.go b/x/superfluid/keeper/msg_server.go index 7b9a8339ebf..cded0c8eab4 100644 --- a/x/superfluid/keeper/msg_server.go +++ b/x/superfluid/keeper/msg_server.go @@ -132,38 +132,6 @@ func (server msgServer) LockAndSuperfluidDelegate(goCtx context.Context, msg *ty }, err } -func (server msgServer) CreateFullRangePositionAndSuperfluidDelegate(goCtx context.Context, msg *types.MsgCreateFullRangePositionAndSuperfluidDelegate) (*types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - address, err := sdk.AccAddressFromBech32(msg.Sender) - if err != nil { - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err - } - positionId, _, _, _, _, lockId, err := server.keeper.clk.CreateFullRangePositionLocked(ctx, msg.PoolId, address, msg.Coins, server.keeper.sk.GetParams(ctx).UnbondingTime) - if err != nil { - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err - } - - superfluidDelegateMsg := types.MsgSuperfluidDelegate{ - Sender: msg.Sender, - LockId: lockId, - ValAddr: msg.ValAddr, - } - - _, err = server.SuperfluidDelegate(goCtx, &superfluidDelegateMsg) - - if err != nil { - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err - } - - events.EmitCreateFullRangePositionAndSuperfluidDelegateEvent(ctx, lockId, positionId, msg.ValAddr) - - return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{ - LockID: lockId, - PositionID: positionId, - }, nil -} - func (server msgServer) UnPoolWhitelistedPool(goCtx context.Context, msg *types.MsgUnPoolWhitelistedPool) (*types.MsgUnPoolWhitelistedPoolResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) @@ -197,6 +165,38 @@ func (server msgServer) UnPoolWhitelistedPool(goCtx context.Context, msg *types. return &types.MsgUnPoolWhitelistedPoolResponse{ExitedLockIds: allExitedLockIDs}, nil } +func (server msgServer) CreateFullRangePositionAndSuperfluidDelegate(goCtx context.Context, msg *types.MsgCreateFullRangePositionAndSuperfluidDelegate) (*types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + address, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err + } + positionId, _, _, _, _, lockId, err := server.keeper.clk.CreateFullRangePositionLocked(ctx, msg.PoolId, address, msg.Coins, server.keeper.sk.GetParams(ctx).UnbondingTime) + if err != nil { + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err + } + + superfluidDelegateMsg := types.MsgSuperfluidDelegate{ + Sender: msg.Sender, + LockId: lockId, + ValAddr: msg.ValAddr, + } + + _, err = server.SuperfluidDelegate(goCtx, &superfluidDelegateMsg) + + if err != nil { + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{}, err + } + + events.EmitCreateFullRangePositionAndSuperfluidDelegateEvent(ctx, lockId, positionId, msg.ValAddr) + + return &types.MsgCreateFullRangePositionAndSuperfluidDelegateResponse{ + LockID: lockId, + PositionID: positionId, + }, nil +} + func (server msgServer) UnlockAndMigrateSharesToFullRangeConcentratedPosition(goCtx context.Context, msg *types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) (*types.MsgUnlockAndMigrateSharesToFullRangeConcentratedPositionResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) diff --git a/x/superfluid/keeper/msg_server_test.go b/x/superfluid/keeper/msg_server_test.go index e7e329ee876..3f1da11de32 100644 --- a/x/superfluid/keeper/msg_server_test.go +++ b/x/superfluid/keeper/msg_server_test.go @@ -473,15 +473,6 @@ func (suite *KeeperTestSuite) TestMsgUnPoolWhitelistedPool_Event() { func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedPosition_Event() { suite.SetupTest() - const ( - token0Denom = "token0" - ) - - // Update authorized quote denoms with the quote denom relied on by the test - concentratedLiquidityParams := suite.App.ConcentratedLiquidityKeeper.GetParams(suite.Ctx) - concentratedLiquidityParams.AuthorizedQuoteDenoms = append(concentratedLiquidityParams.AuthorizedQuoteDenoms, token0Denom) - suite.App.ConcentratedLiquidityKeeper.SetParams(suite.Ctx, concentratedLiquidityParams) - msgServer := keeper.NewMsgServerImpl(suite.App.SuperfluidKeeper) suite.FundAcc(suite.TestAccs[0], defaultAcctFunds) fullRangeCoins := sdk.NewCoins(defaultPoolAssets[0].Token, defaultPoolAssets[1].Token) @@ -489,7 +480,7 @@ func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedP // Set validators valAddrs := suite.SetupValidators([]stakingtypes.BondStatus{stakingtypes.Bonded}) - // Set balancer pool and make its respective gamm share an authorized superfluid asset + // Set balancer pool (foo and stake) and make its respective gamm share an authorized superfluid asset msg := balancer.NewMsgCreateBalancerPool(suite.TestAccs[0], balancer.PoolParams{ SwapFee: sdk.NewDecWithPrec(1, 2), ExitFee: sdk.NewDec(0), @@ -505,7 +496,7 @@ func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedP }) suite.Require().NoError(err) - // Set concentrated pool with the same denoms as the balancer pool + // Set concentrated pool with the same denoms as the balancer pool (foo and stake) clPool := suite.PrepareCustomConcentratedPool(suite.TestAccs[0], defaultPoolAssets[0].Token.Denom, defaultPoolAssets[1].Token.Denom, 1, sdk.ZeroDec()) // Set migration link between the balancer and concentrated pool @@ -529,7 +520,8 @@ func (suite *KeeperTestSuite) TestUnlockAndMigrateSharesToFullRangeConcentratedP suite.Require().NoError(err) // Execute UnlockAndMigrateSharesToFullRangeConcentratedPosition message - sender, _ := sdk.AccAddressFromBech32(locks[0].Owner) + sender, err := sdk.AccAddressFromBech32(locks[0].Owner) + suite.Require().NoError(err) _, err = msgServer.UnlockAndMigrateSharesToFullRangeConcentratedPosition(sdk.WrapSDKContext(suite.Ctx), types.NewMsgUnlockAndMigrateSharesToFullRangeConcentratedPosition(sender, locks[0].ID, locks[0].Coins[0])) suite.Require().NoError(err) diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index d643ce94c9d..64dfcb276fc 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -242,7 +242,7 @@ func (k Keeper) SuperfluidDelegate(ctx sdk.Context, sender string, lockID uint64 return k.mintOsmoTokensAndDelegate(ctx, amount, acc) } -// undelegateCommon is a helper function for SuperfluidUndelegate and SuperfluidUndelegateToConcentratedPosition. +// undelegateCommon is a helper function for SuperfluidUndelegate and superfluidUndelegateToConcentratedPosition. // It performs the following tasks: // - checks that the lock is valid for superfluid staking // - gets the intermediary account associated with the lock id @@ -300,10 +300,10 @@ func (k Keeper) SuperfluidUndelegate(ctx sdk.Context, sender string, lockID uint return k.createSyntheticLockup(ctx, lockID, intermediaryAcc, unlockingStatus) } -// SuperfluidUndelegateToConcentratedPosition starts undelegating superfluid delegated position for the given lock. It behaves similarly to SuperfluidUndelegate, +// superfluidUndelegateToConcentratedPosition starts undelegating superfluid delegated position for the given lock. It behaves similarly to SuperfluidUndelegate, // however it does not create a new synthetic lockup representing the unstaking side. This is because at the time this function is called, the new concentrated liquidity side // lock has not yet been created. Once the new cl side lock is created, the synthetic lockup representing the unstaking side is created. -func (k Keeper) SuperfluidUndelegateToConcentratedPosition(ctx sdk.Context, sender string, gammLockID uint64) (types.SuperfluidIntermediaryAccount, error) { +func (k Keeper) superfluidUndelegateToConcentratedPosition(ctx sdk.Context, sender string, gammLockID uint64) (types.SuperfluidIntermediaryAccount, error) { return k.undelegateCommon(ctx, sender, gammLockID) } diff --git a/x/superfluid/keeper/unpool.go b/x/superfluid/keeper/unpool.go index 0f3dfadc4b5..f2be67e163c 100644 --- a/x/superfluid/keeper/unpool.go +++ b/x/superfluid/keeper/unpool.go @@ -33,14 +33,17 @@ func (k Keeper) UnpoolAllowedPools(ctx sdk.Context, sender sdk.AccAddress, poolI // 2) Consistency check that lockID corresponds to sender, and contains correct LP shares. // These are expected to be true by the caller, but good to double check // TODO: Try to minimize dependence on lock here - lock, err := k.validateLockForUnpool(ctx, sender, poolId, lockId) + lock, err := k.validateGammLockForSuperfluidStaking(ctx, sender, poolId, lockId) if err != nil { return []uint64{}, err } gammSharesInLock := lock.Coins[0] // 3) Get remaining duration on the lock. Handle if the lock was unbonding. - lockRemainingDuration := k.getExistingLockRemainingDuration(ctx, lock) + lockRemainingDuration, err := k.getExistingLockRemainingDuration(ctx, lock) + if err != nil { + return []uint64{}, err + } // 4) If superfluid delegated, superfluid undelegate err = k.unbondSuperfluidIfExists(ctx, sender, lockId) @@ -99,39 +102,48 @@ func (k Keeper) checkUnpoolWhitelisted(ctx sdk.Context, poolId uint64) error { return types.ErrPoolNotWhitelisted } -// check if pool is whitelisted for unpool -func (k Keeper) validateLockForUnpool(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { +// validateGammLockForSuperfluidStaking checks if the provided lock: +// 1) is owned by the provided sender +// 2) contains only 1 coin +// 3) contains the gamm LP shares associated with the provided poolId +func (k Keeper) validateGammLockForSuperfluidStaking(ctx sdk.Context, sender sdk.AccAddress, poolId uint64, lockId uint64) (*lockuptypes.PeriodLock, error) { lock, err := k.lk.GetLockByID(ctx, lockId) if err != nil { - return lock, err + return &lockuptypes.PeriodLock{}, err } - // consistency check: validate lock owner - // However, we expect this to be guaranteed by caller though. + // Validate lock owner. + // We expect this to be guaranteed by caller, though. if lock.Owner != sender.String() { - return lock, lockuptypes.ErrNotLockOwner + return &lockuptypes.PeriodLock{}, lockuptypes.ErrNotLockOwner } if lock.Coins.Len() != 1 { - return lock, types.ErrMultipleCoinsLockupNotSupported + return &lockuptypes.PeriodLock{}, types.ErrMultipleCoinsLockupNotSupported } gammShare := lock.Coins[0] if gammShare.Denom != gammtypes.GetPoolShareDenom(poolId) { - return lock, types.ErrLockUnpoolNotAllowed + return &lockuptypes.PeriodLock{}, types.UnexpectedDenomError{ExpectedDenom: gammtypes.GetPoolShareDenom(poolId), ProvidedDenom: gammShare.Denom} } return lock, nil } -func (k Keeper) getExistingLockRemainingDuration(ctx sdk.Context, lock *lockuptypes.PeriodLock) time.Duration { +// getExistingLockRemainingDuration returns the time remaining until the lock is finished unlocking. +// If the lock is not unlocking, then the duration field of the lock is returned. +func (k Keeper) getExistingLockRemainingDuration(ctx sdk.Context, lock *lockuptypes.PeriodLock) (time.Duration, error) { if lock.IsUnlocking() { - // lock is unlocking, so remaining duration equals lock.EndTime - ctx.BlockTime + // Lock is unlocking, so remaining duration equals lock.EndTime - ctx.BlockTime. remainingDuration := lock.EndTime.Sub(ctx.BlockTime()) - return remainingDuration + // Defense in depth, ensure the duration is not negative. + if remainingDuration < 0 { + return 0, types.NegativeDurationError{Duration: remainingDuration} + } + return remainingDuration, nil } - // lock is bonded, thus the time it should take to unlock is lock.Duration - return lock.Duration + // Lock is not unlocking, thus the time it should take to unlock is the locks duration. + return lock.Duration, nil } // TODO: Review this in more depth diff --git a/x/superfluid/keeper/unpool_test.go b/x/superfluid/keeper/unpool_test.go index 19433dab95f..86d78dd9e4c 100644 --- a/x/superfluid/keeper/unpool_test.go +++ b/x/superfluid/keeper/unpool_test.go @@ -1,14 +1,17 @@ package keeper_test import ( + "fmt" "time" + 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" "github.com/osmosis-labs/osmosis/v15/x/gamm/pool-models/balancer" gammtypes "github.com/osmosis-labs/osmosis/v15/x/gamm/types" + lockuptypes "github.com/osmosis-labs/osmosis/v15/x/lockup/types" "github.com/osmosis-labs/osmosis/v15/x/superfluid/keeper" "github.com/osmosis-labs/osmosis/v15/x/superfluid/types" ) @@ -280,3 +283,146 @@ func (suite *KeeperTestSuite) TestUnpoolAllowedPools_WhiteList() { suite.Error(err) suite.Require().ErrorIs(err, types.ErrPoolNotWhitelisted) } + +func (suite *KeeperTestSuite) TestValidateGammLockForSuperfluid() { + lockCreator := suite.TestAccs[0] + nonLockCreator := suite.TestAccs[1] + type sendTest struct { + fundsToLock sdk.Coins + accountToValidate sdk.AccAddress + poolIdToValidate uint64 + lockIdToValidate uint64 + expectedError error + } + testCases := map[string]sendTest{ + "happy path": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + }, + "error: non-existent lock ID": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 2, + expectedError: errorsmod.Wrap(lockuptypes.ErrLockupNotFound, fmt.Sprintf("lock with ID %d does not exist", 2)), + }, + "error: mismatched owner": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: nonLockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + expectedError: lockuptypes.ErrNotLockOwner, + }, + "error: more than one coin in lock": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100)), + sdk.NewCoin("gamm/pool/2", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + expectedError: types.ErrMultipleCoinsLockupNotSupported, + }, + "error: wrong pool ID provided when compared to lock denom": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("gamm/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 2, + lockIdToValidate: 1, + expectedError: types.UnexpectedDenomError{ExpectedDenom: gammtypes.GetPoolShareDenom(2), ProvidedDenom: "gamm/pool/1"}, + }, + "error: right pool ID provided but not gamm/pool/ prefix": { + fundsToLock: sdk.NewCoins(sdk.NewCoin("cl/pool/1", sdk.NewInt(100))), + accountToValidate: lockCreator, + poolIdToValidate: 1, + lockIdToValidate: 1, + expectedError: types.UnexpectedDenomError{ExpectedDenom: gammtypes.GetPoolShareDenom(1), ProvidedDenom: "cl/pool/1"}, + }, + } + + for name, tc := range testCases { + suite.Run(name, func() { + suite.SetupTest() + + ctx := suite.Ctx + superfluidKeeper := suite.App.SuperfluidKeeper + + suite.FundAcc(lockCreator, tc.fundsToLock) + _, err := suite.App.LockupKeeper.CreateLock(ctx, lockCreator, tc.fundsToLock, time.Hour) + suite.Require().NoError(err) + + // System under test + _, err = superfluidKeeper.ValidateGammLockForSuperfluidStaking(ctx, tc.accountToValidate, tc.poolIdToValidate, tc.lockIdToValidate) + if tc.expectedError != nil { + suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.expectedError.Error()) + return + } + suite.Require().NoError(err) + }) + } +} + +func (suite *KeeperTestSuite) TestGetExistingLockRemainingDuration() { + defaultJoinTime := suite.Ctx.BlockTime() + lockCreator := suite.TestAccs[0] + type sendTest struct { + isUnlocking bool + lockDuration time.Duration + timePassed time.Duration + expectedRemainingDuration time.Duration + expectedError error + } + testCases := map[string]sendTest{ + "lock that is not unlocking": { + isUnlocking: false, + lockDuration: time.Hour, + timePassed: time.Hour, + expectedRemainingDuration: time.Hour, + }, + "lock that is unlocking": { + isUnlocking: true, + lockDuration: time.Hour, + timePassed: time.Minute, + expectedRemainingDuration: time.Hour - time.Minute, + }, + "error: negative duration": { + isUnlocking: true, + lockDuration: time.Hour, + timePassed: time.Hour + time.Minute, + expectedError: types.NegativeDurationError{Duration: -time.Minute}, + }, + } + + for name, tc := range testCases { + suite.Run(name, func() { + suite.SetupTest() + ctx := suite.Ctx.WithBlockTime(defaultJoinTime) + + superfluidKeeper := suite.App.SuperfluidKeeper + + suite.FundAcc(lockCreator, defaultAcctFunds) + lock, err := suite.App.LockupKeeper.CreateLock(ctx, lockCreator, defaultAcctFunds, tc.lockDuration) + suite.Require().NoError(err) + + if tc.isUnlocking { + _, err = suite.App.LockupKeeper.BeginUnlock(ctx, lock.ID, defaultAcctFunds) + suite.Require().NoError(err) + } + + ctx = ctx.WithBlockTime(defaultJoinTime.Add(tc.timePassed)) + + lockAfterTime, err := suite.App.LockupKeeper.GetLockByID(ctx, lock.ID) + suite.Require().NoError(err) + + // System under test + remainingDuration, err := superfluidKeeper.GetExistingLockRemainingDuration(ctx, lockAfterTime) + if tc.expectedError != nil { + suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.expectedError.Error()) + return + } + suite.Require().NoError(err) + suite.Require().Equal(tc.expectedRemainingDuration, remainingDuration) + }) + } +} diff --git a/x/superfluid/types/errors.go b/x/superfluid/types/errors.go index 45f8c886c8a..27db450f7d5 100644 --- a/x/superfluid/types/errors.go +++ b/x/superfluid/types/errors.go @@ -2,6 +2,7 @@ package types import ( fmt "fmt" + "time" errorsmod "cosmossdk.io/errors" ) @@ -51,3 +52,20 @@ type LockOwnerMismatchError struct { func (e LockOwnerMismatchError) Error() string { return fmt.Sprintf("lock ID %d owner %s does not match provided owner %s.", e.LockId, e.LockOwner, e.ProvidedOwner) } + +type NegativeDurationError struct { + Duration time.Duration +} + +func (e NegativeDurationError) Error() string { + return fmt.Sprintf("duration cannot be negative (%s)", e.Duration) +} + +type UnexpectedDenomError struct { + ExpectedDenom string + ProvidedDenom string +} + +func (e UnexpectedDenomError) Error() string { + return fmt.Sprintf("provided denom (%s) was expected to be formatted as follows: %s", e.ProvidedDenom, e.ExpectedDenom) +} diff --git a/x/superfluid/types/tx.pb.go b/x/superfluid/types/tx.pb.go index 786cc740f9b..ecd48822493 100644 --- a/x/superfluid/types/tx.pb.go +++ b/x/superfluid/types/tx.pb.go @@ -766,7 +766,7 @@ type MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition struct { LockId uint64 `protobuf:"varint,2,opt,name=lock_id,json=lockId,proto3" json:"lock_id,omitempty" yaml:"lock_id"` SharesToMigrate types.Coin `protobuf:"bytes,3,opt,name=shares_to_migrate,json=sharesToMigrate,proto3" json:"shares_to_migrate" yaml:"shares_to_migrate"` // token_out_mins indicates minimum token to exit Balancer pool with. - TokenOutMins []types.Coin `protobuf:"bytes,4,rep,name=token_out_mins,json=tokenOutMins,proto3" json:"token_out_mins" yaml:"token_out_min_amounts"` + TokenOutMins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,4,rep,name=token_out_mins,json=tokenOutMins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"token_out_mins" yaml:"token_out_mins"` } func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) Reset() { @@ -827,7 +827,7 @@ func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) GetSharesToMi return types.Coin{} } -func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) GetTokenOutMins() []types.Coin { +func (m *MsgUnlockAndMigrateSharesToFullRangeConcentratedPosition) GetTokenOutMins() github_com_cosmos_cosmos_sdk_types.Coins { if m != nil { return m.TokenOutMins } @@ -1040,81 +1040,80 @@ func init() { func init() { proto.RegisterFile("osmosis/superfluid/tx.proto", fileDescriptor_55b645f187d22814) } var fileDescriptor_55b645f187d22814 = []byte{ - // 1173 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0x41, 0x4f, 0x1b, 0x47, - 0x14, 0x66, 0x6d, 0x17, 0x92, 0xa1, 0x90, 0xb0, 0x0d, 0x89, 0x71, 0x89, 0xd7, 0x99, 0x46, 0x11, - 0x55, 0xc8, 0x2e, 0x26, 0x4d, 0x40, 0x3d, 0x81, 0xb1, 0x5a, 0x39, 0xc5, 0x2a, 0xda, 0x80, 0x2a, - 0x45, 0xaa, 0x56, 0x6b, 0xcf, 0x64, 0xd9, 0xb2, 0xde, 0x71, 0x76, 0xc6, 0x14, 0xd4, 0x1f, 0x90, - 0x6b, 0xfe, 0x41, 0xef, 0x3d, 0xf4, 0x37, 0xf4, 0xd6, 0xa8, 0xa7, 0xdc, 0x5a, 0xb5, 0x92, 0x53, - 0xc1, 0x3f, 0xe0, 0xd8, 0x53, 0x35, 0xbb, 0xb3, 0xeb, 0x35, 0x78, 0x31, 0xeb, 0x90, 0x43, 0x4e, - 0xec, 0xcc, 0xbc, 0xf7, 0x7d, 0xef, 0xbd, 0x79, 0xef, 0xcd, 0xc3, 0xe0, 0x53, 0x42, 0x5b, 0x84, - 0xda, 0x54, 0xa3, 0x9d, 0x36, 0xf6, 0x9e, 0x3b, 0x1d, 0x1b, 0x69, 0xec, 0x40, 0x6d, 0x7b, 0x84, - 0x11, 0x59, 0x16, 0x87, 0x6a, 0xef, 0xb0, 0x70, 0xc3, 0x22, 0x16, 0xf1, 0x8f, 0x35, 0xfe, 0x15, - 0x48, 0x16, 0x8a, 0x16, 0x21, 0x96, 0x83, 0x35, 0x7f, 0xd5, 0xe8, 0x3c, 0xd7, 0x50, 0xc7, 0x33, - 0x99, 0x4d, 0xdc, 0xf0, 0xbc, 0xe9, 0x43, 0x69, 0x0d, 0x93, 0x62, 0x6d, 0xbf, 0xdc, 0xc0, 0xcc, - 0x2c, 0x6b, 0x4d, 0x62, 0x87, 0xe7, 0xca, 0x69, 0x7d, 0x66, 0xb7, 0x30, 0x65, 0x66, 0xab, 0x2d, - 0x04, 0x3e, 0x1b, 0x60, 0x67, 0xef, 0x33, 0x10, 0x82, 0xfb, 0x60, 0xb6, 0x4e, 0xad, 0xa7, 0xd1, - 0x76, 0x15, 0x3b, 0xd8, 0x32, 0x19, 0x96, 0x3f, 0x07, 0xe3, 0x14, 0xbb, 0x08, 0x7b, 0x79, 0xa9, - 0x24, 0x2d, 0x5c, 0xad, 0xcc, 0x9c, 0x74, 0x95, 0xa9, 0x43, 0xb3, 0xe5, 0x7c, 0x09, 0x83, 0x7d, - 0xa8, 0x0b, 0x01, 0xf9, 0x16, 0x98, 0x70, 0x48, 0x73, 0xcf, 0xb0, 0x51, 0x3e, 0x53, 0x92, 0x16, - 0x72, 0xfa, 0x38, 0x5f, 0xd6, 0x90, 0x3c, 0x07, 0xae, 0xec, 0x9b, 0x8e, 0x61, 0x22, 0xe4, 0xe5, - 0xb3, 0x1c, 0x45, 0x9f, 0xd8, 0x37, 0x9d, 0x75, 0x84, 0x3c, 0xa8, 0x80, 0xdb, 0x03, 0x79, 0x75, - 0x4c, 0xdb, 0xc4, 0xa5, 0x18, 0x7e, 0x0f, 0x6e, 0xf5, 0x09, 0xec, 0xb8, 0xe8, 0x12, 0x4d, 0x83, - 0x77, 0x80, 0x92, 0x00, 0x7f, 0x8e, 0x05, 0x0d, 0xe2, 0xa2, 0x4d, 0xd2, 0xdc, 0x7b, 0x4f, 0x16, - 0x84, 0xf0, 0x91, 0x05, 0xbf, 0x4a, 0xe0, 0x6e, 0x82, 0x95, 0xeb, 0xee, 0x25, 0xdb, 0x23, 0x57, - 0x40, 0x8e, 0x67, 0x97, 0x7f, 0x51, 0x93, 0xcb, 0x73, 0x6a, 0x90, 0x7e, 0x2a, 0x4f, 0x3f, 0x55, - 0xa4, 0x9f, 0xba, 0x41, 0x6c, 0xb7, 0xf2, 0xc9, 0xeb, 0xae, 0x32, 0x76, 0xd2, 0x55, 0x26, 0x03, - 0x02, 0xae, 0x04, 0x75, 0x5f, 0x17, 0x7e, 0x0d, 0x16, 0x2f, 0x62, 0x6f, 0xe8, 0x60, 0xdc, 0x18, - 0xa9, 0x2f, 0x38, 0xbf, 0x4b, 0x60, 0xbe, 0x4e, 0x2d, 0x2e, 0xbc, 0xee, 0xa2, 0x77, 0x4b, 0x4f, - 0x13, 0x7c, 0xc4, 0x8d, 0xa3, 0xf9, 0x4c, 0x29, 0x7b, 0xbe, 0x67, 0x4b, 0xdc, 0xb3, 0x5f, 0xde, - 0x2a, 0x0b, 0x96, 0xcd, 0x76, 0x3b, 0x0d, 0xb5, 0x49, 0x5a, 0x9a, 0xa8, 0xc2, 0xe0, 0xcf, 0x03, - 0x8a, 0xf6, 0x34, 0x76, 0xd8, 0xc6, 0xd4, 0x57, 0xa0, 0x7a, 0x80, 0x7c, 0x5e, 0xa2, 0x3f, 0xf6, - 0xaf, 0x30, 0xd1, 0x91, 0x28, 0x14, 0xd3, 0x20, 0x53, 0xab, 0x8a, 0x28, 0x64, 0x6a, 0x55, 0xf8, - 0x32, 0x03, 0xb4, 0x3a, 0xb5, 0x36, 0x3c, 0x6c, 0x32, 0xfc, 0x55, 0xc7, 0x71, 0x74, 0xd3, 0xb5, - 0xf0, 0x16, 0xa1, 0x36, 0xef, 0x11, 0x1f, 0x76, 0x50, 0xe4, 0xfb, 0x60, 0xa2, 0x4d, 0x88, 0xc3, - 0xef, 0x3d, 0xc7, 0x3d, 0xae, 0xc8, 0x27, 0x5d, 0x65, 0x3a, 0xb0, 0x54, 0x1c, 0x40, 0x7d, 0x9c, - 0x7f, 0xd5, 0x10, 0x7c, 0x01, 0x56, 0x52, 0x06, 0x22, 0x0a, 0xea, 0x4d, 0x10, 0x24, 0x54, 0xb5, - 0x2f, 0xbd, 0xaa, 0x72, 0x11, 0x80, 0xb6, 0x00, 0xa8, 0x55, 0x45, 0x1d, 0xc4, 0x76, 0xa0, 0x07, - 0xf2, 0x75, 0x6a, 0xed, 0xb8, 0x5b, 0x84, 0x38, 0xdf, 0xed, 0xda, 0x0c, 0x3b, 0x36, 0x65, 0x18, - 0xf1, 0x65, 0x9a, 0x20, 0xc7, 0xdc, 0xcc, 0x0c, 0x75, 0xf3, 0x09, 0x28, 0x25, 0x71, 0x46, 0xfe, - 0xdc, 0x03, 0xd7, 0xf0, 0x81, 0xcd, 0x30, 0x32, 0x44, 0xd9, 0xd0, 0xbc, 0x54, 0xca, 0x2e, 0xe4, - 0xf4, 0xa9, 0x60, 0x7b, 0xd3, 0xaf, 0x1e, 0x0a, 0x8f, 0x32, 0x60, 0xd5, 0x07, 0x73, 0x82, 0xbc, - 0xab, 0xdb, 0x96, 0x67, 0x32, 0xfc, 0x74, 0xd7, 0xf4, 0x30, 0xdd, 0x26, 0x51, 0x14, 0x37, 0x88, - 0xdb, 0xc4, 0x2e, 0xe3, 0x67, 0x28, 0x8c, 0x68, 0x4a, 0x07, 0xfb, 0x9a, 0x49, 0xdc, 0x41, 0x71, - 0x00, 0xa3, 0x06, 0x63, 0x81, 0x19, 0xea, 0x1b, 0x60, 0x30, 0x62, 0xb4, 0x02, 0x8b, 0x86, 0x77, - 0x9b, 0x92, 0xe8, 0x36, 0x79, 0x61, 0xc1, 0x69, 0x04, 0xa8, 0x5f, 0xa3, 0xc2, 0x2d, 0xe1, 0xa5, - 0x8c, 0xc1, 0x34, 0x23, 0x7b, 0xd8, 0x35, 0x48, 0x87, 0x19, 0x2d, 0x9e, 0xe4, 0xb9, 0x61, 0x49, - 0x7e, 0x57, 0xb0, 0xcc, 0x07, 0x2c, 0x7d, 0xea, 0x86, 0xd9, 0x22, 0x1d, 0x97, 0x51, 0xa8, 0x7f, - 0xec, 0xef, 0x7f, 0xdb, 0x61, 0x75, 0xdb, 0xa5, 0xf0, 0x8f, 0x2c, 0x58, 0x1b, 0x35, 0xc8, 0xd1, - 0x8d, 0x3e, 0x03, 0x13, 0x01, 0xfc, 0x92, 0x88, 0xf6, 0x1a, 0xb7, 0xe4, 0xef, 0xae, 0x72, 0xef, - 0x02, 0xe5, 0x56, 0x73, 0x59, 0x2f, 0xde, 0x02, 0x06, 0xea, 0x21, 0x60, 0x0f, 0xbb, 0xec, 0xdf, - 0xce, 0x3b, 0x63, 0x97, 0x23, 0xec, 0xb2, 0xfc, 0x23, 0x98, 0x71, 0xec, 0x17, 0x1d, 0x1b, 0xd9, - 0xec, 0xd0, 0x68, 0xfa, 0xb5, 0x89, 0x82, 0x2a, 0xaf, 0x3c, 0x49, 0xc1, 0x52, 0xc5, 0xcd, 0xde, - 0xdd, 0x9e, 0x01, 0x84, 0xfa, 0xf5, 0x68, 0x2f, 0xa8, 0x7f, 0x24, 0xef, 0x80, 0xab, 0x3f, 0x10, - 0xdb, 0x35, 0xf8, 0xb4, 0xe3, 0x37, 0x8f, 0xc9, 0xe5, 0x82, 0x1a, 0x8c, 0x42, 0x6a, 0x38, 0x0a, - 0xa9, 0xdb, 0xe1, 0x28, 0x54, 0x99, 0x17, 0x17, 0x7b, 0x3d, 0xa0, 0x88, 0x54, 0xe1, 0xab, 0xb7, - 0x8a, 0xa4, 0x5f, 0xe1, 0x6b, 0x2e, 0x0c, 0xff, 0x0c, 0xda, 0xed, 0x3a, 0x42, 0xdb, 0x24, 0x7e, - 0x61, 0x9b, 0x21, 0x7f, 0xaf, 0xcb, 0x44, 0x85, 0xb2, 0x02, 0x26, 0xc3, 0x9e, 0x11, 0xbd, 0x60, - 0x95, 0x9b, 0x27, 0x5d, 0x45, 0x0e, 0x4b, 0x3c, 0x3a, 0x84, 0xb1, 0xf6, 0x82, 0x62, 0x15, 0x96, - 0x19, 0x56, 0x61, 0x46, 0x98, 0xcb, 0x08, 0x53, 0xdb, 0xc3, 0x68, 0x69, 0x78, 0xc5, 0xdc, 0x16, - 0x2e, 0xcf, 0xc6, 0x73, 0x39, 0x54, 0x87, 0xfa, 0x94, 0xbf, 0x51, 0x15, 0xeb, 0x33, 0x04, 0x65, - 0x11, 0xd4, 0x11, 0x09, 0xca, 0xa7, 0x08, 0xca, 0x3c, 0xb2, 0x2b, 0x29, 0x23, 0x1b, 0x55, 0xc7, - 0xc8, 0x11, 0x8e, 0x95, 0x55, 0xe6, 0x3d, 0x96, 0x55, 0xf6, 0xb2, 0xcb, 0x2a, 0xd6, 0x50, 0x73, - 0xc3, 0x1a, 0xea, 0xf2, 0x7f, 0x00, 0x64, 0xeb, 0xd4, 0x92, 0x3d, 0x20, 0x0f, 0x1a, 0x06, 0xd4, - 0xb3, 0xff, 0x8a, 0xa8, 0x03, 0x67, 0xee, 0x42, 0xf9, 0xc2, 0xa2, 0xd1, 0xcd, 0x1c, 0x80, 0x1b, - 0x03, 0x67, 0xf3, 0xfb, 0x43, 0xa1, 0x7a, 0xc2, 0x85, 0x87, 0x29, 0x84, 0x93, 0x98, 0xa3, 0x19, - 0xf8, 0x22, 0xcc, 0xa1, 0xf0, 0x85, 0x98, 0xcf, 0x4c, 0xab, 0x3f, 0x4b, 0xe0, 0xce, 0xf0, 0x59, - 0x7c, 0x35, 0x85, 0x53, 0x7d, 0x9a, 0x85, 0xb5, 0x51, 0x35, 0x23, 0x0b, 0x5f, 0x4a, 0x60, 0x2e, - 0x79, 0x66, 0x5e, 0x4a, 0xc0, 0x4f, 0xd4, 0x28, 0xac, 0xa6, 0xd5, 0x88, 0x2c, 0xf9, 0x4d, 0x02, - 0x8b, 0xa9, 0x66, 0xd7, 0x8d, 0x04, 0xaa, 0x34, 0x20, 0x85, 0x6f, 0x2e, 0x01, 0x24, 0x72, 0xe1, - 0x27, 0x30, 0x3b, 0x78, 0x02, 0x5c, 0x4c, 0x60, 0x19, 0x28, 0x5d, 0xf8, 0x22, 0x8d, 0x74, 0x44, - 0xfe, 0x8f, 0x04, 0x1e, 0x8d, 0x36, 0xbe, 0x6d, 0x26, 0xf2, 0x8d, 0x80, 0x56, 0xd8, 0xbe, 0x4c, - 0xb4, 0xbe, 0xec, 0x48, 0xf5, 0xd4, 0x26, 0x65, 0x47, 0x1a, 0x90, 0xc4, 0xec, 0x18, 0xe5, 0x69, - 0xaa, 0x6c, 0xbd, 0x3e, 0x2a, 0x4a, 0x6f, 0x8e, 0x8a, 0xd2, 0xbf, 0x47, 0x45, 0xe9, 0xd5, 0x71, - 0x71, 0xec, 0xcd, 0x71, 0x71, 0xec, 0xaf, 0xe3, 0xe2, 0xd8, 0xb3, 0xc7, 0xb1, 0x67, 0x40, 0x10, - 0x3e, 0x70, 0xcc, 0x06, 0x0d, 0x17, 0xda, 0x7e, 0xf9, 0x91, 0x76, 0xd0, 0xf7, 0xeb, 0x11, 0x7f, - 0x1a, 0x1a, 0xe3, 0xfe, 0xf8, 0xf2, 0xf0, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x23, 0xf9, - 0xff, 0x60, 0x12, 0x00, 0x00, + // 1168 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0x4f, 0x4f, 0x1b, 0x47, + 0x14, 0x67, 0x6d, 0x17, 0x92, 0xa1, 0x21, 0x61, 0x1b, 0x12, 0xe3, 0x26, 0x5e, 0x67, 0x5a, 0x45, + 0x54, 0x21, 0xbb, 0x98, 0x34, 0x01, 0xf5, 0x04, 0xc6, 0x6a, 0xe5, 0x14, 0xab, 0x68, 0x03, 0xaa, + 0x14, 0xa9, 0xb2, 0xd6, 0x9e, 0xc9, 0xb2, 0x65, 0xbd, 0xe3, 0xec, 0x8c, 0x29, 0xa8, 0x1f, 0x20, + 0x55, 0x4f, 0xf9, 0x06, 0xbd, 0xf7, 0xd0, 0xcf, 0xd0, 0x5b, 0xa3, 0x9e, 0x72, 0x6b, 0xd5, 0x4a, + 0x4e, 0x05, 0xdf, 0x80, 0x63, 0x4f, 0xd5, 0xec, 0xce, 0x8e, 0x6d, 0xf0, 0x62, 0xd6, 0x71, 0x0e, + 0x3d, 0xb1, 0x33, 0xef, 0xbd, 0xdf, 0xfb, 0xff, 0xe6, 0x61, 0xf0, 0x21, 0xa1, 0x4d, 0x42, 0x1d, + 0x6a, 0xd0, 0x76, 0x0b, 0xfb, 0xcf, 0xdc, 0xb6, 0x83, 0x0c, 0x76, 0xa0, 0xb7, 0x7c, 0xc2, 0x88, + 0xaa, 0x0a, 0xa2, 0xde, 0x25, 0xe6, 0xae, 0xdb, 0xc4, 0x26, 0x01, 0xd9, 0xe0, 0x5f, 0x21, 0x67, + 0x2e, 0x6f, 0x13, 0x62, 0xbb, 0xd8, 0x08, 0x4e, 0xf5, 0xf6, 0x33, 0x03, 0xb5, 0x7d, 0x8b, 0x39, + 0xc4, 0x8b, 0xe8, 0x8d, 0x00, 0xca, 0xa8, 0x5b, 0x14, 0x1b, 0xfb, 0xc5, 0x3a, 0x66, 0x56, 0xd1, + 0x68, 0x10, 0x27, 0xa2, 0x6b, 0xa7, 0xe5, 0x99, 0xd3, 0xc4, 0x94, 0x59, 0xcd, 0x96, 0x60, 0xf8, + 0x68, 0x80, 0x9d, 0xdd, 0xcf, 0x90, 0x09, 0xee, 0x83, 0xb9, 0x2a, 0xb5, 0x9f, 0xc8, 0xeb, 0x32, + 0x76, 0xb1, 0x6d, 0x31, 0xac, 0x7e, 0x02, 0x26, 0x29, 0xf6, 0x10, 0xf6, 0xb3, 0x4a, 0x41, 0x59, + 0xb8, 0x5c, 0x9a, 0x3d, 0xe9, 0x68, 0x57, 0x0e, 0xad, 0xa6, 0xfb, 0x19, 0x0c, 0xef, 0xa1, 0x29, + 0x18, 0xd4, 0x9b, 0x60, 0xca, 0x25, 0x8d, 0xbd, 0x9a, 0x83, 0xb2, 0xa9, 0x82, 0xb2, 0x90, 0x31, + 0x27, 0xf9, 0xb1, 0x82, 0xd4, 0x79, 0x70, 0x69, 0xdf, 0x72, 0x6b, 0x16, 0x42, 0x7e, 0x36, 0xcd, + 0x51, 0xcc, 0xa9, 0x7d, 0xcb, 0x5d, 0x47, 0xc8, 0x87, 0x1a, 0xb8, 0x3d, 0x50, 0xaf, 0x89, 0x69, + 0x8b, 0x78, 0x14, 0xc3, 0x6f, 0xc0, 0xcd, 0x3e, 0x86, 0x1d, 0x0f, 0x8d, 0xd1, 0x34, 0x78, 0x07, + 0x68, 0x31, 0xf0, 0xe7, 0x58, 0x50, 0x27, 0x1e, 0xda, 0x24, 0x8d, 0xbd, 0x77, 0x64, 0x41, 0x04, + 0x2f, 0x2d, 0xf8, 0x45, 0x01, 0x1f, 0xc7, 0x58, 0xb9, 0xee, 0x8d, 0xd9, 0x1e, 0xb5, 0x04, 0x32, + 0xbc, 0xba, 0x82, 0x44, 0x4d, 0x2f, 0xcf, 0xeb, 0x61, 0xf9, 0xe9, 0xbc, 0xfc, 0x74, 0x51, 0x7e, + 0xfa, 0x06, 0x71, 0xbc, 0xd2, 0x07, 0xaf, 0x3a, 0xda, 0xc4, 0x49, 0x47, 0x9b, 0x0e, 0x15, 0x70, + 0x21, 0x68, 0x06, 0xb2, 0xf0, 0x0b, 0xb0, 0x78, 0x11, 0x7b, 0x23, 0x07, 0x7b, 0x8d, 0x51, 0xfa, + 0x82, 0xf3, 0x9b, 0x02, 0x6e, 0x55, 0xa9, 0xcd, 0x99, 0xd7, 0x3d, 0xf4, 0x76, 0xe5, 0x69, 0x81, + 0xf7, 0xb8, 0x71, 0x34, 0x9b, 0x2a, 0xa4, 0xcf, 0xf7, 0x6c, 0x89, 0x7b, 0xf6, 0xf3, 0x1b, 0x6d, + 0xc1, 0x76, 0xd8, 0x6e, 0xbb, 0xae, 0x37, 0x48, 0xd3, 0x10, 0x5d, 0x18, 0xfe, 0xb9, 0x4f, 0xd1, + 0x9e, 0xc1, 0x0e, 0x5b, 0x98, 0x06, 0x02, 0xd4, 0x0c, 0x91, 0xcf, 0x2b, 0xf4, 0x47, 0x41, 0x0a, + 0x63, 0x1d, 0x91, 0xa1, 0x98, 0x01, 0xa9, 0x4a, 0x59, 0x44, 0x21, 0x55, 0x29, 0xc3, 0x17, 0x29, + 0x60, 0x54, 0xa9, 0xbd, 0xe1, 0x63, 0x8b, 0xe1, 0xcf, 0xdb, 0xae, 0x6b, 0x5a, 0x9e, 0x8d, 0xb7, + 0x08, 0x75, 0xf8, 0x8c, 0xf8, 0x7f, 0x07, 0x45, 0xbd, 0x07, 0xa6, 0x5a, 0x84, 0xb8, 0x3c, 0xef, + 0x19, 0xee, 0x71, 0x49, 0x3d, 0xe9, 0x68, 0x33, 0xa1, 0xa5, 0x82, 0x00, 0xcd, 0x49, 0xfe, 0x55, + 0x41, 0xf0, 0x39, 0x58, 0x49, 0x18, 0x08, 0x19, 0xd4, 0x1b, 0x20, 0x2c, 0xa8, 0x72, 0x5f, 0x79, + 0x95, 0xd5, 0x3c, 0x00, 0x2d, 0x01, 0x50, 0x29, 0x8b, 0x3e, 0xe8, 0xb9, 0x81, 0x3e, 0xc8, 0x56, + 0xa9, 0xbd, 0xe3, 0x6d, 0x11, 0xe2, 0x7e, 0xbd, 0xeb, 0x30, 0xec, 0x3a, 0x94, 0x61, 0xc4, 0x8f, + 0x49, 0x82, 0xdc, 0xe3, 0x66, 0x6a, 0xa8, 0x9b, 0x8f, 0x41, 0x21, 0x4e, 0xa7, 0xf4, 0xe7, 0x2e, + 0xb8, 0x8a, 0x0f, 0x1c, 0x86, 0x51, 0x4d, 0xb4, 0x0d, 0xcd, 0x2a, 0x85, 0xf4, 0x42, 0xc6, 0xbc, + 0x12, 0x5e, 0x6f, 0x06, 0xdd, 0x43, 0xe1, 0x0f, 0x69, 0xb0, 0x1a, 0x80, 0xb9, 0x61, 0xdd, 0x55, + 0x1d, 0xdb, 0xb7, 0x18, 0x7e, 0xb2, 0x6b, 0xf9, 0x98, 0x6e, 0x13, 0x19, 0xc5, 0x0d, 0xe2, 0x35, + 0xb0, 0xc7, 0x38, 0x0d, 0x45, 0x11, 0x4d, 0xe8, 0x60, 0xdf, 0x30, 0xe9, 0x75, 0x50, 0x10, 0xa0, + 0x1c, 0x30, 0x36, 0x98, 0xa5, 0x81, 0x01, 0x35, 0x46, 0x6a, 0xcd, 0xd0, 0xa2, 0xe1, 0xd3, 0xa6, + 0x20, 0xa6, 0x4d, 0x56, 0x58, 0x70, 0x1a, 0x01, 0x9a, 0x57, 0xa9, 0x70, 0x4b, 0x78, 0xa9, 0xfe, + 0xa8, 0x80, 0x19, 0x46, 0xf6, 0xb0, 0x57, 0x23, 0x6d, 0x56, 0x6b, 0xf2, 0x2a, 0xcf, 0x0c, 0xab, + 0xf2, 0x8a, 0x50, 0x33, 0x17, 0xaa, 0xe9, 0x17, 0x87, 0x89, 0xca, 0xff, 0xfd, 0x40, 0xf8, 0xab, + 0x36, 0xab, 0x72, 0xd1, 0xdf, 0xd3, 0x60, 0x6d, 0xd4, 0x54, 0xc8, 0xbc, 0x3f, 0x05, 0x53, 0x56, + 0x93, 0xb4, 0x3d, 0xb6, 0x24, 0x72, 0xb2, 0xc6, 0xcd, 0xfd, 0xab, 0xa3, 0xdd, 0xbd, 0x80, 0x55, + 0x15, 0x8f, 0x75, 0xb3, 0x22, 0x60, 0xa0, 0x19, 0x01, 0x76, 0xb1, 0x8b, 0x41, 0x0e, 0xdf, 0x1a, + 0xbb, 0x28, 0xb1, 0x8b, 0xea, 0x77, 0x60, 0xd6, 0x75, 0x9e, 0xb7, 0x1d, 0xe4, 0xb0, 0xc3, 0x5a, + 0x23, 0xe8, 0x60, 0x14, 0xce, 0x82, 0xd2, 0xe3, 0x04, 0x5a, 0xca, 0xb8, 0xd1, 0xad, 0x80, 0x33, + 0x80, 0xd0, 0xbc, 0x26, 0xef, 0xc2, 0x29, 0x81, 0xd4, 0x1d, 0x70, 0xf9, 0x5b, 0xe2, 0x78, 0x35, + 0xbe, 0x13, 0x05, 0x23, 0x66, 0x7a, 0x39, 0xa7, 0x87, 0x0b, 0x93, 0x1e, 0x2d, 0x4c, 0xfa, 0x76, + 0xb4, 0x30, 0x95, 0x6e, 0x89, 0xec, 0x5f, 0x0b, 0x55, 0x48, 0x51, 0xf8, 0xf2, 0x8d, 0xa6, 0x98, + 0x97, 0xf8, 0x99, 0x33, 0xc3, 0x3f, 0xc2, 0xa1, 0xbc, 0x8e, 0xd0, 0x36, 0xe9, 0x4d, 0xd8, 0x66, + 0xa4, 0xbf, 0x3b, 0x8b, 0x64, 0x3b, 0xad, 0x80, 0xe9, 0x68, 0xb2, 0xc8, 0x77, 0xae, 0x74, 0xe3, + 0xa4, 0xa3, 0xa9, 0xd1, 0x20, 0x90, 0x44, 0xd8, 0x33, 0x84, 0x50, 0x4f, 0x1f, 0xa6, 0x86, 0xf5, + 0x61, 0x2d, 0x2a, 0x78, 0x84, 0xa9, 0xe3, 0x63, 0xb4, 0x34, 0xbc, 0xaf, 0x6e, 0x0f, 0x2a, 0xf8, + 0x48, 0x1c, 0x9a, 0x57, 0x82, 0x8b, 0xb2, 0x38, 0x9f, 0x51, 0x50, 0x14, 0x41, 0x1d, 0x51, 0x41, + 0xf1, 0x94, 0x82, 0x22, 0x8f, 0xec, 0x4a, 0xc2, 0xc8, 0xca, 0xee, 0x18, 0x39, 0xc2, 0x3d, 0x6d, + 0x95, 0x7a, 0x87, 0x6d, 0x95, 0x1e, 0x77, 0x5b, 0xf5, 0x8c, 0xdd, 0xcc, 0xb0, 0xb1, 0xbb, 0xfc, + 0x2f, 0x00, 0xe9, 0x2a, 0xb5, 0x55, 0x1f, 0xa8, 0x83, 0x56, 0x06, 0xfd, 0xec, 0x3f, 0x2c, 0xfa, + 0xc0, 0xcd, 0x3c, 0x57, 0xbc, 0x30, 0xab, 0xcc, 0xcc, 0x01, 0xb8, 0x3e, 0x70, 0x83, 0xbf, 0x37, + 0x14, 0xaa, 0xcb, 0x9c, 0x7b, 0x90, 0x80, 0x39, 0x4e, 0xb3, 0xdc, 0x94, 0x2f, 0xa2, 0x39, 0x62, + 0xbe, 0x90, 0xe6, 0x33, 0x3b, 0xed, 0x4f, 0x0a, 0xb8, 0x33, 0x7c, 0x63, 0x5f, 0x4d, 0xe0, 0x54, + 0x9f, 0x64, 0x6e, 0x6d, 0x54, 0x49, 0x69, 0xe1, 0x0b, 0x05, 0xcc, 0xc7, 0x6f, 0xd6, 0x4b, 0x31, + 0xf8, 0xb1, 0x12, 0xb9, 0xd5, 0xa4, 0x12, 0xd2, 0x92, 0x5f, 0x15, 0xb0, 0x98, 0x68, 0xc3, 0xdd, + 0x88, 0x51, 0x95, 0x04, 0x24, 0xf7, 0xe5, 0x18, 0x40, 0xa4, 0x0b, 0xdf, 0x83, 0xb9, 0xc1, 0x7b, + 0xe2, 0x62, 0x8c, 0x96, 0x81, 0xdc, 0xb9, 0x4f, 0x93, 0x70, 0x4b, 0xe5, 0x7f, 0x2b, 0xe0, 0xe1, + 0x68, 0x4b, 0xde, 0x66, 0xac, 0xbe, 0x11, 0xd0, 0x72, 0xdb, 0xe3, 0x44, 0xeb, 0xab, 0x8e, 0x44, + 0x4f, 0x6d, 0x5c, 0x75, 0x24, 0x01, 0x89, 0xad, 0x8e, 0x51, 0x9e, 0xa6, 0xd2, 0xd6, 0xab, 0xa3, + 0xbc, 0xf2, 0xfa, 0x28, 0xaf, 0xfc, 0x73, 0x94, 0x57, 0x5e, 0x1e, 0xe7, 0x27, 0x5e, 0x1f, 0xe7, + 0x27, 0xfe, 0x3c, 0xce, 0x4f, 0x3c, 0x7d, 0xd4, 0xf3, 0x0c, 0x08, 0x85, 0xf7, 0x5d, 0xab, 0x4e, + 0xa3, 0x83, 0xb1, 0x5f, 0x7c, 0x68, 0x1c, 0xf4, 0xfd, 0xc6, 0xc4, 0x9f, 0x86, 0xfa, 0x64, 0xb0, + 0xbe, 0x3c, 0xf8, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x8b, 0x8c, 0x15, 0x86, 0x12, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. From b966b05c38ff53941a6b290ddc0c4bf8f50bdd2a Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Mon, 15 May 2023 13:31:06 -0500 Subject: [PATCH 14/15] Update x/superfluid/keeper/migrate.go Co-authored-by: Matt, Park <45252226+mattverse@users.noreply.github.com> --- x/superfluid/keeper/migrate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index 27a734f1e2c..cc8fe4613d3 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -316,7 +316,7 @@ func (k Keeper) validateMigration(ctx sdk.Context, sender sdk.AccAddress, lockId // 3. Ensures that the number of shares to migrate is less than or equal to the number of shares in the lock. // 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 for the remaining time on the lock. +// 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, remainingLockTime time.Duration) (exitCoins sdk.Coins, remainingSharesLock lockuptypes.PeriodLock, err error) { // validateMigration ensures that the preMigrationLock contains coins of length 1. gammSharesInLock := lock.Coins[0] From 17f5bde970b12c958e0a3e6d222831e40c3c66ba Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Mon, 15 May 2023 13:39:58 -0500 Subject: [PATCH 15/15] add check for both bonded and unbonding --- x/superfluid/keeper/migrate.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x/superfluid/keeper/migrate.go b/x/superfluid/keeper/migrate.go index cc8fe4613d3..40d1554877d 100644 --- a/x/superfluid/keeper/migrate.go +++ b/x/superfluid/keeper/migrate.go @@ -251,6 +251,9 @@ func (k Keeper) routeMigration(ctx sdk.Context, sender sdk.AccAddress, lockId ui if strings.Contains(synthLockBeforeMigration.SynthDenom, "superunbonding") { migrationType = SuperfluidUnbonding } + if strings.Contains(synthLockBeforeMigration.SynthDenom, "superbonding") && strings.Contains(synthLockBeforeMigration.SynthDenom, "superunbonding") { + return nil, Unsupported, fmt.Errorf("lock %d contains both superfluid bonded and unbonded tokens", lockId) + } } return synthLocksBeforeMigration, migrationType, nil