Skip to content

Commit

Permalink
feat: epoch hook for CLP reward distribution
Browse files Browse the repository at this point in the history
  • Loading branch information
snobbee committed Nov 15, 2023
1 parent 1dde1f6 commit e19b32e
Show file tree
Hide file tree
Showing 19 changed files with 308 additions and 54 deletions.
40 changes: 22 additions & 18 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 .
Expand Down Expand Up @@ -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,
)
Expand All @@ -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),
Expand All @@ -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(
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion app/setup_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions testutil/keeper/clp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.2.0-beta
1.3.0-beta
4 changes: 2 additions & 2 deletions x/clp/client/rest/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
2 changes: 1 addition & 1 deletion x/clp/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
13 changes: 9 additions & 4 deletions x/clp/keeper/epoch_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,31 @@ 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)
if !found {
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)
}
Expand Down
54 changes: 54 additions & 0 deletions x/clp/keeper/epoch_hooks_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 1 addition & 1 deletion x/clp/keeper/executors.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions x/clp/keeper/executors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
2 changes: 1 addition & 1 deletion x/clp/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions x/clp/keeper/liquidityprovider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions x/clp/keeper/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
53 changes: 52 additions & 1 deletion x/clp/keeper/rewards_bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading

0 comments on commit e19b32e

Please sign in to comment.