Skip to content

Commit

Permalink
eden boost cut issue fix on redelegation (#383)
Browse files Browse the repository at this point in the history
Co-authored-by: Cosmic Vagabond <121588426+cosmic-vagabond@users.noreply.github.com>
  • Loading branch information
jelysn and cosmic-vagabond authored Feb 22, 2024
1 parent b5745b8 commit 9256717
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 67 deletions.
2 changes: 1 addition & 1 deletion app/test_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ func GenesisStateWithValSet(app *ElysApp) (GenesisState, *tmtypes.ValidatorSet,
}
validators = append(validators, validator)
delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec()))

}

// set validators and delegations
params := stakingtypes.DefaultParams()
params.BondDenom = ptypes.Elys
Expand Down
54 changes: 21 additions & 33 deletions x/incentive/keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,43 @@ import (
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"

ctypes "github.com/elys-network/elys/x/commitment/types"
"github.com/elys-network/elys/x/incentive/types"
ptypes "github.com/elys-network/elys/x/parameter/types"
)

// EndBlocker of incentive module
func (k Keeper) EndBlocker(ctx sdk.Context) {
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker)

// Elys staked amount tracking
k.ProcessElysStakedTracking(ctx)
// Burn EdenB tokens if staking changed
k.BurnEdenBIfElysStakingReduced(ctx)

// Rewards distribution
k.ProcessRewardsDistribution(ctx)
}

// Elys staked amount tracking
func (k Keeper) ProcessElysStakedTracking(ctx sdk.Context) {
params := k.GetParams(ctx)
// Update Elys staked amount every n blocks
if params.ElysStakeSnapInterval == 0 || ctx.BlockHeight()%params.ElysStakeSnapInterval != 0 {
return
}

// Track the amount of Elys staked
k.cmk.IterateCommitments(
ctx, func(commitments ctypes.Commitments) bool {
// Commitment owner
creator := commitments.Creator
_, err := sdk.AccAddressFromBech32(creator)
if err != nil {
// This could be validator address
return false
}
func (k Keeper) TakeDelegationSnapshot(ctx sdk.Context, addr string) {
// Calculate delegated amount per delegator
delegatedAmt := k.CalculateDelegatedAmount(ctx, addr)

// Calculate delegated amount per delegator
delegatedAmt := k.CalculateDelegatedAmount(ctx, creator)
elysStaked := types.ElysStaked{
Address: addr,
Amount: delegatedAmt,
}

elysStaked := types.ElysStaked{
Address: creator,
Amount: delegatedAmt,
}
// Set Elys staked amount
k.SetElysStaked(ctx, elysStaked)
}

// Set Elys staked amount
k.SetElysStaked(ctx, elysStaked)
func (k Keeper) BurnEdenBIfElysStakingReduced(ctx sdk.Context) {
addrs := k.GetAllElysStakeChange(ctx)

return false
},
)
// Handle addresses recorded on AfterDelegationModified
// This hook is exposed for genesis delegations as well
for _, delAddr := range addrs {
k.BurnEdenBFromElysUnstaking(ctx, delAddr)
k.TakeDelegationSnapshot(ctx, delAddr.String())
k.RemoveElysStakeChange(ctx, delAddr)
}
}

// Rewards distribution
Expand Down
15 changes: 7 additions & 8 deletions x/incentive/keeper/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,11 @@ func TestABCI_EndBlocker(t *testing.T) {

// Add testing commitment
simapp.AddTestCommitment(app, ctx, genAccount, committed, unclaimed)
// Update Elys staked amount
ik.EndBlocker(ctx)

// Get elys staked
elysStaked, found := ik.GetElysStaked(ctx, genAccount.String())
require.Equal(t, found, true)
require.Equal(t, elysStaked.Amount, sdk.DefaultPowerReduction)
elysStaked := ik.GetElysStaked(ctx, genAccount.String())
require.Equal(t, elysStaked, sdk.DefaultPowerReduction)

authority := authtypes.NewModuleAddress(govtypes.ModuleName).String()

Expand Down Expand Up @@ -101,27 +99,28 @@ func TestABCI_EndBlocker(t *testing.T) {
ctx = ctx.WithBlockHeight(6307210)

// Incentive param should be empty
stakerEpoch, stakeIncentive := ik.IsStakerRewardsDistributionEpoch(ctx)
stakerEpoch, _ := ik.IsStakerRewardsDistributionEpoch(ctx)
params = ik.GetParams(ctx)
require.Equal(t, stakerEpoch, false)
require.Nil(t, params.StakeIncentives)

// Incentive param should be empty
lpEpoch, lpIncentive := ik.IsLPRewardsDistributionEpoch(ctx)
lpEpoch, _ := ik.IsLPRewardsDistributionEpoch(ctx)
params = ik.GetParams(ctx)
require.Equal(t, lpEpoch, false)
require.Nil(t, params.LpIncentives)

// After reading tokenomics again
paramSet = ik.ProcessUpdateIncentiveParams(ctx)
require.Equal(t, paramSet, true)

// Check params
stakerEpoch, stakeIncentive = ik.IsStakerRewardsDistributionEpoch(ctx)
_, stakeIncentive := ik.IsStakerRewardsDistributionEpoch(ctx)
params = ik.GetParams(ctx)
require.Equal(t, stakeIncentive.EdenAmountPerYear, sdk.NewInt(int64(listTimeBasdInflations[0].Inflation.IcsStakingRewards)))

// Check params
lpEpoch, lpIncentive = ik.IsLPRewardsDistributionEpoch(ctx)
_, lpIncentive := ik.IsLPRewardsDistributionEpoch(ctx)
params = ik.GetParams(ctx)
require.Equal(t, lpIncentive.EdenAmountPerYear, sdk.NewInt(int64(listTimeBasdInflations[0].Inflation.IcsStakingRewards)))
}
36 changes: 36 additions & 0 deletions x/incentive/keeper/elys_stake_change.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package keeper

import (
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/elys-network/elys/x/incentive/types"
)

func (k Keeper) SetElysStakeChange(ctx sdk.Context, addr sdk.AccAddress) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.ElysStakeChangeKeyPrefix))
store.Set([]byte(addr), addr)
}

func (k Keeper) GetElysStakeChange(ctx sdk.Context, addr sdk.AccAddress) (found bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.ElysStakeChangeKeyPrefix))

return store.Has([]byte(addr))
}

func (k Keeper) RemoveElysStakeChange(ctx sdk.Context, address sdk.AccAddress) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.ElysStakeChangeKeyPrefix))
store.Delete([]byte(address))
}

func (k Keeper) GetAllElysStakeChange(ctx sdk.Context) (list []sdk.AccAddress) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.ElysStakeChangeKeyPrefix))
iterator := sdk.KVStorePrefixIterator(store, []byte{})

defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
list = append(list, sdk.AccAddress(iterator.Value()))
}

return
}
8 changes: 5 additions & 3 deletions x/incentive/keeper/elys_staked.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper

import (
"cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/elys-network/elys/x/incentive/types"
Expand All @@ -14,16 +15,17 @@ func (k Keeper) SetElysStaked(ctx sdk.Context, elysStaked types.ElysStaked) {
}

// GetElysStaked returns a elysStaked from its index
func (k Keeper) GetElysStaked(ctx sdk.Context, address string) (val types.ElysStaked, found bool) {
func (k Keeper) GetElysStaked(ctx sdk.Context, address string) math.Int {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.ElysStakedKeyPrefix))

b := store.Get(types.ElysStakedKey(address))
if b == nil {
return val, false
return math.ZeroInt()
}

val := types.ElysStaked{}
k.cdc.MustUnmarshal(b, &val)
return val, true
return val.Amount
}

// RemoveElysStaked removes a elysStaked from the store
Expand Down
3 changes: 2 additions & 1 deletion x/incentive/keeper/hooks_staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func (k Keeper) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress,

// Updating commitments on delegation changes
func (k Keeper) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
return k.BurnEdenBFromElysUnstaking(ctx, delAddr)
k.SetElysStakeChange(ctx, delAddr)
return nil
}

func (k Keeper) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) error {
Expand Down
33 changes: 17 additions & 16 deletions x/incentive/keeper/keeper_burn_edenB.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,24 @@ import (
)

// Burn EdenBoost from Elys unstaked
func (k Keeper) BurnEdenBFromElysUnstaking(ctx sdk.Context, delegator sdk.AccAddress) error {
func (k Keeper) BurnEdenBFromElysUnstaking(ctx sdk.Context, delegator sdk.AccAddress) {
delAddr := delegator.String()
// Get commitments
commitments := k.cmk.GetCommitments(ctx, delAddr)

// Get previous amount
prevElysStaked, found := k.GetElysStaked(ctx, delAddr)
// should return nil otherwise it will break staking module
if !found {
return nil
prevElysStaked := k.GetElysStaked(ctx, delAddr)
if prevElysStaked.IsZero() {
return
}

// Calculate current delegated amount of delegator
delegatedAmt := k.CalculateDelegatedAmount(ctx, delAddr)

// If not unstaked,
// should return nil otherwise it will break staking module
if delegatedAmt.GTE(prevElysStaked.Amount) {
return nil
if delegatedAmt.GTE(prevElysStaked) {
return
}

edenCommitted := commitments.GetCommittedAmountForDenom(ptypes.Eden)
Expand All @@ -39,27 +38,29 @@ func (k Keeper) BurnEdenBFromElysUnstaking(ctx sdk.Context, delegator sdk.AccAdd
totalEdenB := edenBCommitted.Add(edenBUnclaimed).Add(edenBClaimed)

// Unstaked
unstakedElys := prevElysStaked.Amount.Sub(delegatedAmt)
unstakedElys := prevElysStaked.Sub(delegatedAmt)

unstakedElysDec := sdk.NewDecFromInt(unstakedElys)
edenCommittedAndElysStakedDec := sdk.NewDecFromInt(edenCommitted.Add(prevElysStaked.Amount))
edenCommittedAndElysStakedDec := sdk.NewDecFromInt(edenCommitted.Add(prevElysStaked))
totalEdenBDec := sdk.NewDecFromInt(totalEdenB)
edenBToBurn := sdk.ZeroDec()
if edenCommittedAndElysStakedDec.GT(sdk.ZeroDec()) {
edenBToBurn = unstakedElysDec.Quo(edenCommittedAndElysStakedDec).Mul(totalEdenBDec)
}
// Burn EdenB ( Deduction EdenB in commitment module)
// Burn EdenB in commitment module
commitment, err := k.cmk.BurnEdenBoost(ctx, delAddr, ptypes.EdenB, edenBToBurn.TruncateInt())
k.cmk.SetCommitments(ctx, commitment)

return err
if err != nil {
k.Logger(ctx).Error("EdenB burn failure", err)
} else {
k.cmk.SetCommitments(ctx, commitment)
}
}

// Burn EdenBoost from Eden unclaimed
func (k Keeper) BurnEdenBFromEdenUncommitted(ctx sdk.Context, delegator string, uncommitAmt math.Int) error {
// Get elys staked amount
elysStaked, found := k.GetElysStaked(ctx, delegator)
if !found {
elysStaked := k.GetElysStaked(ctx, delegator)
if elysStaked.IsZero() {
return nil
}

Expand All @@ -77,7 +78,7 @@ func (k Keeper) BurnEdenBFromEdenUncommitted(ctx sdk.Context, delegator string,
unclaimedAmtDec := sdk.NewDecFromInt(uncommitAmt)
// This formula shud be applied before eden uncommitted or elys staked is removed from eden committed amount and elys staked amount respectively
// So add uncommitted amount to committed eden bucket in calculation.
edenCommittedAndElysStakedDec := sdk.NewDecFromInt(edenCommitted.Add(elysStaked.Amount).Add(uncommitAmt))
edenCommittedAndElysStakedDec := sdk.NewDecFromInt(edenCommitted.Add(elysStaked).Add(uncommitAmt))
totalEdenBDec := sdk.NewDecFromInt(totalEdenB)

edenBToBurn := sdk.ZeroDec()
Expand Down
8 changes: 6 additions & 2 deletions x/incentive/keeper/keeper_burn_edenB_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,16 @@ func TestBurnEdenBFromElysUnstaked(t *testing.T) {
require.Equal(t, commitment.RewardsUnclaimed[1].Denom, ptypes.EdenB)
require.Equal(t, commitment.RewardsUnclaimed[1].Amount, sdk.NewInt(20000))

// Track Elys staked amount
ik.EndBlocker(ctx)
// Take elys staked snapshot
ik.TakeDelegationSnapshot(ctx, genAccount.String())

// burn amount = 100000 (unbonded amt) / (1000000 (elys staked) + 10000 (Eden committed)) * (20000 EdenB + 5000 EdenB committed)
unbondAmt, err := sk.Unbond(ctx, genAccount, valAddr, sdk.NewDecWithPrec(10, 2))
require.Equal(t, unbondAmt, sdk.NewInt(100000))
require.NoError(t, err)

// Process EdenB burn operation
ik.EndBlocker(ctx)

commitment = app.CommitmentKeeper.GetCommitments(ctx, genAccount.String())
require.Equal(t, commitment.RewardsUnclaimed[1].Denom, ptypes.EdenB)
Expand Down
4 changes: 2 additions & 2 deletions x/incentive/keeper/keeper_shares.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (k Keeper) CalculateTotalShareOfStaking(amount math.Int) sdk.Dec {
// Calculate the delegated amount
func (k Keeper) CalculateDelegatedAmount(ctx sdk.Context, delegator string) math.Int {
// Derivate bech32 based delegator address
delAdr, err := sdk.AccAddressFromBech32(delegator)
delAddr, err := sdk.AccAddressFromBech32(delegator)
if err != nil {
// This could be validator address
return sdk.ZeroInt()
Expand All @@ -34,7 +34,7 @@ func (k Keeper) CalculateDelegatedAmount(ctx sdk.Context, delegator string) math
delegatedAmt := sdk.ZeroDec()

// Get all delegations
delegations := k.stk.GetDelegatorDelegations(ctx, delAdr, gomath.MaxUint16)
delegations := k.stk.GetDelegatorDelegations(ctx, delAddr, gomath.MaxUint16)
for _, del := range delegations {
// Get validator address
valAddr := del.GetValidatorAddr()
Expand Down
3 changes: 2 additions & 1 deletion x/incentive/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ const (
// MemStoreKey defines the in-memory store key
MemStoreKey = "mem_incentive"

ElysStakedKeyPrefix = "ElysStaked/value/"
ElysStakedKeyPrefix = "ElysStaked/value/"
ElysStakeChangeKeyPrefix = "ElysStakeChanged/value/"

// ParamsKey is the prefix to retrieve all Params
ParamsKey = "Params/value/"
Expand Down

0 comments on commit 9256717

Please sign in to comment.