From 8e1f1b70d4440869ad3d1d864aa75a3172a329f7 Mon Sep 17 00:00:00 2001 From: jelysn <129082781+jelysn@users.noreply.github.com> Date: Fri, 23 Feb 2024 23:22:40 +0800 Subject: [PATCH] Usdc high apr fix for Elys/Eden staking (#386) * usdc high apr fix and some simplification in incentive module * add migration script to migrate distribution interval from 10 to 1200 * fixing unit test failure * resolve unit test failure --------- Co-authored-by: Cosmic Vagabond <121588426+cosmic-vagabond@users.noreply.github.com> --- proto/elys/incentive/genesis.proto | 1 - x/amm/keeper/calc_in_route_by_denom_test.go | 10 +++--- x/amm/keeper/calc_out_route_by_denom_test.go | 10 +++--- x/amm/keeper/keeper_test.go | 4 +-- x/commitment/keeper/claim_reward.go | 8 +---- x/commitment/keeper/commitments.go | 12 +++---- .../keeper/msg_server_uncommit_tokens.go | 2 +- x/commitment/types/commitments.go | 35 +++---------------- x/incentive/keeper/apr.go | 1 - x/incentive/keeper/keeper.go | 4 ++- x/incentive/keeper/keeper_withdraw.go | 23 +++++++----- x/incentive/keeper/keeper_withdraw_test.go | 6 ++-- x/incentive/migrations/v10_migration.go | 18 ++++++++++ x/incentive/module.go | 4 +-- 14 files changed, 66 insertions(+), 72 deletions(-) create mode 100644 x/incentive/migrations/v10_migration.go diff --git a/proto/elys/incentive/genesis.proto b/proto/elys/incentive/genesis.proto index e334c9b2d..e03177057 100644 --- a/proto/elys/incentive/genesis.proto +++ b/proto/elys/incentive/genesis.proto @@ -17,4 +17,3 @@ message GenesisState { // fee_pool defines the fee pool at genesis. FeePool fee_pool = 2 [(gogoproto.nullable) = false]; } - diff --git a/x/amm/keeper/calc_in_route_by_denom_test.go b/x/amm/keeper/calc_in_route_by_denom_test.go index b472546ee..061012791 100644 --- a/x/amm/keeper/calc_in_route_by_denom_test.go +++ b/x/amm/keeper/calc_in_route_by_denom_test.go @@ -17,24 +17,24 @@ func TestCalcInRouteByDenom(t *testing.T) { SetupMockPools(&k, ctx) // Test direct pool route - route, err := k.CalcInRouteByDenom(ctx, "denom1", "denom2", "baseCurrency") + route, err := k.CalcInRouteByDenom(ctx, "denom1", "denom2", "uusdc") require.NoError(t, err) require.Len(t, route, 1) require.Equal(t, route[0].TokenOutDenom, "denom2") // Test route via base currency - route, err = k.CalcInRouteByDenom(ctx, "denom1", "denom3", "baseCurrency") + route, err = k.CalcInRouteByDenom(ctx, "denom1", "denom3", "uusdc") require.NoError(t, err) require.Len(t, route, 2) - require.Equal(t, route[0].TokenOutDenom, "baseCurrency") + require.Equal(t, route[0].TokenOutDenom, "uusdc") require.Equal(t, route[1].TokenOutDenom, "denom3") // Test no available pool - _, err = k.CalcInRouteByDenom(ctx, "denom1", "nonexistent", "baseCurrency") + _, err = k.CalcInRouteByDenom(ctx, "denom1", "nonexistent", "uusdc") require.Error(t, err) // Test same input and output denomination - route, err = k.CalcInRouteByDenom(ctx, "denom1", "denom1", "baseCurrency") + route, err = k.CalcInRouteByDenom(ctx, "denom1", "denom1", "uusdc") require.NoError(t, err) require.Len(t, route, 1) require.Equal(t, route[0].TokenOutDenom, "denom1") diff --git a/x/amm/keeper/calc_out_route_by_denom_test.go b/x/amm/keeper/calc_out_route_by_denom_test.go index c55d4733f..c91bb500f 100644 --- a/x/amm/keeper/calc_out_route_by_denom_test.go +++ b/x/amm/keeper/calc_out_route_by_denom_test.go @@ -17,23 +17,23 @@ func TestCalcOutRouteByDenom(t *testing.T) { SetupMockPools(&k, ctx) // Test direct pool route - route, err := k.CalcOutRouteByDenom(ctx, "denom2", "denom1", "baseCurrency") + route, err := k.CalcOutRouteByDenom(ctx, "denom2", "denom1", "uusdc") require.NoError(t, err) require.Len(t, route, 1) require.Equal(t, route[0].TokenInDenom, "denom1") // Test route via base currency - route, err = k.CalcOutRouteByDenom(ctx, "denom3", "denom1", "baseCurrency") + route, err = k.CalcOutRouteByDenom(ctx, "denom3", "denom1", "uusdc") require.NoError(t, err) require.Len(t, route, 2) - require.Equal(t, route[0].TokenInDenom, "baseCurrency") + require.Equal(t, route[0].TokenInDenom, "uusdc") require.Equal(t, route[1].TokenInDenom, "denom1") // Test no available pool - _, err = k.CalcOutRouteByDenom(ctx, "nonexistent", "denom1", "baseCurrency") + _, err = k.CalcOutRouteByDenom(ctx, "nonexistent", "denom1", "uusdc") require.Error(t, err) // Test same input and output denomination - _, err = k.CalcOutRouteByDenom(ctx, "denom1", "denom1", "baseCurrency") + _, err = k.CalcOutRouteByDenom(ctx, "denom1", "denom1", "uusdc") require.Error(t, err) } diff --git a/x/amm/keeper/keeper_test.go b/x/amm/keeper/keeper_test.go index f363a7da6..973768725 100644 --- a/x/amm/keeper/keeper_test.go +++ b/x/amm/keeper/keeper_test.go @@ -97,7 +97,7 @@ func SetupMockPools(k *keeper.Keeper, ctx sdk.Context) { { PoolId: 2, PoolAssets: []types.PoolAsset{ - {Token: sdk.NewCoin("baseCurrency", sdk.NewInt(1000)), Weight: sdk.OneInt()}, + {Token: sdk.NewCoin("uusdc", sdk.NewInt(1000)), Weight: sdk.OneInt()}, {Token: sdk.NewCoin("denom1", sdk.NewInt(1000)), Weight: sdk.OneInt()}, }, TotalWeight: sdk.NewInt(2), @@ -108,7 +108,7 @@ func SetupMockPools(k *keeper.Keeper, ctx sdk.Context) { { PoolId: 3, PoolAssets: []types.PoolAsset{ - {Token: sdk.NewCoin("baseCurrency", sdk.NewInt(1000)), Weight: sdk.OneInt()}, + {Token: sdk.NewCoin("uusdc", sdk.NewInt(1000)), Weight: sdk.OneInt()}, {Token: sdk.NewCoin("denom3", sdk.NewInt(1000)), Weight: sdk.OneInt()}, }, TotalWeight: sdk.NewInt(2), diff --git a/x/commitment/keeper/claim_reward.go b/x/commitment/keeper/claim_reward.go index 6eadd5459..9583f53cc 100644 --- a/x/commitment/keeper/claim_reward.go +++ b/x/commitment/keeper/claim_reward.go @@ -4,7 +4,6 @@ import ( errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" aptypes "github.com/elys-network/elys/x/assetprofile/types" "github.com/elys-network/elys/x/commitment/types" ) @@ -70,12 +69,7 @@ func (k Keeper) RecordClaimReward(ctx sdk.Context, creator string, denom string, withdrawCoins := sdk.NewCoins(sdk.NewCoin(denom, amount)) - addr, err := sdk.AccAddressFromBech32(commitments.Creator) - if err != nil { - return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "unable to convert address from bech32") - } - - err = k.HandleWithdrawFromCommitment(ctx, &commitments, addr, withdrawCoins, false) + err := k.HandleWithdrawFromCommitment(ctx, &commitments, withdrawCoins, false, sdk.AccAddress{}) if err != nil { return err } diff --git a/x/commitment/keeper/commitments.go b/x/commitment/keeper/commitments.go index e02105a22..aec3d59b2 100644 --- a/x/commitment/keeper/commitments.go +++ b/x/commitment/keeper/commitments.go @@ -164,21 +164,21 @@ func (k Keeper) BurnEdenBoost(ctx sdk.Context, creator string, denom string, amo return commitments, nil } -func (k Keeper) HandleWithdrawFromCommitment(ctx sdk.Context, commitments *types.Commitments, addr sdk.AccAddress, amount sdk.Coins, sendCoins bool) error { +func (k Keeper) HandleWithdrawFromCommitment(ctx sdk.Context, commitments *types.Commitments, amount sdk.Coins, sendCoins bool, addr sdk.AccAddress) error { edenAmount := amount.AmountOf(ptypes.Eden) edenBAmount := amount.AmountOf(ptypes.EdenB) commitments.AddClaimed(sdk.NewCoin(ptypes.Eden, edenAmount)) commitments.AddClaimed(sdk.NewCoin(ptypes.EdenB, edenBAmount)) k.SetCommitments(ctx, *commitments) + // Emit Hook commitment changed + k.AfterCommitmentChange(ctx, commitments.Creator, amount) + withdrawCoins := amount. Sub(sdk.NewCoin(ptypes.Eden, edenAmount)). Sub(sdk.NewCoin(ptypes.EdenB, edenBAmount)) - // Emit Hook commitment changed - k.AfterCommitmentChange(ctx, addr.String(), withdrawCoins) - - if sendCoins { + if sendCoins && !withdrawCoins.Empty() { return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addr, withdrawCoins) } return nil @@ -212,7 +212,7 @@ func (k Keeper) RecordWithdrawValidatorCommission(ctx sdk.Context, delegator str } commitments = k.GetCommitments(ctx, delegator) - err = k.HandleWithdrawFromCommitment(ctx, &commitments, addr, withdrawCoins, false) + err = k.HandleWithdrawFromCommitment(ctx, &commitments, withdrawCoins, false, addr) if err != nil { return err } diff --git a/x/commitment/keeper/msg_server_uncommit_tokens.go b/x/commitment/keeper/msg_server_uncommit_tokens.go index 43a51d53d..23405c590 100644 --- a/x/commitment/keeper/msg_server_uncommit_tokens.go +++ b/x/commitment/keeper/msg_server_uncommit_tokens.go @@ -41,7 +41,7 @@ func (k msgServer) UncommitTokens(goCtx context.Context, msg *types.MsgUncommitT return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "unable to convert address from bech32") } - err = k.HandleWithdrawFromCommitment(ctx, &commitments, addr, liquidCoins, true) + err = k.HandleWithdrawFromCommitment(ctx, &commitments, liquidCoins, true, addr) if err != nil { return nil, err } diff --git a/x/commitment/types/commitments.go b/x/commitment/types/commitments.go index 502830ea9..630787fe8 100644 --- a/x/commitment/types/commitments.go +++ b/x/commitment/types/commitments.go @@ -118,52 +118,27 @@ func (c *Commitments) DeductFromCommitted(denom string, amount math.Int, currTim } func (c *Commitments) GetRewardUnclaimedForDenom(denom string) math.Int { - for _, token := range c.RewardsUnclaimed { - if token.Denom == denom { - return token.Amount - } - } - return sdk.ZeroInt() + return c.RewardsUnclaimed.AmountOf(denom) } // Sub bucket rewards query - Elys func (c *Commitments) GetElysSubBucketRewardUnclaimedForDenom(denom string) math.Int { - for _, token := range c.RewardsByElysUnclaimed { - if token.Denom == denom { - return token.Amount - } - } - return sdk.ZeroInt() + return c.RewardsByElysUnclaimed.AmountOf(denom) } // Sub bucket rewards query - Eden func (c *Commitments) GetEdenSubBucketRewardUnclaimedForDenom(denom string) math.Int { - for _, token := range c.RewardsByEdenUnclaimed { - if token.Denom == denom { - return token.Amount - } - } - return sdk.ZeroInt() + return c.RewardsByEdenUnclaimed.AmountOf(denom) } // Sub bucket rewards query - EdenB func (c *Commitments) GetEdenBSubBucketRewardUnclaimedForDenom(denom string) math.Int { - for _, token := range c.RewardsByEdenbUnclaimed { - if token.Denom == denom { - return token.Amount - } - } - return sdk.ZeroInt() + return c.RewardsByEdenbUnclaimed.AmountOf(denom) } // Sub bucket rewards query - Usdc func (c *Commitments) GetUsdcSubBucketRewardUnclaimedForDenom(denom string) math.Int { - for _, token := range c.RewardsByUsdcUnclaimed { - if token.Denom == denom { - return token.Amount - } - } - return sdk.ZeroInt() + return c.RewardsByUsdcUnclaimed.AmountOf(denom) } func (c *Commitments) AddRewardsUnclaimed(amount sdk.Coin) { diff --git a/x/incentive/keeper/apr.go b/x/incentive/keeper/apr.go index cca868ff3..645ec8b90 100644 --- a/x/incentive/keeper/apr.go +++ b/x/incentive/keeper/apr.go @@ -154,7 +154,6 @@ func (k Keeper) CalculateApr(ctx sdk.Context, query *types.QueryAprRequest) (mat apr := dailyDexRewardAmount. MulInt(sdk.NewInt(ptypes.DaysPerYear)). MulInt(sdk.NewInt(100)). - MulInt(sdk.NewInt(1000000)). Quo(edenPrice). QuoInt(totalStakedSnapshot) diff --git a/x/incentive/keeper/keeper.go b/x/incentive/keeper/keeper.go index 2dbe776ff..da140ea80 100644 --- a/x/incentive/keeper/keeper.go +++ b/x/incentive/keeper/keeper.go @@ -150,7 +150,9 @@ func (k Keeper) UpdateStakersRewardsUnclaimed(ctx sdk.Context, stakeIncentive ty // Reset amount from other tracker params.DexRewardsStakers.AmountCollectedByOtherTracker = sdk.ZeroDec() // Don't increase Lps rewards blocks, it will be increased whenever LP distribution epoch happens. - params.DexRewardsLps.AmountCollectedByOtherTracker = params.DexRewardsLps.AmountCollectedByOtherTracker.Add(dexRevenueLPsAmtPerDistribution).Add(gasFeesLPsAmtPerDistribution) + params.DexRewardsLps.AmountCollectedByOtherTracker = params.DexRewardsLps.AmountCollectedByOtherTracker. + Add(dexRevenueLPsAmtPerDistribution). + Add(gasFeesLPsAmtPerDistribution) k.SetParams(ctx, params) totalEdenGiven := sdk.ZeroInt() diff --git a/x/incentive/keeper/keeper_withdraw.go b/x/incentive/keeper/keeper_withdraw.go index 8c24e783b..873cabd43 100644 --- a/x/incentive/keeper/keeper_withdraw.go +++ b/x/incentive/keeper/keeper_withdraw.go @@ -117,6 +117,9 @@ func (k Keeper) ProcessWithdrawRewards(ctx sdk.Context, delegator string, withdr unclaimed := k.CalcAmountSubbucketsPerProgram(ctx, delegator, ptypes.Eden, withdrawType, commitments) if !unclaimed.IsZero() { err = k.cmk.RecordClaimReward(ctx, delegator, ptypes.Eden, unclaimed, withdrawType) + if err != nil { + return err + } } // Claim EdenB @@ -124,6 +127,9 @@ func (k Keeper) ProcessWithdrawRewards(ctx sdk.Context, delegator string, withdr unclaimed = k.CalcAmountSubbucketsPerProgram(ctx, delegator, ptypes.EdenB, withdrawType, commitments) if !unclaimed.IsZero() { err = k.cmk.RecordClaimReward(ctx, delegator, ptypes.EdenB, unclaimed, withdrawType) + if err != nil { + return err + } } // Claim USDC @@ -134,7 +140,7 @@ func (k Keeper) ProcessWithdrawRewards(ctx sdk.Context, delegator string, withdr } baseCurrency := entry.Denom - // Get available usdc amount can be withdrew + // Get available usdc amount can be withdraw unclaimedUsdc := k.CalcAmountSubbucketsPerProgram(ctx, delegator, baseCurrency, withdrawType, commitments) if unclaimedUsdc.IsZero() { return nil @@ -190,12 +196,18 @@ func (k Keeper) RecordWithdrawValidatorCommission(ctx sdk.Context, delegator str unclaimed := commitments.GetRewardUnclaimedForDenom(ptypes.Eden) if !unclaimed.IsZero() { err = k.cmk.RecordWithdrawValidatorCommission(ctx, delegator, validator, ptypes.Eden, unclaimed) + if err != nil { + return err + } } // EdenB unclaimed = commitments.GetRewardUnclaimedForDenom(ptypes.EdenB) if !unclaimed.IsZero() { err = k.cmk.RecordWithdrawValidatorCommission(ctx, delegator, validator, ptypes.EdenB, unclaimed) + if err != nil { + return err + } } entry, found := k.assetProfileKeeper.GetEntry(ctx, ptypes.BaseCurrency) @@ -223,7 +235,7 @@ func (k Keeper) RecordWithdrawValidatorCommission(ctx sdk.Context, delegator str // This function call will deduct the accounting in commitment module only. err = k.cmk.RecordClaimReward(ctx, validator, baseCurrency, unclaimedUsdc, commitmenttypes.EarnType_ALL_PROGRAM) if err != nil { - return errorsmod.Wrapf(types.ErrIntOverflowTx, "Internal error with amount: %d", unclaimedUsdc) + return err } // Get Bech32 address for delegator @@ -235,10 +247,5 @@ func (k Keeper) RecordWithdrawValidatorCommission(ctx sdk.Context, delegator str // Set withdraw usdc amount revenue := sdk.NewCoin(baseCurrency, unclaimedUsdc) // Transfer revenue from a single wallet of DEX revenue wallet to user's wallet. - err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, k.dexRevCollectorName, addr, sdk.NewCoins(revenue)) - if err != nil { - panic(err) - } - - return err + return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, k.dexRevCollectorName, addr, sdk.NewCoins(revenue)) } diff --git a/x/incentive/keeper/keeper_withdraw_test.go b/x/incentive/keeper/keeper_withdraw_test.go index f7f6778f4..fc84e15c1 100644 --- a/x/incentive/keeper/keeper_withdraw_test.go +++ b/x/incentive/keeper/keeper_withdraw_test.go @@ -126,9 +126,9 @@ func TestRecordWithdrawValidatorCommission(t *testing.T) { app.CommitmentKeeper.BeforeDelegationCreated(ctx, delegator, valAddress.String()) // Set assetprofile entry for denom - app.AssetprofileKeeper.SetEntry(ctx, aptypes.Entry{BaseDenom: ptypes.BaseCurrency, CommitEnabled: false, WithdrawEnabled: true}) - app.AssetprofileKeeper.SetEntry(ctx, aptypes.Entry{BaseDenom: ptypes.Eden, CommitEnabled: true, WithdrawEnabled: true}) - app.AssetprofileKeeper.SetEntry(ctx, aptypes.Entry{BaseDenom: ptypes.EdenB, CommitEnabled: true, WithdrawEnabled: true}) + app.AssetprofileKeeper.SetEntry(ctx, aptypes.Entry{BaseDenom: ptypes.BaseCurrency, Denom: ptypes.BaseCurrency, CommitEnabled: false, WithdrawEnabled: true}) + app.AssetprofileKeeper.SetEntry(ctx, aptypes.Entry{BaseDenom: ptypes.Eden, Denom: ptypes.Eden, CommitEnabled: true, WithdrawEnabled: true}) + app.AssetprofileKeeper.SetEntry(ctx, aptypes.Entry{BaseDenom: ptypes.EdenB, Denom: ptypes.EdenB, CommitEnabled: true, WithdrawEnabled: true}) // Give commission to validators ( Eden from stakers and Dex rewards from stakers. ) edenCommissionGiven, dexRewardsCommissionGiven := ik.GiveCommissionToValidators(ctx, delegator, delegatedAmt, newUnclaimedEdenTokens, dexRewardsByStakers, ptypes.BaseCurrency) diff --git a/x/incentive/migrations/v10_migration.go b/x/incentive/migrations/v10_migration.go new file mode 100644 index 000000000..3a4928a12 --- /dev/null +++ b/x/incentive/migrations/v10_migration.go @@ -0,0 +1,18 @@ +package migrations + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (m Migrator) V10Migration(ctx sdk.Context) error { + params := m.keeper.GetParams(ctx) + params.DistributionInterval = 1200 + if params.LpIncentives != nil { + params.LpIncentives.DistributionEpochInBlocks = sdk.NewInt(params.DistributionInterval) + } + if params.StakeIncentives != nil { + params.StakeIncentives.DistributionEpochInBlocks = sdk.NewInt(params.DistributionInterval) + } + m.keeper.SetParams(ctx, params) + return nil +} diff --git a/x/incentive/module.go b/x/incentive/module.go index bac636cd4..79e263ce8 100644 --- a/x/incentive/module.go +++ b/x/incentive/module.go @@ -111,7 +111,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) types.RegisterQueryServer(cfg.QueryServer(), am.keeper) m := migrations.NewMigrator(am.keeper) - err := cfg.RegisterMigration(types.ModuleName, 8, m.V9Migration) + err := cfg.RegisterMigration(types.ModuleName, 9, m.V10Migration) if err != nil { panic(err) } @@ -137,7 +137,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1 -func (AppModule) ConsensusVersion() uint64 { return 9 } +func (AppModule) ConsensusVersion() uint64 { return 10 } // BeginBlock contains the logic that is automatically triggered at the beginning of each block func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}