diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a835588b2a..2e324fffb76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,10 +42,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### State Breaking + +* [#8169](https://github.com/osmosis-labs/osmosis/pull/8169) Support non-pool assets in superfluid staking. +* [#8274](https://github.com/osmosis-labs/osmosis/pull/8274) SDK v50 and Comet v0.38 upgrade + +### State Compatible + * [#8128](https://github.com/osmosis-labs/osmosis/pull/8128) Cache the result for poolmanager.GetPoolModule * [#8253](https://github.com/osmosis-labs/osmosis/pull/8253) Update gogoproto to v1.4.11 and golang.org/x/exp to * [#8148](https://github.com/osmosis-labs/osmosis/pull/8148) Remove the deserialization time for GetDefaultTakerFee() -* [#8274](https://github.com/osmosis-labs/osmosis/pull/8274) SDK v50 and Comet v0.38 upgrade + ## v25.0.0 diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 531aab9e98f..c08072b457b 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -566,7 +566,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.SuperfluidKeeper = superfluidkeeper.NewKeeper( appKeepers.keys[superfluidtypes.StoreKey], appKeepers.GetSubspace(superfluidtypes.ModuleName), *appKeepers.AccountKeeper, appKeepers.BankKeeper, appKeepers.StakingKeeper, appKeepers.DistrKeeper, appKeepers.EpochsKeeper, appKeepers.LockupKeeper, appKeepers.GAMMKeeper, appKeepers.IncentivesKeeper, - lockupkeeper.NewMsgServerImpl(appKeepers.LockupKeeper), appKeepers.ConcentratedLiquidityKeeper, appKeepers.PoolManagerKeeper, appKeepers.ValidatorSetPreferenceKeeper) + lockupkeeper.NewMsgServerImpl(appKeepers.LockupKeeper), appKeepers.ConcentratedLiquidityKeeper, appKeepers.PoolManagerKeeper, appKeepers.ValidatorSetPreferenceKeeper, appKeepers.TwapKeeper) // The last arguments can contain custom message handlers, and custom query handlers, // if we want to allow any custom callbacks diff --git a/proto/osmosis/superfluid/superfluid.proto b/proto/osmosis/superfluid/superfluid.proto index e3ce43a54d9..1b1e532b437 100644 --- a/proto/osmosis/superfluid/superfluid.proto +++ b/proto/osmosis/superfluid/superfluid.proto @@ -29,6 +29,9 @@ message SuperfluidAsset { // AssetType indicates whether the superfluid asset is a native token or an lp // share SuperfluidAssetType asset_type = 2; + // For non-osmo native assets, we need a pool_id osmo/asset to determine the + // twap of the asset + uint64 price_pool_id = 3; } // SuperfluidIntermediaryAccount takes the role of intermediary between LP token diff --git a/x/superfluid/client/cli/tx.go b/x/superfluid/client/cli/tx.go index 1fcbfd6d1bb..5549ab76cb2 100644 --- a/x/superfluid/client/cli/tx.go +++ b/x/superfluid/client/cli/tx.go @@ -5,6 +5,8 @@ import ( "strconv" "strings" + appparams "github.com/osmosis-labs/osmosis/v25/app/params" + "github.com/spf13/cobra" flag "github.com/spf13/pflag" @@ -211,8 +213,10 @@ func parseSetSuperfluidAssetsArgsToContent(cmd *cobra.Command) (govtypesv1beta1. assetType = types.SuperfluidAssetTypeLPShare } else if strings.HasPrefix(asset, cltypes.ConcentratedLiquidityTokenPrefix) { assetType = types.SuperfluidAssetTypeConcentratedShare + } else if asset == appparams.BaseCoinUnit { + return nil, fmt.Errorf("invalid asset type: %s", asset) } else { - return nil, fmt.Errorf("Invalid asset prefix: %s", asset) + assetType = types.SuperfluidAssetTypeNative } superfluidAssets = append(superfluidAssets, types.SuperfluidAsset{ diff --git a/x/superfluid/keeper/epoch.go b/x/superfluid/keeper/epoch.go index 6804e2565cd..0c4fa210fe8 100644 --- a/x/superfluid/keeper/epoch.go +++ b/x/superfluid/keeper/epoch.go @@ -4,10 +4,13 @@ import ( "errors" "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/osmosis-labs/osmosis/osmomath" + "github.com/osmosis-labs/osmosis/osmoutils" cl "github.com/osmosis-labs/osmosis/v25/x/concentrated-liquidity" "github.com/osmosis-labs/osmosis/v25/x/concentrated-liquidity/model" @@ -149,10 +152,23 @@ func (k Keeper) UpdateOsmoEquivalentMultipliers(ctx sdk.Context, asset types.Sup return k.updateConcentratedOsmoEquivalentMultiplier(cacheCtx, asset, newEpochNumber) }) } else if asset.AssetType == types.SuperfluidAssetTypeNative { - // TODO: Consider deleting superfluid asset type native - k.Logger(ctx).Error("unsupported superfluid asset type") - return errors.New("SuperfluidAssetTypeNative is unsupported") + bondDenom, err := k.sk.BondDenom(ctx) + if err != nil { + return err + } + if asset.Denom == bondDenom { + // The bond denom should be locked via x/lockup and not superfluid + return errors.New("osmo should not be a superfluid asset. It can be staked natively") + } + // get the twap price of the native asset in osmo + startTime := k.ek.GetEpochInfo(ctx, k.GetEpochIdentifier(ctx)).StartTime + price, err := k.twapk.GetArithmeticTwapToNow(ctx, asset.PricePoolId, bondDenom, asset.Denom, startTime) + if err != nil { + return sdkerrors.Wrap(err, "failed to get twap price") + } + k.SetOsmoEquivalentMultiplier(ctx, newEpochNumber, asset.Denom, osmomath.NewDec(1).Quo(price)) } + return nil } @@ -170,10 +186,6 @@ func (k Keeper) updateConcentratedOsmoEquivalentMultiplier(ctx sdk.Context, asse // get underlying assets from all liquidity in a full range position // note: this is not the same as the total liquidity in the pool, as this includes positions not in the full range - bondDenom, err := k.sk.BondDenom(ctx) - if err != nil { - return err - } fullRangeLiquidity, err := k.clk.GetFullRangeLiquidityInPool(ctx, poolId) if err != nil { k.Logger(ctx).Error(err.Error()) @@ -195,6 +207,10 @@ func (k Keeper) updateConcentratedOsmoEquivalentMultiplier(ctx sdk.Context, asse assets := sdk.NewCoins(asset0, asset1) // get OSMO amount from underlying assets + bondDenom, err := k.sk.BondDenom(ctx) + if err != nil { + return err + } osmoPoolAsset := assets.AmountOf(bondDenom) if osmoPoolAsset.IsZero() { // Pool has unexpectedly removed OSMO from its assets. diff --git a/x/superfluid/keeper/epoch_test.go b/x/superfluid/keeper/epoch_test.go index d733e00817d..da44ef34957 100644 --- a/x/superfluid/keeper/epoch_test.go +++ b/x/superfluid/keeper/epoch_test.go @@ -63,6 +63,16 @@ func (s *KeeperTestSuite) TestUpdateOsmoEquivalentMultipliers() { // Note: this does not error since CL errors are surrounded in `ApplyFuncIfNoError` expectedZeroMultipler: true, }, + { + name: "update native token Osmo equivalent successfully", + asset: types.SuperfluidAsset{Denom: "foo", AssetType: types.SuperfluidAssetTypeNative, PricePoolId: 1}, + expectedMultiplier: osmomath.MustNewDecFromStr("2.0"), + }, + { + name: "update native token Osmo equivalent successfully - no pool", + asset: types.SuperfluidAsset{Denom: "foo", AssetType: types.SuperfluidAssetTypeNative}, + expectedError: errors.New("failed to get twap price"), + }, } for _, tc := range testCases { @@ -78,7 +88,7 @@ func (s *KeeperTestSuite) TestUpdateOsmoEquivalentMultipliers() { if tc.removeStakingAsset { stakeDenom = "bar" } - poolCoins := sdk.NewCoins(sdk.NewCoin(stakeDenom, osmomath.NewInt(1000000000000000000)), sdk.NewCoin("foo", osmomath.NewInt(1000000000000000000))) + poolCoins := sdk.NewCoins(sdk.NewCoin(stakeDenom, osmomath.NewInt(1000000000000000000)), sdk.NewCoin("foo", osmomath.NewInt(500000000000000000))) // Ensure that the multiplier is zero before the test multiplier := superfluidKeeper.GetOsmoEquivalentMultiplier(ctx, tc.asset.Denom) @@ -86,7 +96,7 @@ func (s *KeeperTestSuite) TestUpdateOsmoEquivalentMultipliers() { // Create the respective pool if the test case requires it if !tc.poolDoesNotExist { - if tc.asset.AssetType == types.SuperfluidAssetTypeLPShare { + if tc.asset.AssetType == types.SuperfluidAssetTypeLPShare || tc.asset.AssetType == types.SuperfluidAssetTypeNative { s.PrepareBalancerPoolWithCoins(poolCoins...) } else if tc.asset.AssetType == types.SuperfluidAssetTypeConcentratedShare { s.PrepareConcentratedPoolWithCoinsAndLockedFullRangePosition(stakeDenom, "foo") @@ -115,6 +125,9 @@ func (s *KeeperTestSuite) TestUpdateOsmoEquivalentMultipliers() { if !tc.expectedZeroMultipler { s.Require().NotEqual(multiplier, osmomath.ZeroDec()) + if !tc.expectedMultiplier.IsNil() { + s.Require().Equal(tc.expectedMultiplier, multiplier) + } } else { // Zero on success is expected on CL errors since those are surrounded with `ApplyFuncIfNoError` s.Require().Equal(multiplier, osmomath.ZeroDec()) diff --git a/x/superfluid/keeper/gov/gov.go b/x/superfluid/keeper/gov/gov.go index 8356a400d75..f7056ad9c8f 100644 --- a/x/superfluid/keeper/gov/gov.go +++ b/x/superfluid/keeper/gov/gov.go @@ -22,6 +22,9 @@ func HandleSetSuperfluidAssetsProposal(ctx sdk.Context, k keeper.Keeper, ek type return fmt.Errorf("concentrated LP share denom (%s) must have asset type %s", asset.Denom, types.SuperfluidAssetTypeConcentratedShare) } } + if asset.AssetType == types.SuperfluidAssetTypeNative && asset.PricePoolId == 0 { + return fmt.Errorf("native asset (%s) must have a price pool id", asset.Denom) + } if err := k.AddNewSuperfluidAsset(ctx, asset); err != nil { return err } diff --git a/x/superfluid/keeper/grpc_query.go b/x/superfluid/keeper/grpc_query.go index dde61fff6a1..701986a46f2 100644 --- a/x/superfluid/keeper/grpc_query.go +++ b/x/superfluid/keeper/grpc_query.go @@ -261,7 +261,7 @@ func (q Querier) SuperfluidDelegationsByDelegator(goCtx context.Context, req *ty // Find how many osmo tokens this delegation is worth at superfluids current risk adjustment // and twap of the denom. - equivalentAmount, err := q.Keeper.GetSuperfluidOSMOTokens(ctx, baseDenom, lockedCoins.Amount) + equivalentAmount, err := q.Keeper.GetSuperfluidOSMOTokensExcludeNative(ctx, baseDenom, lockedCoins.Amount) if err != nil { return nil, err } @@ -430,7 +430,7 @@ func (q Querier) SuperfluidDelegationsByValidatorDenom(goCtx context.Context, re lockedCoins := sdk.NewCoin(req.Denom, lock.GetCoins().AmountOf(req.Denom)) baseDenom := lock.Coins.GetDenomByIndex(0) - equivalentAmount, err := q.Keeper.GetSuperfluidOSMOTokens(ctx, baseDenom, lockedCoins.Amount) + equivalentAmount, err := q.Keeper.GetSuperfluidOSMOTokensExcludeNative(ctx, baseDenom, lockedCoins.Amount) if err != nil { return nil, err } @@ -525,7 +525,7 @@ func (q Querier) TotalDelegationByValidatorForDenom(goCtx context.Context, req * amount = amount.Add(record.DelegationAmount.Amount) } - equivalentAmountOSMO, err := q.Keeper.GetSuperfluidOSMOTokens(ctx, req.Denom, amount) + equivalentAmountOSMO, err := q.Keeper.GetSuperfluidOSMOTokensExcludeNative(ctx, req.Denom, amount) if err != nil { return nil, err } @@ -704,7 +704,7 @@ func (q Querier) filterConcentratedPositionLocks(ctx sdk.Context, positions []mo baseDenom := lock.Coins.GetDenomByIndex(0) lockedCoins := sdk.NewCoin(baseDenom, lock.GetCoins().AmountOf(baseDenom)) - equivalentAmount, err := q.Keeper.GetSuperfluidOSMOTokens(ctx, baseDenom, lockedCoins.Amount) + equivalentAmount, err := q.Keeper.GetSuperfluidOSMOTokensExcludeNative(ctx, baseDenom, lockedCoins.Amount) if err != nil { return nil, err } diff --git a/x/superfluid/keeper/grpc_query_test.go b/x/superfluid/keeper/grpc_query_test.go index 26d24de2626..a40a00c0310 100644 --- a/x/superfluid/keeper/grpc_query_test.go +++ b/x/superfluid/keeper/grpc_query_test.go @@ -70,7 +70,7 @@ func (s *KeeperTestSuite) TestTotalDelegationByValidatorForAsset() { for _, result := range res.Assets { // check osmo equivalent is correct actual_response_osmo := result.OsmoEquivalent - needed_response_osmo, err := s.App.SuperfluidKeeper.GetSuperfluidOSMOTokens(ctx, denom, osmomath.NewInt(delegation_amount)) + needed_response_osmo, err := s.App.SuperfluidKeeper.GetSuperfluidOSMOTokensExcludeNative(ctx, denom, osmomath.NewInt(delegation_amount)) s.Require().NoError(err) s.Require().Equal(actual_response_osmo, needed_response_osmo) diff --git a/x/superfluid/keeper/integration_test.go b/x/superfluid/keeper/integration_test.go new file mode 100644 index 00000000000..febe73e4530 --- /dev/null +++ b/x/superfluid/keeper/integration_test.go @@ -0,0 +1,551 @@ +package keeper_test + +import ( + gocontext "context" + "fmt" + "strconv" + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/stretchr/testify/suite" + + "github.com/osmosis-labs/osmosis/osmomath" + appparams "github.com/osmosis-labs/osmosis/v25/app/params" + balancertypes "github.com/osmosis-labs/osmosis/v25/x/gamm/pool-models/balancer" + minttypes "github.com/osmosis-labs/osmosis/v25/x/mint/types" + "github.com/osmosis-labs/osmosis/v25/x/superfluid/keeper" + "github.com/osmosis-labs/osmosis/v25/x/superfluid/types" +) + +type TestSuite struct { + KeeperTestSuite + + proposal govv1.Proposal +} + +func TestTestSuite(t *testing.T) { + suite.Run(t, new(TestSuite)) +} + +func (s *TestSuite) SetupTest() { + s.KeeperTestSuite.SetupTest() + + // set the bond denom to be osmo (because it's hardcoded in protorev) + stakingParams, err := s.App.StakingKeeper.GetParams(s.Ctx) + s.Require().NoError(err) + stakingParams.BondDenom = appparams.BaseCoinUnit + err = s.App.StakingKeeper.SetParams(s.Ctx, stakingParams) + s.Require().NoError(err) + + // set incentives min value in osmo + incentivesParams := s.App.IncentivesKeeper.GetParams(s.Ctx) + incentivesParams.MinValueForDistribution.Denom = appparams.BaseCoinUnit + s.App.IncentivesKeeper.SetParams(s.Ctx, incentivesParams) + + // make pool creation fees be paid in the bond denom. Also make them low. + poolmanagerParams := s.App.PoolManagerKeeper.GetParams(s.Ctx) + bondDenom, err := s.App.StakingKeeper.BondDenom(s.Ctx) + s.Require().NoError(err) + poolmanagerParams.PoolCreationFee = sdk.NewCoins(sdk.NewInt64Coin(bondDenom, 1)) + s.App.PoolManagerKeeper.SetParams(s.Ctx, poolmanagerParams) + + // create a proposal + // Populate the gov keeper in advance with an active proposal + testProposal := &govtypes.TextProposal{ + Title: "IBC Gov Proposal", + Description: "tokens for all!", + } + + proposalMsg, err := govv1.NewLegacyContent(testProposal, "") + s.Require().NoError(err) + + proposal, err := govv1.NewProposal( + []sdk.Msg{proposalMsg}, + govtypes.DefaultStartingProposalID, + s.Ctx.BlockTime(), + s.Ctx.BlockTime(), + "test proposal", + "title", + "Description", + sdk.AccAddress("proposer"), + false, + ) + s.Require().NoError(err) + s.App.GovKeeper.SetProposal(s.Ctx, proposal) + s.App.GovKeeper.ActivateVotingPeriod(s.Ctx, proposal) + s.proposal = proposal +} + +func createPoolMsgGen(sender sdk.AccAddress, assets sdk.Coins) *balancertypes.MsgCreateBalancerPool { + if len(assets) != 2 { + panic("baseCreatePoolMsg requires 2 assets") + } + poolAssets := []balancertypes.PoolAsset{ + { + Weight: osmomath.NewInt(1), + Token: assets[0], + }, + { + Weight: osmomath.NewInt(1), + Token: assets[1], + }, + } + + poolParams := &balancertypes.PoolParams{ + SwapFee: osmomath.NewDecWithPrec(1, 2), + ExitFee: osmomath.ZeroDec(), + } + + msg := &balancertypes.MsgCreateBalancerPool{ + Sender: sender.String(), + PoolAssets: poolAssets, + PoolParams: poolParams, + FuturePoolGovernor: "", + } + + return msg +} + +func (s *TestSuite) mintToAccount(amount osmomath.Int, denom string, acc sdk.AccAddress) { + err := s.App.BankKeeper.MintCoins(s.Ctx, minttypes.ModuleName, sdk.NewCoins(sdk.NewCoin(denom, amount))) + s.Require().NoError(err) + // send the coins to user1 + err = s.App.BankKeeper.SendCoinsFromModuleToAccount(s.Ctx, minttypes.ModuleName, acc, sdk.NewCoins(sdk.NewCoin(denom, amount))) + s.Require().NoError(err) +} + +func (s *TestSuite) TestGammSuperfluid() { + //////// + // Setup + //////// + + s.SetupTest() + + // denoms + btcDenom := "factory/osmo1pfyxruwvtwk00y8z06dh2lqjdj82ldvy74wzm3/allBTC" // Asset to superfluid stake + bondDenom, err := s.App.StakingKeeper.BondDenom(s.Ctx) + s.Require().NoError(err) + + // accounts + // pool creator + lpKey := ed25519.GenPrivKey().PubKey() + lpAddr := sdk.AccAddress(lpKey.Address()) + + osmoPoolAmount := osmomath.NewInt(1_000_000_000_000) + btcPoolAmount := osmomath.NewInt(10_000_000_000) + // default bond denom + + // mint necessary tokens + s.mintToAccount(btcPoolAmount, btcDenom, lpAddr) + s.mintToAccount(osmoPoolAmount.Mul(osmomath.NewInt(2)), bondDenom, lpAddr) + + nextPoolId := s.App.PoolManagerKeeper.GetNextPoolId(s.Ctx) // the pool id we'll create + + // create an bondDenom/btcDenom pool + createPoolMsg := createPoolMsgGen( + lpAddr, + sdk.NewCoins(sdk.NewCoin(btcDenom, btcPoolAmount), sdk.NewCoin(bondDenom, osmoPoolAmount)), + ) + + _, err = s.RunMsg(createPoolMsg) + s.Require().NoError(err) + gammToken := fmt.Sprintf("gamm/pool/%d", nextPoolId) + + totalGammTokens := s.App.BankKeeper.GetBalance(s.Ctx, lpAddr, gammToken) + + // Add btcDenom as an allowed superfluid asset + err = s.App.SuperfluidKeeper.AddNewSuperfluidAsset(s.Ctx, types.SuperfluidAsset{Denom: gammToken, AssetType: types.SuperfluidAssetTypeLPShare}) + s.Require().NoError(err) + + // Mint assets to the lockup module. This will ensure there are assets to distribute. + err = s.App.BankKeeper.MintCoins(s.Ctx, minttypes.ModuleName, sdk.NewCoins(sdk.NewCoin(bondDenom, osmomath.NewInt(1_000_000_000)))) + s.Require().NoError(err) + err = s.App.BankKeeper.SendCoinsFromModuleToModule(s.Ctx, minttypes.ModuleName, authtypes.FeeCollectorName, sdk.NewCoins(sdk.NewCoin(bondDenom, osmomath.NewInt(1_000_000_000)))) + s.Require().NoError(err) + + // Keep track of the original balance of the bond denom to make sure rewards are distributed later on + originalBondDenomBalance := s.App.BankKeeper.GetBalance(s.Ctx, lpAddr, bondDenom).Amount + + //////// + // TEST: Delegate gamm tokens + //////// + + // No delegations + delegations := s.App.LockupKeeper.GetAllSyntheticLockupsByAddr(s.Ctx, lpAddr) + s.Require().Equal(0, len(delegations)) + + // superfluid stake gamm token + validators, err := s.App.StakingKeeper.GetAllValidators(s.Ctx) + s.Require().NoError(err) + validator := validators[0] + gammDelegationAmount := osmomath.NewInt(1000000000000000000) + delegateMsg := &types.MsgLockAndSuperfluidDelegate{ + Sender: lpAddr.String(), + Coins: sdk.NewCoins(sdk.NewCoin(gammToken, gammDelegationAmount)), + ValAddr: validator.GetOperator(), + } + _, err = s.RunMsg(delegateMsg) + s.Require().NoError(err) + + // Check delegations + delegations = s.App.LockupKeeper.GetAllSyntheticLockupsByAddr(s.Ctx, lpAddr) + s.Require().Equal(1, len(delegations)) + synthLock := delegations[0] + + // Get underlying lock + underlyingLock, err := s.App.LockupKeeper.GetLockByID(s.Ctx, synthLock.UnderlyingLockId) + s.Require().NoError(err) + s.Require().Equal(lpAddr.String(), underlyingLock.Owner) + s.Require().Equal(gammToken, underlyingLock.Coins[0].Denom) + + remainingGammTokens := s.App.BankKeeper.GetBalance(s.Ctx, lpAddr, gammToken) + s.Require().Equal(totalGammTokens.Amount.Sub(gammDelegationAmount), remainingGammTokens.Amount) + + //////// + // TEST: Reward distribution + //////// + + // Check that the user has not received any rewards yet + bondDenomBalance := s.App.BankKeeper.GetBalance(s.Ctx, lpAddr, bondDenom) + s.Require().Equal(originalBondDenomBalance, bondDenomBalance.Amount) + + // There are no rewards assigned to the validator yet + validatorRewards := new(distrtypes.QueryValidatorOutstandingRewardsResponse) + err = s.QueryHelper.Invoke(gocontext.Background(), + "/cosmos.distribution.v1beta1.Query/ValidatorOutstandingRewards", + &distrtypes.QueryValidatorOutstandingRewardsRequest{ + ValidatorAddress: validator.GetOperator(), + }, + validatorRewards) + s.Require().Equal(0, len(validatorRewards.Rewards.Rewards)) + s.Require().NoError(err) + + // Move to block 50 because rewards are only distributed every 50 blocks. Rewards will be available after unstaking + s.AdvanceToBlockNAndRunEpoch(50) + + // After a block that is not a multiple of 50, the rewards will be assigned to the validator + validatorRewards = new(distrtypes.QueryValidatorOutstandingRewardsResponse) + err = s.QueryHelper.Invoke(gocontext.Background(), + "/cosmos.distribution.v1beta1.Query/ValidatorOutstandingRewards", + &distrtypes.QueryValidatorOutstandingRewardsRequest{ + ValidatorAddress: validator.GetOperator(), + }, + validatorRewards) + s.Require().NoError(err) + s.Require().Equal(1, len(validatorRewards.Rewards.Rewards)) + // After unstaking we will check that those rewards were properly distributed to the delegator + + //////// + // TEST: Voting. User can vote + //////// + + // check user can vote + voteMsg := &govtypes.MsgVote{ + ProposalId: 1, + Voter: lpAddr.String(), + Option: govtypes.OptionYes, + } + _, err = s.RunMsg(voteMsg) + s.Require().NoError(err) + + // Move time beyond voting end time + s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(96 * time.Hour)) + s.EndBlock() + s.BeginNewBlock(true) + + proposal, err := s.App.GovKeeper.Proposals.Get(s.Ctx, 1) + s.Require().NoError(err) + s.Require().Equal(govv1.StatusFailed, proposal.Status) + s.Require().Equal("5000000000", proposal.FinalTallyResult.YesCount) + + //////// + // TEST: Unstake + //////// + + // Check that the user can unstake and the delegation is removed + undelegateMsg := &types.MsgSuperfluidUndelegateAndUnbondLock{ + Sender: lpAddr.String(), + LockId: underlyingLock.ID, + Coin: sdk.NewCoin(gammToken, gammDelegationAmount), + } + _, err = s.RunMsg(undelegateMsg) + s.Require().NoError(err) + + // Check delegations + querier := keeper.NewQuerier(*s.App.SuperfluidKeeper) + queryDelegations := types.SuperfluidDelegationsByDelegatorRequest{DelegatorAddress: lpAddr.String()} + res, err := querier.SuperfluidDelegationsByDelegator(s.Ctx, &queryDelegations) + s.Require().NoError(err) + s.Require().Len(res.SuperfluidDelegationRecords, 0) + + // Check undelegations + queryUndelegations := types.SuperfluidUndelegationsByDelegatorRequest{DelegatorAddress: lpAddr.String()} + undelegationResponse, err := querier.SuperfluidUndelegationsByDelegator(s.Ctx, &queryUndelegations) + s.Require().NoError(err) + s.Require().Len(undelegationResponse.SuperfluidDelegationRecords, 1) + + // check pool token balance before undelegation time passes. Should be the same as before + balance := s.App.BankKeeper.GetBalance(s.Ctx, lpAddr, gammToken) + s.Require().Equal(remainingGammTokens.Amount, balance.Amount) + + s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(undelegationResponse.SyntheticLocks[0].Duration + time.Second)) + // move forward to block 60 because we only check matured locks every 30 blocks + s.AdvanceToBlockNAndRunEpoch(60) + + // No more undelegations + undelegationResponse, err = querier.SuperfluidUndelegationsByDelegator(s.Ctx, &queryUndelegations) + s.Require().NoError(err) + s.Require().Len(undelegationResponse.SuperfluidDelegationRecords, 0) + + // check pool token balance after undelegation time passes. Should be back to original + balance = s.App.BankKeeper.GetBalance(s.Ctx, lpAddr, gammToken) + s.Require().Equal(totalGammTokens.Amount, balance.Amount) + + //////// + // TEST: Check delegation rewards were distributed + //////// + bondDenomBalance = s.App.BankKeeper.GetBalance(s.Ctx, lpAddr, bondDenom) + s.Require().True(bondDenomBalance.Amount.GT(originalBondDenomBalance)) +} + +func (s *TestSuite) TestNativeSuperfluid() { + // + // TEST: Setup + // + + s.SetupTest() + + // denoms + btcDenom := "factory/osmo1pfyxruwvtwk00y8z06dh2lqjdj82ldvy74wzm3/allBTC" // Asset to superfluid stake + bondDenom, err := s.App.StakingKeeper.BondDenom(s.Ctx) + s.Require().NoError(err) + + // accounts + // pool creator + lpKey := ed25519.GenPrivKey().PubKey() + poolAddr := sdk.AccAddress(lpKey.Address()) + userKey := ed25519.GenPrivKey().PubKey() + userAddr := sdk.AccAddress(userKey.Address()) + + osmoPoolAmount := osmomath.NewInt(1_000_000_000_000) + btcPoolAmount := osmomath.NewInt(10_000_000_000) + // default bond denom + + // mint necessary tokens + s.mintToAccount(btcPoolAmount, btcDenom, poolAddr) + s.mintToAccount(osmoPoolAmount.Mul(osmomath.NewInt(2)), bondDenom, poolAddr) + s.mintToAccount(osmomath.NewInt(100_000_000), bondDenom, userAddr) + totalBTCAmount := osmomath.NewInt(1_000_000) + s.mintToAccount(totalBTCAmount, btcDenom, userAddr) + + nextPoolId := s.App.PoolManagerKeeper.GetNextPoolId(s.Ctx) // the pool id we'll create + + // create an bondDenom/btcDenom pool. This is only used so that the native asset can have a price. + createPoolMsg := createPoolMsgGen( + poolAddr, + sdk.NewCoins(sdk.NewCoin(btcDenom, btcPoolAmount), sdk.NewCoin(bondDenom, osmoPoolAmount)), + ) + + _, err = s.RunMsg(createPoolMsg) + s.Require().NoError(err) + + // Creating a native type without a pool should fail + err = s.App.SuperfluidKeeper.AddNewSuperfluidAsset(s.Ctx, types.SuperfluidAsset{Denom: btcDenom, AssetType: types.SuperfluidAssetTypeNative}) + s.Require().Error(err) + s.Require().Contains(err.Error(), "failed to get twap price") + + // Creating a native type with a non-existing pool should fail + err = s.App.SuperfluidKeeper.AddNewSuperfluidAsset(s.Ctx, types.SuperfluidAsset{Denom: btcDenom, AssetType: types.SuperfluidAssetTypeNative, PricePoolId: nextPoolId + 10}) + s.Require().Error(err) + s.Require().Contains(err.Error(), "failed to get twap price") + + // Add btcDenom as an allowed superfluid asset + err = s.App.SuperfluidKeeper.AddNewSuperfluidAsset(s.Ctx, types.SuperfluidAsset{Denom: btcDenom, AssetType: types.SuperfluidAssetTypeNative, PricePoolId: nextPoolId}) + s.Require().NoError(err) + + // Mint assets to the lockup module. This will ensure there are assets to distribute. + err = s.App.BankKeeper.MintCoins(s.Ctx, minttypes.ModuleName, sdk.NewCoins(sdk.NewCoin(bondDenom, osmomath.NewInt(1_000_000_000)))) + s.Require().NoError(err) + err = s.App.BankKeeper.SendCoinsFromModuleToModule(s.Ctx, minttypes.ModuleName, authtypes.FeeCollectorName, sdk.NewCoins(sdk.NewCoin(bondDenom, osmomath.NewInt(1_000_000_000)))) + s.Require().NoError(err) + + // Keep track of the original balance of the bond denom to make sure rewards are distributed later on + originalBondDenomBalance := s.App.BankKeeper.GetBalance(s.Ctx, userAddr, bondDenom).Amount + + // + // TEST: Delegation + // + + // No delegations + delegations := s.App.LockupKeeper.GetAllSyntheticLockupsByAddr(s.Ctx, userAddr) + s.Require().Equal(0, len(delegations)) + + balance := s.App.BankKeeper.GetBalance(s.Ctx, userAddr, btcDenom) + s.Require().Equal(totalBTCAmount, balance.Amount) + + // superfluid stake btcDenom + btcStakeAmount := osmomath.NewInt(500_000) + validators, err := s.App.StakingKeeper.GetAllValidators(s.Ctx) + s.Require().NoError(err) + validator := validators[0] + delegateMsg := &types.MsgLockAndSuperfluidDelegate{ + Sender: userAddr.String(), + Coins: sdk.NewCoins(sdk.NewCoin(btcDenom, btcStakeAmount)), + ValAddr: validator.GetOperator(), + } + result, err := s.RunMsg(delegateMsg) + s.Require().NoError(err) + // Extract the lock id to use later when undelegating + attrs := s.ExtractAttributes(s.FindEvent(result.Events, "superfluid_delegate")) + lockId, err := strconv.ParseUint(attrs["lock_id"], 10, 64) + s.Require().NoError(err) + + // Check delegations + delegations = s.App.LockupKeeper.GetAllSyntheticLockupsByAddr(s.Ctx, userAddr) + s.Require().Equal(1, len(delegations)) + synthLock := delegations[0] + s.Require().Equal(synthLock.UnderlyingLockId, lockId) + + // check balance + balance = s.App.BankKeeper.GetBalance(s.Ctx, userAddr, btcDenom) + s.Require().Equal(btcStakeAmount, balance.Amount) + + underlyingLock, err := s.App.LockupKeeper.GetLockByID(s.Ctx, synthLock.UnderlyingLockId) + s.Require().NoError(err) + s.Require().Equal(userAddr.String(), underlyingLock.Owner) + s.Require().Equal(btcDenom, underlyingLock.Coins[0].Denom) + + queryDelegations := types.SuperfluidDelegationsByDelegatorRequest{DelegatorAddress: userAddr.String()} + querier := keeper.NewQuerier(*s.App.SuperfluidKeeper) + res, err := querier.SuperfluidDelegationsByDelegator(s.Ctx, &queryDelegations) + s.Require().NoError(err) + s.Require().Len(res.SuperfluidDelegationRecords, 1) + s.Require().Equal(userAddr.String(), res.SuperfluidDelegationRecords[0].DelegatorAddress) + s.Require().Equal(validator.GetOperator(), res.SuperfluidDelegationRecords[0].ValidatorAddress) + s.Require().Equal(btcDenom, res.SuperfluidDelegationRecords[0].DelegationAmount.Denom) + s.Require().Equal(btcStakeAmount, res.SuperfluidDelegationRecords[0].DelegationAmount.Amount) + s.Require().Equal("uosmo", res.SuperfluidDelegationRecords[0].EquivalentStakedAmount.Denom) + s.Require().Equal(osmomath.NewInt(0), res.SuperfluidDelegationRecords[0].EquivalentStakedAmount.Amount) + s.Require().Equal("uosmo", res.TotalEquivalentStakedAmount.Denom) + s.Require().Equal(osmomath.NewInt(0), res.TotalEquivalentStakedAmount.Amount) + + // + // TEST: Reward distribution + // + + // Check that the user has not received any rewards yet + bondDenomBalance := s.App.BankKeeper.GetBalance(s.Ctx, userAddr, bondDenom) + s.Require().Equal(originalBondDenomBalance, bondDenomBalance.Amount) + + // There are no rewards assigned to the validator yet + validatorRewards := new(distrtypes.QueryValidatorOutstandingRewardsResponse) + err = s.QueryHelper.Invoke(gocontext.Background(), + "/cosmos.distribution.v1beta1.Query/ValidatorOutstandingRewards", + &distrtypes.QueryValidatorOutstandingRewardsRequest{ + ValidatorAddress: validator.GetOperator(), + }, + validatorRewards) + s.Require().Equal(0, len(validatorRewards.Rewards.Rewards)) + s.Require().NoError(err) + + // Move to block 50 because rewards are only distributed every 50 blocks. Rewards will be available after unstaking + s.AdvanceToBlockNAndRunEpoch(50) + + // After a block that is not a multiple of 50, the rewards will be assigned to the validator + validatorRewards = new(distrtypes.QueryValidatorOutstandingRewardsResponse) + err = s.QueryHelper.Invoke(gocontext.Background(), + "/cosmos.distribution.v1beta1.Query/ValidatorOutstandingRewards", + &distrtypes.QueryValidatorOutstandingRewardsRequest{ + ValidatorAddress: validator.GetOperator(), + }, + validatorRewards) + s.Require().NoError(err) + s.Require().Equal(1, len(validatorRewards.Rewards.Rewards)) + // After unstaking we will check that those rewards were properly distributed to the delegator + + // + // TEST: Voting. Users should not be allowed to vote when superfluid staking native assets + // + + // Send vote message + voteMsg := &govtypes.MsgVote{ + ProposalId: 1, + Voter: userAddr.String(), + Option: 1, + } + _, err = s.RunMsg(voteMsg) + s.Require().NoError(err) + + // Move time beyond voting end time + s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(96 * time.Hour)) + s.EndBlock() + s.BeginNewBlock(true) + + proposal, err := s.App.GovKeeper.Proposals.Get(s.Ctx, 1) + s.Require().NoError(err) + s.Require().Equal(govv1.StatusRejected, proposal.Status) + s.Require().Equal("0", proposal.FinalTallyResult.YesCount) + + //////// + // TEST: Unstake + //////// + + // Check that the user can unstake and the delegation is removed + undelegateMsg := &types.MsgSuperfluidUndelegateAndUnbondLock{ + Sender: userAddr.String(), + LockId: underlyingLock.ID, + Coin: sdk.NewCoin(btcDenom, btcStakeAmount), + } + _, err = s.RunMsg(undelegateMsg) + s.Require().NoError(err) + + // Check delegations + res, err = querier.SuperfluidDelegationsByDelegator(s.Ctx, &queryDelegations) + s.Require().NoError(err) + s.Require().Len(res.SuperfluidDelegationRecords, 0) + + // Check undelegations + queryUndelegations := types.SuperfluidUndelegationsByDelegatorRequest{DelegatorAddress: userAddr.String()} + undelegationResponse, err := querier.SuperfluidUndelegationsByDelegator(s.Ctx, &queryUndelegations) + s.Require().NoError(err) + s.Require().Len(undelegationResponse.SuperfluidDelegationRecords, 1) + + // check balance before undelegation time passes + balance = s.App.BankKeeper.GetBalance(s.Ctx, userAddr, btcDenom) + s.Require().Equal(btcStakeAmount, balance.Amount) + + s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(undelegationResponse.SyntheticLocks[0].Duration + time.Second)) + // move forward to block 60 because we only check matured locks every 30 blocks + s.AdvanceToBlockNAndRunEpoch(60) + + // No more undelegations + undelegationResponse, err = querier.SuperfluidUndelegationsByDelegator(s.Ctx, &queryUndelegations) + s.Require().NoError(err) + s.Require().Len(undelegationResponse.SuperfluidDelegationRecords, 0) + + // check the btc balance after undelegation time passes. Funds should be restored + balance = s.App.BankKeeper.GetBalance(s.Ctx, userAddr, btcDenom) + s.Require().Equal(totalBTCAmount, balance.Amount) + + //////// + // TEST: Check delegation rewards were distributed + //////// + bondDenomBalance = s.App.BankKeeper.GetBalance(s.Ctx, userAddr, bondDenom) + s.Require().True(bondDenomBalance.Amount.GT(originalBondDenomBalance)) +} + +func (s *TestSuite) AdvanceToBlockNAndRunEpoch(n int64) { + for i := s.Ctx.BlockHeight(); i < n; i++ { + s.EndBlock() + s.BeginNewBlock(i%n == 0) + } + s.EndBlock() + fmt.Printf("moved to block %d and ran epoch\n", s.Ctx.BlockHeight()) + s.BeginNewBlock(false) +} diff --git a/x/superfluid/keeper/keeper.go b/x/superfluid/keeper/keeper.go index cfdf24ff3bd..395f25e1b2c 100644 --- a/x/superfluid/keeper/keeper.go +++ b/x/superfluid/keeper/keeper.go @@ -19,17 +19,18 @@ type Keeper struct { storeKey storetypes.StoreKey paramSpace paramtypes.Subspace - ak authkeeper.AccountKeeper - bk types.BankKeeper - sk types.StakingKeeper - ck types.CommunityPoolKeeper - ek types.EpochKeeper - lk types.LockupKeeper - gk types.GammKeeper - ik types.IncentivesKeeper - clk types.ConcentratedKeeper - pmk types.PoolManagerKeeper - vspk types.ValSetPreferenceKeeper + ak authkeeper.AccountKeeper + bk types.BankKeeper + sk types.StakingKeeper + ck types.CommunityPoolKeeper + ek types.EpochKeeper + lk types.LockupKeeper + gk types.GammKeeper + ik types.IncentivesKeeper + clk types.ConcentratedKeeper + pmk types.PoolManagerKeeper + vspk types.ValSetPreferenceKeeper + twapk types.TwapKeeper lms types.LockupMsgServer } @@ -37,7 +38,7 @@ type Keeper struct { var _ govtypes.StakingKeeper = (*Keeper)(nil) // NewKeeper returns an instance of Keeper. -func NewKeeper(storeKey storetypes.StoreKey, paramSpace paramtypes.Subspace, ak authkeeper.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, dk types.CommunityPoolKeeper, ek types.EpochKeeper, lk types.LockupKeeper, gk types.GammKeeper, ik types.IncentivesKeeper, lms types.LockupMsgServer, clk types.ConcentratedKeeper, pmk types.PoolManagerKeeper, vspk types.ValSetPreferenceKeeper) *Keeper { +func NewKeeper(storeKey storetypes.StoreKey, paramSpace paramtypes.Subspace, ak authkeeper.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, dk types.CommunityPoolKeeper, ek types.EpochKeeper, lk types.LockupKeeper, gk types.GammKeeper, ik types.IncentivesKeeper, lms types.LockupMsgServer, clk types.ConcentratedKeeper, pmk types.PoolManagerKeeper, vspk types.ValSetPreferenceKeeper, twapk types.TwapKeeper) *Keeper { // set KeyTable if it has not already been set if !paramSpace.HasKeyTable() { paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) @@ -57,6 +58,7 @@ func NewKeeper(storeKey storetypes.StoreKey, paramSpace paramtypes.Subspace, ak clk: clk, pmk: pmk, vspk: vspk, + twapk: twapk, lms: lms, } diff --git a/x/superfluid/keeper/stake.go b/x/superfluid/keeper/stake.go index 953491b92c5..ee683d865f4 100644 --- a/x/superfluid/keeper/stake.go +++ b/x/superfluid/keeper/stake.go @@ -165,11 +165,16 @@ func (k Keeper) validateLockForSFDelegate(ctx sdk.Context, lock *lockuptypes.Per denom := lock.Coins[0].Denom // ensure that the locks underlying denom is for an existing superfluid asset - _, err = k.GetSuperfluidAsset(ctx, denom) + asset, err := k.GetSuperfluidAsset(ctx, denom) if err != nil { return err } + // ensure that the asset is properly configured + if asset.AssetType == types.SuperfluidAssetTypeNative && asset.PricePoolId == 0 { + return errorsmod.Wrap(types.ErrNonSuperfluidAsset, "asset is not properly configured for superfluid staking (no price pool id)") + } + // prevent unbonding lockups to be not able to be used for superfluid staking if lock.IsUnlocking() { return errorsmod.Wrapf(types.ErrUnbondingLockupNotSupported, "lock id : %d", lock.ID) @@ -594,7 +599,7 @@ func (k Keeper) IterateDelegations(context context.Context, delegator sdk.AccAdd } // get osmo-equivalent token amount - amount, err := k.GetSuperfluidOSMOTokens(ctx, interim.Denom, coin.Amount) + amount, err := k.GetSuperfluidOSMOTokensExcludeNative(ctx, interim.Denom, coin.Amount) if err != nil { ctx.Logger().Error("failed to get osmo equivalent of token", "Denom", interim.Denom, "Amount", coin.Amount, "Error", err) return err diff --git a/x/superfluid/keeper/stake_test.go b/x/superfluid/keeper/stake_test.go index f72e48cbe19..e135b6c1fa8 100644 --- a/x/superfluid/keeper/stake_test.go +++ b/x/superfluid/keeper/stake_test.go @@ -238,6 +238,39 @@ func (s *KeeperTestSuite) TestValidateLockForSFDelegate() { lockIdAlreadySuperfluidDelegated: true, expectedErr: errorsmod.Wrapf(types.ErrAlreadyUsedSuperfluidLockup, "lock id : %d", uint64(1)), }, + { + name: "valid native lock", + lock: &lockuptypes.PeriodLock{ + Owner: lockOwner.String(), + Coins: sdk.NewCoins(sdk.NewCoin("foo", osmomath.NewInt(100))), + Duration: time.Hour * 24 * 21, + ID: 1, + }, + superfluidAssetToSet: types.SuperfluidAsset{Denom: "foo", AssetType: types.SuperfluidAssetTypeNative, PricePoolId: 1}, + expectedErr: nil, + }, + { + name: "invalid native lock - asset not set", + lock: &lockuptypes.PeriodLock{ + Owner: lockOwner.String(), + Coins: sdk.NewCoins(sdk.NewCoin("bar", osmomath.NewInt(100))), + Duration: time.Hour * 24 * 21, + ID: 1, + }, + superfluidAssetToSet: types.SuperfluidAsset{Denom: "foo", AssetType: types.SuperfluidAssetTypeNative, PricePoolId: 1}, + expectedErr: errorsmod.Wrapf(types.ErrNonSuperfluidAsset, "denom: %s", "bar"), + }, + { + name: "invalid native lock - asset not properly configured", + lock: &lockuptypes.PeriodLock{ + Owner: lockOwner.String(), + Coins: sdk.NewCoins(sdk.NewCoin("bar", osmomath.NewInt(100))), + Duration: time.Hour * 24 * 21, + ID: 1, + }, + superfluidAssetToSet: types.SuperfluidAsset{Denom: "foo", AssetType: types.SuperfluidAssetTypeNative}, + expectedErr: errorsmod.Wrapf(types.ErrNonSuperfluidAsset, "denom: %s", "bar"), + }, } for _, test := range tests { @@ -813,7 +846,7 @@ func (s *KeeperTestSuite) TestSuperfluidUndelegateAndUnbondLock() { bondDenom, err := s.App.StakingKeeper.BondDenom(s.Ctx) s.Require().NoError(err) supplyBefore := s.App.BankKeeper.GetSupply(s.Ctx, bondDenom) - osmoAmount, err := s.App.SuperfluidKeeper.GetSuperfluidOSMOTokens(s.Ctx, intermediaryAcc.Denom, tc.unlockAmount) + osmoAmount, err := s.App.SuperfluidKeeper.GetSuperfluidOSMOTokensExcludeNative(s.Ctx, intermediaryAcc.Denom, tc.unlockAmount) s.Require().NoError(err) unbondLockStartTime := startTime.Add(time.Hour) diff --git a/x/superfluid/keeper/twap_price.go b/x/superfluid/keeper/twap_price.go index d2db3f90389..7f40d4699a1 100644 --- a/x/superfluid/keeper/twap_price.go +++ b/x/superfluid/keeper/twap_price.go @@ -1,7 +1,10 @@ package keeper import ( + "strings" + "github.com/cosmos/gogoproto/proto" + cltypes "github.com/osmosis-labs/osmosis/v25/x/concentrated-liquidity/types" "github.com/osmosis-labs/osmosis/osmomath" gammtypes "github.com/osmosis-labs/osmosis/v25/x/gamm/types" @@ -34,7 +37,30 @@ func (k Keeper) SetOsmoEquivalentMultiplier(ctx sdk.Context, epoch int64, denom prefixStore.Set([]byte(denom), bz) } +func isNonNative(denom string) bool { + if strings.HasPrefix(denom, cltypes.ConcentratedLiquidityTokenPrefix) { + return true + } + if strings.HasPrefix(denom, gammtypes.GAMMTokenPrefix) { + return true + } + + return false +} + func (k Keeper) GetSuperfluidOSMOTokens(ctx sdk.Context, denom string, amount osmomath.Int) (osmomath.Int, error) { + return k.getSuperfluidOSMOTokens(ctx, denom, amount, true) +} + +func (k Keeper) GetSuperfluidOSMOTokensExcludeNative(ctx sdk.Context, denom string, amount osmomath.Int) (osmomath.Int, error) { + return k.getSuperfluidOSMOTokens(ctx, denom, amount, false) +} + +func (k Keeper) getSuperfluidOSMOTokens(ctx sdk.Context, denom string, amount osmomath.Int, includeNative bool) (osmomath.Int, error) { + if !includeNative && !isNonNative(denom) { + return osmomath.ZeroInt(), nil + } + multiplier := k.GetOsmoEquivalentMultiplier(ctx, denom) if multiplier.IsZero() { return osmomath.ZeroInt(), nil @@ -45,6 +71,8 @@ func (k Keeper) GetSuperfluidOSMOTokens(ctx sdk.Context, denom string, amount os if err != nil { return osmomath.ZeroInt(), err } + + // TODO: here! change the risk adjusted value based on the type/denom return k.GetRiskAdjustedOsmoValue(ctx, decAmt.RoundInt()), nil } diff --git a/x/superfluid/keeper/twap_price_test.go b/x/superfluid/keeper/twap_price_test.go index a14694a3ebc..0726c417df5 100644 --- a/x/superfluid/keeper/twap_price_test.go +++ b/x/superfluid/keeper/twap_price_test.go @@ -75,7 +75,7 @@ func (s *KeeperTestSuite) TestGetSuperfluidOSMOTokens() { s.Require().Equal(multiplier, osmomath.NewDec(2)) // Should get error since asset is not superfluid enabled - osmoTokens, err := s.App.SuperfluidKeeper.GetSuperfluidOSMOTokens(s.Ctx, gammShareDenom, testAmount) + osmoTokens, err := s.App.SuperfluidKeeper.GetSuperfluidOSMOTokensExcludeNative(s.Ctx, gammShareDenom, testAmount) s.Require().Error(err) s.Require().ErrorIs(err, types.ErrNonSuperfluidAsset) s.Require().Equal(osmoTokens, osmomath.NewInt(0)) @@ -92,7 +92,7 @@ func (s *KeeperTestSuite) TestGetSuperfluidOSMOTokens() { s.App.SuperfluidKeeper.SetOsmoEquivalentMultiplier(s.Ctx, epoch, gammShareDenom, multiplier) // Get superfluid OSMO tokens - osmoTokens, err = s.App.SuperfluidKeeper.GetSuperfluidOSMOTokens(s.Ctx, gammShareDenom, testAmount) + osmoTokens, err = s.App.SuperfluidKeeper.GetSuperfluidOSMOTokensExcludeNative(s.Ctx, gammShareDenom, testAmount) s.Require().NoError(err) // Adjust result with risk factor @@ -113,7 +113,7 @@ func (s *KeeperTestSuite) TestGetSuperfluidOSMOTokens() { s.App.SuperfluidKeeper.SetOsmoEquivalentMultiplier(s.Ctx, epoch, clShareDenom, multiplier) // Get superfluid OSMO tokens - osmoTokens, err = s.App.SuperfluidKeeper.GetSuperfluidOSMOTokens(s.Ctx, clShareDenom, testAmount) + osmoTokens, err = s.App.SuperfluidKeeper.GetSuperfluidOSMOTokensExcludeNative(s.Ctx, clShareDenom, testAmount) s.Require().NoError(err) // Adjust result with risk factor diff --git a/x/superfluid/types/expected_keepers.go b/x/superfluid/types/expected_keepers.go index e48b0130421..ab5a4784969 100644 --- a/x/superfluid/types/expected_keepers.go +++ b/x/superfluid/types/expected_keepers.go @@ -141,3 +141,7 @@ type PoolManagerKeeper interface { type ValSetPreferenceKeeper interface { DelegateToValidatorSet(ctx sdk.Context, delegatorAddr string, coin sdk.Coin) error } + +type TwapKeeper interface { + GetArithmeticTwapToNow(ctx sdk.Context, poolId uint64, baseAssetDenom string, quoteAssetDenom string, startTime time.Time) (osmomath.Dec, error) +} diff --git a/x/superfluid/types/superfluid.pb.go b/x/superfluid/types/superfluid.pb.go index 701fd1d589b..ebb2819bf5d 100644 --- a/x/superfluid/types/superfluid.pb.go +++ b/x/superfluid/types/superfluid.pb.go @@ -64,6 +64,9 @@ type SuperfluidAsset struct { // AssetType indicates whether the superfluid asset is a native token or an lp // share AssetType SuperfluidAssetType `protobuf:"varint,2,opt,name=asset_type,json=assetType,proto3,enum=osmosis.superfluid.SuperfluidAssetType" json:"asset_type,omitempty"` + // For non-osmo native assets, we need a pool_id osmo/asset to determine the + // twap of the asset + PricePoolId uint64 `protobuf:"varint,3,opt,name=price_pool_id,json=pricePoolId,proto3" json:"price_pool_id,omitempty"` } func (m *SuperfluidAsset) Reset() { *m = SuperfluidAsset{} } @@ -494,59 +497,61 @@ func init() { } var fileDescriptor_79d3c29d82dbb734 = []byte{ - // 831 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x55, 0xcf, 0x6f, 0x1b, 0x45, - 0x14, 0xf6, 0xda, 0x6e, 0xd2, 0x4c, 0xa0, 0xb8, 0xdb, 0xa8, 0x24, 0x41, 0xd9, 0x0d, 0x5b, 0xa4, - 0x5a, 0xad, 0xba, 0xab, 0x04, 0x81, 0x50, 0x6f, 0x4e, 0x0a, 0x92, 0x51, 0x28, 0xd6, 0x9a, 0x0a, - 0xc4, 0x65, 0x35, 0xde, 0x79, 0x5d, 0x8f, 0x3c, 0xbb, 0xb3, 0xdd, 0x99, 0x35, 0xf8, 0xc6, 0x81, - 0x43, 0x8f, 0xfc, 0x09, 0x95, 0xb8, 0x71, 0xe5, 0x9f, 0xe8, 0xb1, 0x12, 0x17, 0xc4, 0x21, 0xa0, - 0xe4, 0xc2, 0xb9, 0x7f, 0x01, 0x9a, 0xd9, 0x1f, 0xde, 0x34, 0xae, 0x10, 0x17, 0x7a, 0xf2, 0xcc, - 0xfb, 0xde, 0xbc, 0xf7, 0x7d, 0xf3, 0x3e, 0xcf, 0xa2, 0x5b, 0x5c, 0xc4, 0x5c, 0x50, 0xe1, 0x89, - 0x3c, 0x85, 0xec, 0x31, 0xcb, 0x29, 0x69, 0x2c, 0xdd, 0x34, 0xe3, 0x92, 0x9b, 0x66, 0x99, 0xe4, - 0x2e, 0x91, 0xdd, 0xad, 0x88, 0x47, 0x5c, 0xc3, 0x9e, 0x5a, 0x15, 0x99, 0xbb, 0x56, 0xc4, 0x79, - 0xc4, 0xc0, 0xd3, 0xbb, 0x49, 0xfe, 0xd8, 0x23, 0x79, 0x86, 0x25, 0xe5, 0x49, 0x89, 0xdb, 0xaf, - 0xe2, 0x92, 0xc6, 0x20, 0x24, 0x8e, 0xd3, 0xaa, 0x40, 0xa8, 0x7b, 0x79, 0x13, 0x2c, 0xc0, 0x9b, - 0x1f, 0x4c, 0x40, 0xe2, 0x03, 0x2f, 0xe4, 0xb4, 0x2a, 0xb0, 0x53, 0xf1, 0x65, 0x3c, 0x9c, 0xe5, - 0xa9, 0xfe, 0x29, 0x20, 0x67, 0x81, 0xde, 0x19, 0xd7, 0xfc, 0x06, 0x42, 0x80, 0x34, 0xb7, 0xd0, - 0x15, 0x02, 0x09, 0x8f, 0xb7, 0x8d, 0x7d, 0xa3, 0xbf, 0xe1, 0x17, 0x1b, 0xf3, 0x33, 0x84, 0xb0, - 0x82, 0x03, 0xb9, 0x48, 0x61, 0xbb, 0xbd, 0x6f, 0xf4, 0xaf, 0x1d, 0xde, 0x76, 0x2f, 0x6b, 0x74, - 0x5f, 0x29, 0xf7, 0xd5, 0x22, 0x05, 0x7f, 0x03, 0x57, 0xcb, 0xfb, 0x57, 0x9f, 0x3e, 0xb3, 0x5b, - 0x7f, 0x3f, 0xb3, 0x0d, 0x67, 0x86, 0xf6, 0x96, 0xb9, 0xc3, 0x44, 0x42, 0x16, 0x03, 0xa1, 0x38, - 0x5b, 0x0c, 0xc2, 0x90, 0xe7, 0xc9, 0xeb, 0x88, 0xec, 0xa0, 0xab, 0x73, 0xcc, 0x02, 0x4c, 0x48, - 0xa6, 0x69, 0x6c, 0xf8, 0xeb, 0x73, 0xcc, 0x06, 0x84, 0x64, 0x0a, 0x8a, 0x70, 0x1e, 0x41, 0x40, - 0xc9, 0x76, 0x67, 0xdf, 0xe8, 0x77, 0xfd, 0x75, 0xbd, 0x1f, 0x12, 0xe7, 0x57, 0x03, 0x59, 0x5f, - 0x8a, 0x98, 0x7f, 0xfa, 0x24, 0xa7, 0x73, 0xcc, 0x20, 0x91, 0x5f, 0xe4, 0x4c, 0xd2, 0x94, 0x51, - 0xc8, 0x7c, 0x08, 0x79, 0x46, 0xcc, 0xf7, 0xd1, 0x5b, 0x90, 0xf2, 0x70, 0x1a, 0x24, 0x79, 0x3c, - 0x81, 0x4c, 0x77, 0xed, 0xf8, 0x9b, 0x3a, 0xf6, 0x50, 0x87, 0x96, 0x8c, 0xda, 0x4d, 0x46, 0xdf, - 0x20, 0x14, 0xd7, 0xc5, 0x74, 0xe3, 0x8d, 0xa3, 0x4f, 0x9e, 0x9f, 0xda, 0xad, 0x3f, 0x4e, 0xed, - 0xf7, 0x8a, 0xd1, 0x08, 0x32, 0x73, 0x29, 0xf7, 0x62, 0x2c, 0xa7, 0xee, 0x09, 0x44, 0x38, 0x5c, - 0x3c, 0x80, 0xf0, 0xe5, 0xa9, 0x7d, 0x7d, 0x81, 0x63, 0x76, 0xdf, 0x59, 0x1e, 0x77, 0xfc, 0x46, - 0x2d, 0xe7, 0x65, 0x1b, 0xed, 0x2e, 0xef, 0xe8, 0x01, 0x30, 0x88, 0xb4, 0x31, 0x4a, 0xc6, 0x77, - 0xd1, 0x75, 0x52, 0xc4, 0x78, 0xa6, 0x2f, 0x04, 0x84, 0x28, 0x2f, 0xab, 0x57, 0x03, 0x83, 0x22, - 0xae, 0x92, 0xe7, 0x98, 0x51, 0x72, 0x21, 0xb9, 0xd0, 0xd1, 0xab, 0x81, 0x2a, 0xf9, 0xbb, 0xba, - 0x32, 0xe5, 0x49, 0x80, 0x63, 0x35, 0x0f, 0xad, 0x6c, 0xf3, 0x70, 0xc7, 0x2d, 0x24, 0xb9, 0xca, - 0x6d, 0x6e, 0xe9, 0x36, 0xf7, 0x98, 0xd3, 0xe4, 0xc8, 0x53, 0xa2, 0x7f, 0xf9, 0xd3, 0xbe, 0x1d, - 0x51, 0x39, 0xcd, 0x27, 0x6e, 0xc8, 0x63, 0xaf, 0xb4, 0x66, 0xf1, 0x73, 0x4f, 0x90, 0x99, 0xa7, - 0x0c, 0x24, 0xf4, 0x81, 0x9a, 0x25, 0xe5, 0xc9, 0x40, 0xf7, 0x30, 0x7f, 0x30, 0xd0, 0x36, 0xd4, - 0x33, 0x0a, 0x84, 0xc4, 0x33, 0x20, 0x15, 0x81, 0xee, 0xbf, 0x11, 0xb8, 0xfb, 0x5f, 0x9a, 0xdf, - 0x5c, 0xf6, 0x19, 0xeb, 0x36, 0x05, 0x05, 0xe7, 0x09, 0xba, 0x75, 0xc2, 0xc3, 0xd9, 0x70, 0x95, - 0x27, 0x8f, 0x79, 0x92, 0x40, 0xa8, 0xf8, 0x9a, 0xef, 0xa2, 0x75, 0xf5, 0x3f, 0x52, 0x5e, 0x33, - 0xb4, 0xd7, 0xd6, 0x98, 0x3e, 0x65, 0x1e, 0xa0, 0x2d, 0xda, 0x38, 0x19, 0xe0, 0xe2, 0x68, 0x79, - 0xd7, 0x37, 0xe8, 0xe5, 0xaa, 0xce, 0x1d, 0x74, 0xf3, 0x51, 0x92, 0x72, 0xce, 0xbe, 0x9e, 0x52, - 0x09, 0x8c, 0x0a, 0x09, 0x64, 0xc4, 0x39, 0x13, 0x66, 0x0f, 0x75, 0x28, 0x51, 0x43, 0xed, 0xf4, - 0xbb, 0xbe, 0x5a, 0x3a, 0xbf, 0x75, 0x90, 0x73, 0xcc, 0x93, 0x10, 0x12, 0x99, 0xe1, 0x32, 0xef, - 0x91, 0x80, 0x6c, 0xc4, 0x05, 0xbd, 0xe8, 0x8d, 0xcb, 0xe3, 0x36, 0x5e, 0x33, 0x6e, 0x1b, 0x6d, - 0xa6, 0xe5, 0x71, 0xa5, 0xa7, 0xad, 0xf5, 0xa0, 0x2a, 0x34, 0x24, 0x4d, 0xb1, 0x9d, 0x0b, 0x62, - 0x3f, 0x47, 0xd7, 0xc4, 0x22, 0x91, 0x53, 0x90, 0x34, 0x0c, 0x54, 0xac, 0x1c, 0xd2, 0x5e, 0xfd, - 0x34, 0x14, 0x6f, 0x8e, 0x3b, 0xae, 0xb2, 0xd4, 0xdd, 0x1e, 0x75, 0x95, 0x53, 0xfc, 0xb7, 0x45, - 0x33, 0xb8, 0xda, 0x74, 0x57, 0xde, 0xb4, 0xe9, 0xd6, 0xfe, 0x0f, 0xd3, 0xdd, 0xf9, 0xd1, 0x40, - 0x37, 0x56, 0xbc, 0x9c, 0xe6, 0x1e, 0xda, 0x59, 0x11, 0x7e, 0x88, 0x25, 0x9d, 0x43, 0xaf, 0x65, - 0x5a, 0xcd, 0xf7, 0xa1, 0x86, 0x4f, 0x46, 0xe3, 0x29, 0xce, 0xa0, 0x67, 0x98, 0x7d, 0xf4, 0xc1, - 0x0a, 0xbc, 0x69, 0x9f, 0x22, 0xb3, 0xbd, 0xdb, 0x7d, 0xfa, 0xb3, 0xd5, 0x3a, 0x1a, 0x3d, 0x3f, - 0xb3, 0x8c, 0x17, 0x67, 0x96, 0xf1, 0xd7, 0x99, 0x65, 0xfc, 0x74, 0x6e, 0xb5, 0x5e, 0x9c, 0x5b, - 0xad, 0xdf, 0xcf, 0xad, 0xd6, 0xb7, 0x1f, 0x37, 0x14, 0x96, 0xa3, 0xbd, 0xc7, 0xf0, 0x44, 0x54, - 0x1b, 0x6f, 0x7e, 0xf8, 0x91, 0xf7, 0x7d, 0xf3, 0x8b, 0xa8, 0x55, 0x4f, 0xd6, 0xf4, 0x77, 0xe6, - 0xc3, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xc7, 0xb8, 0x2c, 0xd7, 0x34, 0x07, 0x00, 0x00, + // 849 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x55, 0x4d, 0x6f, 0xdc, 0x44, + 0x18, 0x5e, 0xef, 0x6e, 0x93, 0x66, 0x42, 0xcb, 0xd6, 0x8d, 0x4a, 0x12, 0x14, 0x3b, 0xb8, 0x48, + 0x5d, 0xb5, 0xaa, 0xad, 0x04, 0x81, 0x50, 0x6f, 0x9b, 0x14, 0xa4, 0x45, 0xa1, 0x44, 0x5e, 0x2a, + 0x10, 0x17, 0x6b, 0xd6, 0xf3, 0xd6, 0x3b, 0xda, 0xb1, 0xc7, 0xf5, 0x8c, 0x17, 0xf6, 0xc6, 0x81, + 0x43, 0x8f, 0xfc, 0x01, 0xa4, 0x4a, 0xdc, 0xb8, 0xf2, 0x27, 0x7a, 0xac, 0xc4, 0x05, 0x71, 0x08, + 0x28, 0xb9, 0x70, 0xee, 0x2f, 0x40, 0x33, 0xfe, 0x58, 0xa7, 0xd9, 0x08, 0x71, 0xa1, 0x27, 0xcf, + 0xbc, 0x9f, 0xcf, 0x33, 0xef, 0x33, 0x63, 0x74, 0x9b, 0x8b, 0x98, 0x0b, 0x2a, 0x3c, 0x91, 0xa7, + 0x90, 0x3d, 0x61, 0x39, 0x25, 0x8d, 0xa5, 0x9b, 0x66, 0x5c, 0x72, 0xd3, 0x2c, 0x83, 0xdc, 0x85, + 0x67, 0x7b, 0x23, 0xe2, 0x11, 0xd7, 0x6e, 0x4f, 0xad, 0x8a, 0xc8, 0x6d, 0x2b, 0xe2, 0x3c, 0x62, + 0xe0, 0xe9, 0xdd, 0x38, 0x7f, 0xe2, 0x91, 0x3c, 0xc3, 0x92, 0xf2, 0xa4, 0xf4, 0xdb, 0xaf, 0xfb, + 0x25, 0x8d, 0x41, 0x48, 0x1c, 0xa7, 0x55, 0x81, 0x50, 0xf7, 0xf2, 0xc6, 0x58, 0x80, 0x37, 0xdb, + 0x1b, 0x83, 0xc4, 0x7b, 0x5e, 0xc8, 0x69, 0x55, 0x60, 0xab, 0xc2, 0xcb, 0x78, 0x38, 0xcd, 0x53, + 0xfd, 0x29, 0x5c, 0xce, 0x4f, 0x06, 0x7a, 0x7b, 0x54, 0x03, 0x1c, 0x08, 0x01, 0xd2, 0xdc, 0x40, + 0x57, 0x08, 0x24, 0x3c, 0xde, 0x34, 0x76, 0x8d, 0xfe, 0x9a, 0x5f, 0x6c, 0xcc, 0x4f, 0x11, 0xc2, + 0xca, 0x1d, 0xc8, 0x79, 0x0a, 0x9b, 0xed, 0x5d, 0xa3, 0x7f, 0x7d, 0xff, 0x8e, 0x7b, 0x91, 0xa4, + 0xfb, 0x5a, 0xb9, 0x2f, 0xe7, 0x29, 0xf8, 0x6b, 0xb8, 0x5a, 0x9a, 0x0e, 0xba, 0x96, 0x66, 0x34, + 0x84, 0x20, 0xe5, 0x9c, 0x05, 0x94, 0x6c, 0x76, 0x76, 0x8d, 0x7e, 0xd7, 0x5f, 0xd7, 0xc6, 0x63, + 0xce, 0xd9, 0x90, 0x3c, 0xb8, 0xfa, 0xec, 0xb9, 0xdd, 0xfa, 0xfb, 0xb9, 0x6d, 0x38, 0x53, 0xb4, + 0xb3, 0xa8, 0x37, 0x4c, 0x24, 0x64, 0x31, 0x10, 0x8a, 0xb3, 0xf9, 0x20, 0x0c, 0x79, 0x9e, 0x5c, + 0x06, 0x76, 0x0b, 0x5d, 0x9d, 0x61, 0x16, 0x60, 0x42, 0x32, 0x0d, 0x75, 0xcd, 0x5f, 0x9d, 0x61, + 0x36, 0x20, 0x24, 0x53, 0xae, 0x08, 0xe7, 0x11, 0x2c, 0x5a, 0xaf, 0xea, 0xfd, 0x90, 0x38, 0xbf, + 0x1a, 0xc8, 0xfa, 0x42, 0xc4, 0xfc, 0x93, 0xa7, 0x39, 0x9d, 0x61, 0x06, 0x89, 0xfc, 0x3c, 0x67, + 0x92, 0xa6, 0x8c, 0x42, 0xe6, 0x43, 0xc8, 0x33, 0x62, 0xbe, 0x87, 0xde, 0x82, 0x94, 0x87, 0x93, + 0x20, 0xc9, 0xe3, 0x31, 0x64, 0xba, 0x6b, 0xc7, 0x5f, 0xd7, 0xb6, 0x47, 0xda, 0xb4, 0x40, 0xd4, + 0x6e, 0x22, 0xfa, 0x1a, 0xa1, 0xb8, 0x2e, 0xa6, 0x1b, 0xaf, 0x1d, 0x7c, 0xfc, 0xe2, 0xc4, 0x6e, + 0xfd, 0x71, 0x62, 0xbf, 0x5b, 0xcc, 0x4f, 0x90, 0xa9, 0x4b, 0xb9, 0x17, 0x63, 0x39, 0x71, 0x8f, + 0x20, 0xc2, 0xe1, 0xfc, 0x21, 0x84, 0xaf, 0x4e, 0xec, 0x1b, 0x73, 0x1c, 0xb3, 0x07, 0xce, 0x22, + 0xdd, 0xf1, 0x1b, 0xb5, 0x9c, 0x57, 0x6d, 0xb4, 0xbd, 0x38, 0xa3, 0x87, 0xc0, 0x20, 0xd2, 0xea, + 0x29, 0x11, 0xdf, 0x43, 0x37, 0x48, 0x61, 0xe3, 0x99, 0x3e, 0x10, 0x10, 0xa2, 0x3c, 0xac, 0x5e, + 0xed, 0x18, 0x14, 0x76, 0x15, 0x3c, 0xc3, 0x8c, 0x92, 0x73, 0xc1, 0x05, 0x8f, 0x5e, 0xed, 0xa8, + 0x82, 0xbf, 0xad, 0x2b, 0x53, 0x9e, 0x04, 0x38, 0x56, 0xf3, 0xd0, 0xcc, 0xd6, 0xf7, 0xb7, 0xdc, + 0x82, 0x92, 0xab, 0x24, 0xe9, 0x96, 0x92, 0x74, 0x0f, 0x39, 0x4d, 0x0e, 0x3c, 0x45, 0xfa, 0x97, + 0x3f, 0xed, 0x3b, 0x11, 0x95, 0x93, 0x7c, 0xec, 0x86, 0x3c, 0xf6, 0x4a, 0xfd, 0x16, 0x9f, 0xfb, + 0x82, 0x4c, 0x3d, 0x25, 0x32, 0xa1, 0x13, 0x6a, 0x94, 0x94, 0x27, 0x03, 0xdd, 0xc3, 0xfc, 0xde, + 0x40, 0x9b, 0x50, 0xcf, 0x28, 0x10, 0x12, 0x4f, 0x81, 0x54, 0x00, 0xba, 0xff, 0x06, 0xe0, 0xde, + 0x7f, 0x69, 0x7e, 0x6b, 0xd1, 0x67, 0xa4, 0xdb, 0x14, 0x10, 0x9c, 0xa7, 0xe8, 0xf6, 0x11, 0x0f, + 0xa7, 0xc3, 0x65, 0x9a, 0x3c, 0xe4, 0x49, 0x02, 0xa1, 0xc2, 0x6b, 0xbe, 0x83, 0x56, 0xd5, 0x65, + 0x53, 0x5a, 0x33, 0xb4, 0xd6, 0x56, 0x98, 0xce, 0x32, 0xf7, 0xd0, 0x06, 0x6d, 0x64, 0x06, 0xb8, + 0x48, 0x2d, 0xcf, 0xfa, 0x26, 0xbd, 0x58, 0xd5, 0xb9, 0x8b, 0x6e, 0x3d, 0x4e, 0xd4, 0xa5, 0xf9, + 0x6a, 0x42, 0x25, 0x30, 0x2a, 0x24, 0x10, 0x75, 0x5f, 0x84, 0xd9, 0x43, 0x1d, 0x4a, 0xd4, 0x50, + 0x3b, 0xfd, 0xae, 0xaf, 0x96, 0xce, 0x6f, 0x1d, 0xe4, 0x1c, 0xf2, 0x24, 0x84, 0x44, 0x66, 0xb8, + 0x8c, 0x7b, 0x2c, 0x20, 0x3b, 0xe6, 0x82, 0x9e, 0xd7, 0xc6, 0xc5, 0x71, 0x1b, 0x97, 0x8c, 0xdb, + 0x46, 0xeb, 0x69, 0x99, 0xae, 0xf8, 0xb4, 0x35, 0x1f, 0x54, 0x99, 0x86, 0xa4, 0x49, 0xb6, 0x73, + 0x8e, 0xec, 0x67, 0xe8, 0xba, 0x98, 0x27, 0x72, 0x02, 0x92, 0x86, 0x81, 0xb2, 0x95, 0x43, 0xda, + 0xa9, 0x9f, 0x8f, 0xe2, 0x61, 0x72, 0x47, 0x55, 0x94, 0x3a, 0xdb, 0x83, 0xae, 0x52, 0x8a, 0x7f, + 0x4d, 0x34, 0x8d, 0xcb, 0x45, 0x77, 0xe5, 0x4d, 0x8b, 0x6e, 0xe5, 0xff, 0x10, 0xdd, 0xdd, 0x1f, + 0x0c, 0x74, 0x73, 0xc9, 0xeb, 0x6a, 0xee, 0xa0, 0xad, 0x25, 0xe6, 0x47, 0x58, 0xd2, 0x19, 0xf4, + 0x5a, 0xa6, 0xd5, 0x7c, 0x1f, 0x6a, 0xf7, 0xd1, 0xf1, 0x68, 0x82, 0x33, 0xe8, 0x19, 0x66, 0x1f, + 0xbd, 0xbf, 0xc4, 0xdf, 0x94, 0x4f, 0x11, 0xd9, 0xde, 0xee, 0x3e, 0xfb, 0xd9, 0x6a, 0x1d, 0x1c, + 0xbf, 0x38, 0xb5, 0x8c, 0x97, 0xa7, 0x96, 0xf1, 0xd7, 0xa9, 0x65, 0xfc, 0x78, 0x66, 0xb5, 0x5e, + 0x9e, 0x59, 0xad, 0xdf, 0xcf, 0xac, 0xd6, 0x37, 0x1f, 0x35, 0x18, 0x96, 0xa3, 0xbd, 0xcf, 0xf0, + 0x58, 0x54, 0x1b, 0x6f, 0xb6, 0xff, 0xa1, 0xf7, 0x5d, 0xf3, 0xb7, 0xa9, 0x59, 0x8f, 0x57, 0xf4, + 0xcf, 0xe8, 0x83, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xda, 0xb4, 0x77, 0x48, 0x59, 0x07, 0x00, + 0x00, } func (this *SuperfluidAsset) Equal(that interface{}) bool { @@ -574,6 +579,9 @@ func (this *SuperfluidAsset) Equal(that interface{}) bool { if this.AssetType != that1.AssetType { return false } + if this.PricePoolId != that1.PricePoolId { + return false + } return true } func (m *SuperfluidAsset) Marshal() (dAtA []byte, err error) { @@ -596,6 +604,11 @@ func (m *SuperfluidAsset) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.PricePoolId != 0 { + i = encodeVarintSuperfluid(dAtA, i, uint64(m.PricePoolId)) + i-- + dAtA[i] = 0x18 + } if m.AssetType != 0 { i = encodeVarintSuperfluid(dAtA, i, uint64(m.AssetType)) i-- @@ -929,6 +942,9 @@ func (m *SuperfluidAsset) Size() (n int) { if m.AssetType != 0 { n += 1 + sovSuperfluid(uint64(m.AssetType)) } + if m.PricePoolId != 0 { + n += 1 + sovSuperfluid(uint64(m.PricePoolId)) + } return n } @@ -1138,6 +1154,25 @@ func (m *SuperfluidAsset) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PricePoolId", wireType) + } + m.PricePoolId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSuperfluid + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PricePoolId |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipSuperfluid(dAtA[iNdEx:])