From e19b32e9b648cba826aeddb3b3b9c3665eb95f7d Mon Sep 17 00:00:00 2001 From: Snobbish Bee <125891987+snobbee@users.noreply.github.com> Date: Wed, 15 Nov 2023 07:00:38 +0100 Subject: [PATCH] feat: epoch hook for CLP reward distribution --- app/app.go | 40 ++++---- app/setup_handlers.go | 2 +- testutil/keeper/clp.go | 3 + version | 2 +- x/clp/client/rest/query.go | 4 +- x/clp/genesis_test.go | 2 +- x/clp/keeper/epoch_hooks.go | 13 ++- x/clp/keeper/epoch_hooks_test.go | 54 ++++++++++ x/clp/keeper/executors.go | 2 +- x/clp/keeper/executors_test.go | 4 +- x/clp/keeper/keeper_test.go | 2 +- x/clp/keeper/liquidityprovider_test.go | 6 +- x/clp/keeper/querier_test.go | 4 +- x/clp/keeper/rewards_bucket.go | 53 +++++++++- x/clp/keeper/rewards_bucket_test.go | 132 +++++++++++++++++++++++++ x/clp/test/test_common.go | 18 ++-- x/clp/types/types.go | 9 +- x/clp/types/types_test.go | 6 +- x/dispensation/handler_test.go | 6 +- 19 files changed, 308 insertions(+), 54 deletions(-) create mode 100644 x/clp/keeper/epoch_hooks_test.go diff --git a/app/app.go b/app/app.go index 6e49309c37..c5bc737a9d 100644 --- a/app/app.go +++ b/app/app.go @@ -350,22 +350,22 @@ func NewSifAppWithBlacklist( appCodec, keys[feegrant.StoreKey], app.AccountKeeper, ) - stakingKeeper := stakingkeeper.NewKeeper( + app.StakingKeeper = stakingkeeper.NewKeeper( appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName), ) app.MintKeeper = mintkeeper.NewKeeper( - appCodec, keys[minttypes.StoreKey], app.GetSubspace(minttypes.ModuleName), &stakingKeeper, + appCodec, keys[minttypes.StoreKey], app.GetSubspace(minttypes.ModuleName), &app.StakingKeeper, app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, ) app.DistrKeeper = distrkeeper.NewKeeper( appCodec, keys[distrtypes.StoreKey], app.GetSubspace(distrtypes.ModuleName), app.AccountKeeper, app.BankKeeper, - &stakingKeeper, authtypes.FeeCollectorName, app.ModuleAccountAddrs(), + &app.StakingKeeper, authtypes.FeeCollectorName, app.ModuleAccountAddrs(), ) app.SlashingKeeper = slashingkeeper.NewKeeper( - appCodec, keys[slashingtypes.StoreKey], &stakingKeeper, app.GetSubspace(slashingtypes.ModuleName), + appCodec, keys[slashingtypes.StoreKey], &app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName), ) /* Invariants are certain conditions which are checked , which should hold true to certify the network is not byzantine . @@ -410,12 +410,6 @@ func NewSifAppWithBlacklist( app.GetSubspace(margintypes.ModuleName), ) - // register the staking hooks - // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks - app.StakingKeeper = *stakingKeeper.SetHooks( - stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), - ) - app.IBCKeeper = ibckeeper.NewKeeper( appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, app.UpgradeKeeper, scopedIBCKeeper, ) @@ -426,7 +420,7 @@ func NewSifAppWithBlacklist( AddRoute(distrtypes.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper)). AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)). AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)) - govKeeper := govkeeper.NewKeeper( + app.GovKeeper = govkeeper.NewKeeper( appCodec, keys[govtypes.StoreKey], app.GetSubspace(govtypes.ModuleName), @@ -435,11 +429,6 @@ func NewSifAppWithBlacklist( app.StakingKeeper, govRouter, ) - app.GovKeeper = *govKeeper.SetHooks( - govtypes.NewMultiGovHooks( - // register governance hooks - ), - ) // Create Transfer Keepers app.TransferKeeper = ibctransferkeeper.NewKeeper( @@ -509,10 +498,25 @@ func NewSifAppWithBlacklist( // register hooks after all modules have been initialized + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.StakingKeeper.SetHooks( + stakingtypes.NewMultiStakingHooks( + app.DistrKeeper.Hooks(), + app.SlashingKeeper.Hooks(), + ), + ) + + app.GovKeeper.SetHooks( + govtypes.NewMultiGovHooks( + // register governance hooks + ), + ) + app.EpochsKeeper = *app.EpochsKeeper.SetHooks( epochskeeper.NewMultiEpochHooks( - // insert epoch hooks receivers here - // app.CLPKeeper.Hooks(), + // insert epoch hooks receivers here + app.ClpKeeper.Hooks(), ), ) epochsModule := epochs.NewAppModule(appCodec, app.EpochsKeeper) diff --git a/app/setup_handlers.go b/app/setup_handlers.go index 1ba263cf8d..f17c7325d5 100644 --- a/app/setup_handlers.go +++ b/app/setup_handlers.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) -const releaseVersion = "1.2.0-beta" +const releaseVersion = "1.3.0-beta" func SetupHandlers(app *SifchainApp) { app.UpgradeKeeper.SetUpgradeHandler(releaseVersion, func(ctx sdk.Context, _ types.Plan, vm m.VersionMap) (m.VersionMap, error) { diff --git a/testutil/keeper/clp.go b/testutil/keeper/clp.go index f7a1e739e9..5cc9110946 100644 --- a/testutil/keeper/clp.go +++ b/testutil/keeper/clp.go @@ -6,6 +6,7 @@ import ( "github.com/Sifchain/sifnode/x/clp/keeper" "github.com/Sifchain/sifnode/x/clp/types" + "github.com/Sifchain/sifnode/app" "github.com/Sifchain/sifnode/x/clp/types/mocks" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -21,6 +22,8 @@ import ( ) func ClpKeeper(t testing.TB) (*keeper.Keeper, sdk.Context, *mocks.BankKeeper) { + app.SetConfig(false) + storeKey := sdk.NewKVStoreKey(types.StoreKey) memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) diff --git a/version b/version index a7999a6540..d74159f05d 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.2.0-beta +1.3.0-beta diff --git a/x/clp/client/rest/query.go b/x/clp/client/rest/query.go index 359c440f98..83e8913f14 100644 --- a/x/clp/client/rest/query.go +++ b/x/clp/client/rest/query.go @@ -97,11 +97,11 @@ func getLiquidityProviderHandler(cliCtx client.Context) http.HandlerFunc { var params types.LiquidityProviderReq params.Symbol = r.URL.Query().Get("symbol") addressString := r.URL.Query().Get("lpAddress") - lpAddess, err := sdk.AccAddressFromBech32(addressString) + lpAddress, err := sdk.AccAddressFromBech32(addressString) if err != nil { return } - params.LpAddress = lpAddess.String() + params.LpAddress = lpAddress.String() bz, err := cliCtx.LegacyAmino.MarshalJSON(params) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) diff --git a/x/clp/genesis_test.go b/x/clp/genesis_test.go index 6443a1eb47..66e3456220 100644 --- a/x/clp/genesis_test.go +++ b/x/clp/genesis_test.go @@ -82,7 +82,7 @@ func CreateState(ctx sdk.Context, keeper keeper.Keeper, t *testing.T) (int, int) lpList := test.GenerateRandomLP(10) for _, lp := range lpList { lp := lp - keeper.SetLiquidityProvider(ctx, &lp) + keeper.SetLiquidityProvider(ctx, lp) } v1 := test.GenerateWhitelistAddress("") keeper.SetClpWhiteList(ctx, []sdk.AccAddress{v1}) diff --git a/x/clp/keeper/epoch_hooks.go b/x/clp/keeper/epoch_hooks.go index 28e80fc918..cc2b475c39 100644 --- a/x/clp/keeper/epoch_hooks.go +++ b/x/clp/keeper/epoch_hooks.go @@ -8,16 +8,18 @@ import ( // BeforeEpochStart performs a no-op func (k Keeper) BeforeEpochStart(_ sdk.Context, _ string, _ int64) {} -// AfterEpochEnd distributes available rewards from rewards bucket to liquidity pools +// AfterEpochEnd distributes available rewards from rewards bucket to liquidity providers func (k Keeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, _ int64) { if !k.ShouldDistributeRewards(ctx, epochIdentifier) { return } + rewardsEligibleLps, err := k.GetRewardsEligibleLiquidityProviders(ctx) if err != nil { ctx.Logger().Error("unable to get rewards eligible liquidity providers", "error", err) return } + for asset, assetLps := range rewardsEligibleLps { // get reward bucket for given asset rewardsBucket, found := k.GetRewardsBucket(ctx, asset.Symbol) @@ -25,9 +27,12 @@ func (k Keeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, _ int64) ctx.Logger().Error("unable to get rewards bucket", "asset", asset.Symbol) continue } - _ = rewardsBucket - for _, lp := range assetLps { - err := k.DistributeLiquidityProviderRewards(ctx, *lp) + + rewardShares := k.CalculateRewardShareForLiquidityProviders(ctx, assetLps) + rewardAmounts := k.CalculateRewardAmountForLiquidityProviders(ctx, rewardShares, rewardsBucket.Amount) + + for i, lp := range assetLps { + err := k.DistributeLiquidityProviderRewards(ctx, lp, asset.Symbol, rewardAmounts[i]) if err != nil { ctx.Logger().Error("unable to distribute liquidity provider rewards", "error", err) } diff --git a/x/clp/keeper/epoch_hooks_test.go b/x/clp/keeper/epoch_hooks_test.go new file mode 100644 index 0000000000..6a6f363895 --- /dev/null +++ b/x/clp/keeper/epoch_hooks_test.go @@ -0,0 +1,54 @@ +package keeper_test + +import ( + "testing" + + "github.com/Sifchain/sifnode/x/clp/test" + "github.com/Sifchain/sifnode/x/clp/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +// AfterEpochEnd +func TestAfterEpochEnd(t *testing.T) { + ctx, app := test.CreateTestAppClp(false) + clpKeeper := app.ClpKeeper + + asset := types.NewAsset("cusdc") + + // Create a RewardsBucket with a denom and amount + initialAmount := sdk.NewInt(100) + rewardsBucket := types.RewardsBucket{ + Denom: asset.Symbol, + Amount: initialAmount, + } + clpKeeper.SetRewardsBucket(ctx, rewardsBucket) + + // mint coins to the module account + err := app.BankKeeper.MintCoins(ctx, types.ModuleName, sdk.NewCoins( + sdk.NewCoin(asset.Symbol, initialAmount), + )) + require.NoError(t, err) + + // check module balance + moduleBalance := app.BankKeeper.GetBalance(ctx, types.GetCLPModuleAddress(), asset.Symbol) + require.Equal(t, initialAmount, moduleBalance.Amount) + + params := clpKeeper.GetRewardsParams(ctx) + + // Create a liquidity provider + lpAddress, err := sdk.AccAddressFromBech32("sif1azpar20ck9lpys89r8x7zc8yu0qzgvtp48ng5v") + require.NoError(t, err) + lp := types.NewLiquidityProvider(&asset, sdk.NewUint(1), lpAddress, ctx.BlockHeight()-int64(params.RewardsLockPeriod)-1) + clpKeeper.SetLiquidityProvider(ctx, &lp) + + // check account balance + preBalance := app.BankKeeper.GetBalance(ctx, lpAddress, asset.Symbol) + require.Equal(t, sdk.ZeroInt(), preBalance.Amount) + + clpKeeper.AfterEpochEnd(ctx, params.RewardsEpochIdentifier, 1) + + // check account balance + postBalance := app.BankKeeper.GetBalance(ctx, lpAddress, asset.Symbol) + require.Equal(t, preBalance.Add(sdk.NewCoin(asset.Symbol, initialAmount)), postBalance) +} diff --git a/x/clp/keeper/executors.go b/x/clp/keeper/executors.go index 538f236573..360d692e1e 100644 --- a/x/clp/keeper/executors.go +++ b/x/clp/keeper/executors.go @@ -52,7 +52,7 @@ func (k Keeper) CreatePool(ctx sdk.Context, poolUints sdk.Uint, msg *types.MsgCr } func (k Keeper) CreateLiquidityProvider(ctx sdk.Context, asset *types.Asset, lpunits sdk.Uint, lpaddress sdk.AccAddress) types.LiquidityProvider { - lp := types.NewLiquidityProvider(asset, lpunits, lpaddress) + lp := types.NewLiquidityProvider(asset, lpunits, lpaddress, ctx.BlockHeight()) k.SetLiquidityProvider(ctx, &lp) return lp diff --git a/x/clp/keeper/executors_test.go b/x/clp/keeper/executors_test.go index ded7fa27d8..a6bd7466f0 100644 --- a/x/clp/keeper/executors_test.go +++ b/x/clp/keeper/executors_test.go @@ -158,9 +158,9 @@ func TestKeeper_CreatePool_And_AddLiquidity_RemoveLiquidity(t *testing.T) { func TestKeeper_CreateLiquidityProvider(t *testing.T) { ctx, app := test.CreateTestAppClp(false) asset := types.NewAsset("eth") - lpAddess, err := sdk.AccAddressFromBech32("sif1azpar20ck9lpys89r8x7zc8yu0qzgvtp48ng5v") + lpAddress, err := sdk.AccAddressFromBech32("sif1azpar20ck9lpys89r8x7zc8yu0qzgvtp48ng5v") require.NoError(t, err, "Error Creating Liquidity Provider :", err) - lp := app.ClpKeeper.CreateLiquidityProvider(ctx, &asset, sdk.NewUint(1), lpAddess) + lp := app.ClpKeeper.CreateLiquidityProvider(ctx, &asset, sdk.NewUint(1), lpAddress) assert.NoError(t, err) assert.Equal(t, lp.LiquidityProviderAddress, "sif1azpar20ck9lpys89r8x7zc8yu0qzgvtp48ng5v") } diff --git a/x/clp/keeper/keeper_test.go b/x/clp/keeper/keeper_test.go index c870abd9cb..765699d332 100644 --- a/x/clp/keeper/keeper_test.go +++ b/x/clp/keeper/keeper_test.go @@ -35,7 +35,7 @@ func TestKeeper_Errors(t *testing.T) { assert.Equal(t, len(getpools), 0, "No pool added") lp := test.GenerateRandomLP(1)[0] lp.Asset.Symbol = "" - clpKeeper.SetLiquidityProvider(ctx, &lp) + clpKeeper.SetLiquidityProvider(ctx, lp) getlp, err := clpKeeper.GetLiquidityProvider(ctx, lp.Asset.Symbol, lp.LiquidityProviderAddress) assert.Error(t, err) assert.NotEqual(t, getlp, lp) diff --git a/x/clp/keeper/liquidityprovider_test.go b/x/clp/keeper/liquidityprovider_test.go index e1a31cf412..f1d351bd40 100644 --- a/x/clp/keeper/liquidityprovider_test.go +++ b/x/clp/keeper/liquidityprovider_test.go @@ -19,7 +19,7 @@ func TestKeeper_SetLiquidityProvider(t *testing.T) { lp := test.GenerateRandomLP(1)[0] ctx, app := test.CreateTestAppClp(false) clpKeeper := app.ClpKeeper - clpKeeper.SetLiquidityProvider(ctx, &lp) + clpKeeper.SetLiquidityProvider(ctx, lp) getlp, err := clpKeeper.GetLiquidityProvider(ctx, lp.Asset.Symbol, lp.LiquidityProviderAddress) assert.NoError(t, err, "Error in get liquidityProvider") assert.Equal(t, getlp, lp) @@ -32,7 +32,7 @@ func TestKeeper_DestroyLiquidityProvider(t *testing.T) { lp := test.GenerateRandomLP(1)[0] ctx, app := test.CreateTestAppClp(false) clpKeeper := app.ClpKeeper - clpKeeper.SetLiquidityProvider(ctx, &lp) + clpKeeper.SetLiquidityProvider(ctx, lp) getlp, err := clpKeeper.GetLiquidityProvider(ctx, lp.Asset.Symbol, lp.LiquidityProviderAddress) assert.NoError(t, err, "Error in get liquidityProvider") assert.Equal(t, getlp, lp) @@ -51,7 +51,7 @@ func TestKeeper_GetAssetsForLiquidityProvider(t *testing.T) { lpList := test.GenerateRandomLP(10) for i := range lpList { lp := lpList[i] - clpKeeper.SetLiquidityProvider(ctx, &lp) + clpKeeper.SetLiquidityProvider(ctx, lp) } lpaddr, err := sdk.AccAddressFromBech32(lpList[0].LiquidityProviderAddress) require.NoError(t, err) diff --git a/x/clp/keeper/querier_test.go b/x/clp/keeper/querier_test.go index a3a2bb2def..bd2723dd48 100644 --- a/x/clp/keeper/querier_test.go +++ b/x/clp/keeper/querier_test.go @@ -357,6 +357,6 @@ func SetData(keeper clpkeeper.Keeper, ctx sdk.Context) (types.Pool, []types.Pool } } lp := test.GenerateRandomLP(1)[0] - keeper.SetLiquidityProvider(ctx, &lp) - return pool, pools, lp + keeper.SetLiquidityProvider(ctx, lp) + return pool, pools, *lp } diff --git a/x/clp/keeper/rewards_bucket.go b/x/clp/keeper/rewards_bucket.go index 8bbad2b533..69fc534cd5 100644 --- a/x/clp/keeper/rewards_bucket.go +++ b/x/clp/keeper/rewards_bucket.go @@ -136,6 +136,57 @@ func (k Keeper) ShouldDistributeRewards(ctx sdk.Context, epochIdentifier string) } // DistributeLiquidityProviderRewards distributes rewards to a liquidity provider -func (k Keeper) DistributeLiquidityProviderRewards(ctx sdk.Context, lp types.LiquidityProvider) error { +func (k Keeper) DistributeLiquidityProviderRewards(ctx sdk.Context, lp *types.LiquidityProvider, asset string, rewardAmount sdk.Int) error { + // get the liquidity provider address + lpAddress, err := sdk.AccAddressFromBech32(lp.LiquidityProviderAddress) + if err != nil { + return err + } + + // distribute rewards to the liquidity provider + err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, lpAddress, sdk.NewCoins(sdk.NewCoin(asset, rewardAmount))) + if err != nil { + return err + } + + // substract the reward amount from the rewards bucket + err = k.SubtractFromRewardsBucket(ctx, asset, rewardAmount) + if err != nil { + return err + } + return nil } + +// calculate the reward share for each liquidity provider +func (k Keeper) CalculateRewardShareForLiquidityProviders( + ctx sdk.Context, + lps []*types.LiquidityProvider, +) []sdk.Dec { + // sum up the liquidity provider total units + totalUnits := sdk.ZeroInt() + for _, lp := range lps { + totalUnits = totalUnits.Add(sdk.NewIntFromBigInt(lp.LiquidityProviderUnits.BigInt())) + } + + // create a list of the reward share based on lp units and totalUnits + rewardShares := make([]sdk.Dec, len(lps)) + for i, lp := range lps { + rewardShares[i] = sdk.NewDecFromInt(sdk.NewIntFromBigInt(lp.LiquidityProviderUnits.BigInt())).Quo(sdk.NewDecFromInt(totalUnits)) + } + + return rewardShares +} + +// CalculateRewardAmountForLiquidityProviders calculates the reward amount for each liquidity provider +func (k Keeper) CalculateRewardAmountForLiquidityProviders( + ctx sdk.Context, + rewardShares []sdk.Dec, + rewardsBucketAmount sdk.Int, +) []sdk.Int { + rewardAmounts := make([]sdk.Int, len(rewardShares)) + for i, rewardShare := range rewardShares { + rewardAmounts[i] = rewardShare.MulInt(rewardsBucketAmount).TruncateInt() + } + return rewardAmounts +} diff --git a/x/clp/keeper/rewards_bucket_test.go b/x/clp/keeper/rewards_bucket_test.go index c7915096b6..3854a2bb7c 100644 --- a/x/clp/keeper/rewards_bucket_test.go +++ b/x/clp/keeper/rewards_bucket_test.go @@ -8,6 +8,7 @@ import ( keepertest "github.com/Sifchain/sifnode/testutil/keeper" "github.com/Sifchain/sifnode/testutil/nullify" "github.com/Sifchain/sifnode/x/clp/keeper" + "github.com/Sifchain/sifnode/x/clp/test" "github.com/Sifchain/sifnode/x/clp/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" @@ -173,3 +174,134 @@ func TestSubtractFromRewardsBucket_Errors(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), fmt.Errorf(types.ErrNotEnoughBalanceInRewardsBucket.Error(), "atom").Error()) } + +// ShouldDistributeRewards returns true if the epoch identifier is not in the list of already distributed epochs +func TestShouldDistributeRewards(t *testing.T) { + keeper, ctx, _ := keepertest.ClpKeeper(t) + + params := keeper.GetRewardsParams(ctx) + + // Check if the rewards epoch should be distributed + require.True(t, keeper.ShouldDistributeRewards(ctx, params.RewardsEpochIdentifier)) + + // Check if the rewards epoch should be distributed with a wrong epoch identifier + require.False(t, keeper.ShouldDistributeRewards(ctx, "wrong_epoch_identifier")) +} + +// DistributeLiquidityProviderRewards distributes rewards to a liquidity provider +func TestDistributeLiquidityProviderRewards(t *testing.T) { + ctx, app := test.CreateTestAppClp(false) + clpKeeper := app.ClpKeeper + + asset := types.NewAsset("cusdc") + + // Create a liquidity provider + lpAddress, err := sdk.AccAddressFromBech32("sif1azpar20ck9lpys89r8x7zc8yu0qzgvtp48ng5v") + require.NoError(t, err) + lp := types.NewLiquidityProvider(&asset, sdk.NewUint(1), lpAddress, ctx.BlockHeight()) + clpKeeper.SetLiquidityProvider(ctx, &lp) + + // Create a RewardsBucket with a denom and amount + initialAmount := sdk.NewInt(100) + rewardsBucket := types.RewardsBucket{ + Denom: asset.Symbol, + Amount: initialAmount, + } + clpKeeper.SetRewardsBucket(ctx, rewardsBucket) + + // mint coins to the module account + err = app.BankKeeper.MintCoins(ctx, types.ModuleName, sdk.NewCoins( + sdk.NewCoin(asset.Symbol, initialAmount), + )) + require.NoError(t, err) + + // check module balance + moduleBalance := app.BankKeeper.GetBalance(ctx, types.GetCLPModuleAddress(), asset.Symbol) + require.Equal(t, initialAmount, moduleBalance.Amount) + + // check account balance + preBalance := app.BankKeeper.GetBalance(ctx, lpAddress, asset.Symbol) + require.Equal(t, sdk.ZeroInt(), preBalance.Amount) + + // Distribute rewards to the liquidity provider + err = clpKeeper.DistributeLiquidityProviderRewards(ctx, &lp, asset.Symbol, initialAmount) + require.NoError(t, err) + + // check account balance + postBalance := app.BankKeeper.GetBalance(ctx, lpAddress, asset.Symbol) + require.Equal(t, preBalance.Amount.Add(initialAmount), postBalance.Amount) + + // Check distributed rewards got subtracted from the rewards bucket + storedRewardsBucket, found := clpKeeper.GetRewardsBucket(ctx, asset.Symbol) + require.True(t, found) + require.Equal(t, sdk.ZeroInt(), storedRewardsBucket.Amount) +} + +// CalculateRewardShareForLiquidityProviders calculates the reward share for each liquidity provider +func TestCalculateRewardShareForLiquidityProviders(t *testing.T) { + ctx, app := test.CreateTestAppClp(false) + clpKeeper := app.ClpKeeper + + // Create a liquidity provider + lps := test.GenerateRandomLP(10) + for _, lp := range lps { + clpKeeper.SetLiquidityProvider(ctx, lp) + } + + // Calculate reward share for liquidity providers + rewardShares := clpKeeper.CalculateRewardShareForLiquidityProviders(ctx, lps) + + // Check if the rewards amount is correct + require.Equal(t, []sdk.Dec{ + sdk.MustNewDecFromStr("0.1"), + sdk.MustNewDecFromStr("0.1"), + sdk.MustNewDecFromStr("0.1"), + sdk.MustNewDecFromStr("0.1"), + sdk.MustNewDecFromStr("0.1"), + sdk.MustNewDecFromStr("0.1"), + sdk.MustNewDecFromStr("0.1"), + sdk.MustNewDecFromStr("0.1"), + sdk.MustNewDecFromStr("0.1"), + sdk.MustNewDecFromStr("0.1"), + }, rewardShares) +} + +// CalculateRewardAmountForLiquidityProviders calculates the reward amount for each liquidity provider +func TestCalculateRewardAmountForLiquidityProviders(t *testing.T) { + ctx, app := test.CreateTestAppClp(false) + clpKeeper := app.ClpKeeper + + // Create a RewardsBucket with a denom and amount + initialAmount := sdk.NewInt(100) + rewardsBucket := types.RewardsBucket{ + Denom: "atom", + Amount: initialAmount, + } + clpKeeper.SetRewardsBucket(ctx, rewardsBucket) + + // Create a liquidity provider + lps := test.GenerateRandomLP(10) + for _, lp := range lps { + clpKeeper.SetLiquidityProvider(ctx, lp) + } + + // Calculate reward share for liquidity providers + rewardShares := clpKeeper.CalculateRewardShareForLiquidityProviders(ctx, lps) + + // Calculate reward amount for liquidity providers + rewardAmounts := clpKeeper.CalculateRewardAmountForLiquidityProviders(ctx, rewardShares, rewardsBucket.Amount) + + // Check if the rewards amount is correct + require.Equal(t, []sdk.Int{ + sdk.NewInt(10), + sdk.NewInt(10), + sdk.NewInt(10), + sdk.NewInt(10), + sdk.NewInt(10), + sdk.NewInt(10), + sdk.NewInt(10), + sdk.NewInt(10), + sdk.NewInt(10), + sdk.NewInt(10), + }, rewardAmounts) +} diff --git a/x/clp/test/test_common.go b/x/clp/test/test_common.go index 11cb25dbd2..c7701b7512 100644 --- a/x/clp/test/test_common.go +++ b/x/clp/test/test_common.go @@ -135,7 +135,7 @@ func GenerateRandomLPWithUnitsAndAsset(poolUnitss []uint64, asset types.Asset) [ lpList := make([]*types.LiquidityProvider, len(poolUnitss)) for i, poolUnits := range poolUnitss { address := GenerateAddress2(fmt.Sprintf("%d%d%d%d", i, i, i, i)) - lp := types.NewLiquidityProvider(&asset, sdk.NewUint(poolUnits), address) + lp := types.NewLiquidityProvider(&asset, sdk.NewUint(poolUnits), address, 0) lpList[i] = &lp } @@ -152,7 +152,7 @@ func GenerateRandomLPWithUnits(poolUnitss []uint64) []*types.LiquidityProvider { externalToken := tokens[rand.Intn(len(tokens))] asset := types.NewAsset(TrimFirstRune(externalToken)) address := GenerateAddress(fmt.Sprintf("%d", i)) - lp := types.NewLiquidityProvider(&asset, sdk.NewUint(poolUnits), address) + lp := types.NewLiquidityProvider(&asset, sdk.NewUint(poolUnits), address, 0) lpList[i] = &lp } @@ -214,19 +214,19 @@ func GeneratePoolsSetLPs(keeper clpkeeper.Keeper, ctx sdk.Context, nPools, nLPs return poolList } -func GenerateRandomLP(numberOfLp int) []types.LiquidityProvider { - var lpList []types.LiquidityProvider +func GenerateRandomLP(numberOfLp int) []*types.LiquidityProvider { + var lpList []*types.LiquidityProvider tokens := []string{"ceth", "cbtc", "ceos", "cbch", "cbnb", "cusdt", "cada", "ctrx"} rand.Seed(time.Now().Unix()) for i := 0; i < numberOfLp; i++ { externalToken := tokens[rand.Intn(len(tokens))] asset := types.NewAsset(TrimFirstRune(externalToken)) - lpAddess, err := sdk.AccAddressFromBech32("sif1azpar20ck9lpys89r8x7zc8yu0qzgvtp48ng5v") + lpAddress, err := sdk.AccAddressFromBech32("sif1azpar20ck9lpys89r8x7zc8yu0qzgvtp48ng5v") if err != nil { panic(err) } - lp := types.NewLiquidityProvider(&asset, sdk.NewUint(1), lpAddess) - lpList = append(lpList, lp) + lp := types.NewLiquidityProvider(&asset, sdk.NewUint(1), lpAddress, 0) + lpList = append(lpList, &lp) } return lpList } @@ -243,11 +243,11 @@ func GeneratePoolsAndLPs(keeper clpkeeper.Keeper, ctx sdk.Context, tokens []stri panic(err) } poolList = append(poolList, pool) - lpAddess, err := sdk.AccAddressFromBech32("sif1azpar20ck9lpys89r8x7zc8yu0qzgvtp48ng5v") + lpAddress, err := sdk.AccAddressFromBech32("sif1azpar20ck9lpys89r8x7zc8yu0qzgvtp48ng5v") if err != nil { panic(err) } - lp := types.NewLiquidityProvider(&externalAsset, sdk.NewUint(1), lpAddess) + lp := types.NewLiquidityProvider(&externalAsset, sdk.NewUint(1), lpAddress, 0) keeper.SetLiquidityProvider(ctx, &lp) lpList = append(lpList, lp) } diff --git a/x/clp/types/types.go b/x/clp/types/types.go index 48663dee92..b8ca35e639 100755 --- a/x/clp/types/types.go +++ b/x/clp/types/types.go @@ -60,8 +60,13 @@ func (l LiquidityProvider) Validate() bool { } // NewLiquidityProvider returns a new LiquidityProvider -func NewLiquidityProvider(asset *Asset, liquidityProviderUnits sdk.Uint, liquidityProviderAddress sdk.AccAddress) LiquidityProvider { - return LiquidityProvider{Asset: asset, LiquidityProviderUnits: liquidityProviderUnits, LiquidityProviderAddress: liquidityProviderAddress.String()} +func NewLiquidityProvider(asset *Asset, liquidityProviderUnits sdk.Uint, liquidityProviderAddress sdk.AccAddress, lastUpdatedBlock int64) LiquidityProvider { + return LiquidityProvider{ + Asset: asset, + LiquidityProviderUnits: liquidityProviderUnits, + LiquidityProviderAddress: liquidityProviderAddress.String(), + LastUpdatedBlock: lastUpdatedBlock, + } } // ---------------------------------------------------------------------------- diff --git a/x/clp/types/types_test.go b/x/clp/types/types_test.go index 39044afc80..5d3aa7c52c 100644 --- a/x/clp/types/types_test.go +++ b/x/clp/types/types_test.go @@ -40,14 +40,14 @@ func Test_NewLiquidityProvider(t *testing.T) { newAsset := NewAsset("eth") address, _ := sdk.AccAddressFromBech32("A58856F0FD53BF058B4909A21AEC019107BA6") liquidityProviderUnits := sdk.NewUint(1) - liquidityProvider := NewLiquidityProvider(&newAsset, liquidityProviderUnits, address) + liquidityProvider := NewLiquidityProvider(&newAsset, liquidityProviderUnits, address, 0) assert.Equal(t, liquidityProvider.Asset, &newAsset) assert.Equal(t, liquidityProvider.LiquidityProviderAddress, "") assert.Equal(t, liquidityProvider.LiquidityProviderUnits, liquidityProviderUnits) boolean := liquidityProvider.Validate() assert.True(t, boolean) wrongAsset := NewAsset("") - liquidityProvider = NewLiquidityProvider(&wrongAsset, liquidityProviderUnits, address) + liquidityProvider = NewLiquidityProvider(&wrongAsset, liquidityProviderUnits, address, 0) boolean = liquidityProvider.Validate() assert.False(t, boolean) } @@ -58,7 +58,7 @@ func Test_NewLiquidityProviderResponse(t *testing.T) { liquidityProviderUnits := sdk.NewUint(1) nativeAssetAmount := sdk.NewUintFromString("998") externalAssetAmount := sdk.NewUintFromString("998") - liquidityProvider := NewLiquidityProvider(&newAsset, liquidityProviderUnits, address) + liquidityProvider := NewLiquidityProvider(&newAsset, liquidityProviderUnits, address, 0) liquidityProviderResponse := NewLiquidityProviderResponse(liquidityProvider, int64(10), nativeAssetAmount.String(), externalAssetAmount.String()) assert.Equal(t, liquidityProviderResponse.ExternalAssetBalance, externalAssetAmount.String()) assert.Equal(t, liquidityProviderResponse.NativeAssetBalance, nativeAssetAmount.String()) diff --git a/x/dispensation/handler_test.go b/x/dispensation/handler_test.go index 0e88c29f4c..97026948d6 100644 --- a/x/dispensation/handler_test.go +++ b/x/dispensation/handler_test.go @@ -111,9 +111,9 @@ func TestNewHandler_CreateDistribution_PayRewardsInAnyToken_HappyCase(t *testing msgRun := types.NewMsgRunDistribution(runner.String(), distributionName, types.DistributionType_DISTRIBUTION_TYPE_AIRDROP, 10) res, err = handler(ctx, &msgRun) for i := 0; i < len(outputList); i++ { - lpAddess, _ := sdk.AccAddressFromBech32(outputList[i].Address) - assert.True(t, keeper.GetBankKeeper().GetBalance(ctx, lpAddess, "ceth").Amount.Equal(sdk.NewInt(10)) || - keeper.GetBankKeeper().GetBalance(ctx, lpAddess, "catk").Amount.Equal(sdk.NewInt(10)) || keeper.GetBankKeeper().GetBalance(ctx, lpAddess, "rowan").Amount.Equal(sdk.NewInt(10))) + lpAddress, _ := sdk.AccAddressFromBech32(outputList[i].Address) + assert.True(t, keeper.GetBankKeeper().GetBalance(ctx, lpAddress, "ceth").Amount.Equal(sdk.NewInt(10)) || + keeper.GetBankKeeper().GetBalance(ctx, lpAddress, "catk").Amount.Equal(sdk.NewInt(10)) || keeper.GetBankKeeper().GetBalance(ctx, lpAddress, "rowan").Amount.Equal(sdk.NewInt(10))) } require.NoError(t, err)