From 3b17353a0edad083f83050d500facba8d9e84ec7 Mon Sep 17 00:00:00 2001 From: Marc Puig Date: Thu, 15 Feb 2024 10:06:54 +0100 Subject: [PATCH] feat: Improve C value updation (#760) * baseline * keeper test * add hourly calculation back * fix dynamic c value limits for new host chains * changelog --- CHANGELOG.md | 1 + x/liquidstakeibc/keeper/hooks.go | 3 + x/liquidstakeibc/keeper/icq.go | 3 + x/liquidstakeibc/keeper/keeper.go | 151 +++++++++++++------------ x/liquidstakeibc/keeper/keeper_test.go | 8 +- 5 files changed, 88 insertions(+), 78 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index baed225e9..49f38e791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +- [760](https://github.com/persistenceOne/pstake-native/pull/760) Calculate C Value after autocompounding / slashing. - [758](https://github.com/persistenceOne/pstake-native/pull/758) Dynamic C Value limit updates. - [757](https://github.com/persistenceOne/pstake-native/pull/757) Change ibc transfer to use timeoutTimestamp instead of timeoutHeight diff --git a/x/liquidstakeibc/keeper/hooks.go b/x/liquidstakeibc/keeper/hooks.go index 7b694b251..906dba97e 100644 --- a/x/liquidstakeibc/keeper/hooks.go +++ b/x/liquidstakeibc/keeper/hooks.go @@ -310,6 +310,9 @@ func (k *Keeper) OnRecvIBCTransferPacket( deposit.Amount.Amount = deposit.Amount.Amount.Add(transferAmount.Sub(feeAmount.TruncateInt())) k.SetDeposit(ctx, deposit) + // update the c value for the auto compounding chain + k.UpdateCValue(ctx, hc) + // emit autocompound received event ctx.EventManager().EmitEvent( sdk.NewEvent( diff --git a/x/liquidstakeibc/keeper/icq.go b/x/liquidstakeibc/keeper/icq.go index 508939e4b..3225ff68c 100644 --- a/x/liquidstakeibc/keeper/icq.go +++ b/x/liquidstakeibc/keeper/icq.go @@ -111,6 +111,9 @@ func DelegationCallback(k Keeper, ctx sdk.Context, data []byte, query icqtypes.Q validator.DelegatedAmount = delegatedAmount.TruncateInt() k.SetHostChainValidator(ctx, hc, validator) + // update the c value for the slashed chain + k.UpdateCValue(ctx, hc) + ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeSlashing, diff --git a/x/liquidstakeibc/keeper/keeper.go b/x/liquidstakeibc/keeper/keeper.go index bf6a2a5e3..73260f576 100644 --- a/x/liquidstakeibc/keeper/keeper.go +++ b/x/liquidstakeibc/keeper/keeper.go @@ -255,90 +255,93 @@ func (k *Keeper) UpdateCValues(ctx sdk.Context) { hostChains := k.GetAllHostChains(ctx) for _, hc := range hostChains { + k.UpdateCValue(ctx, hc) + } +} - // total stk tokens minted - mintedAmount := k.bankKeeper.GetSupply(ctx, hc.MintDenom()).Amount - - // total tokenized staked amount - tokenizedStakedAmount := k.GetLSMDepositAmountUntokenized(ctx, hc.ChainId) - - // amount staked by the module in any of the validators of the host chain - stakedAmount := hc.GetHostChainTotalDelegations() - - // amount that is in the staking flow and hasn't left Persistence yet - amountOnPersistence := k.GetDepositAmountOnPersistence(ctx, hc.ChainId) - - // amount that is in the staking flow and has arrived to the host chain, but hasn't been staked yet - amountOnHostChain := k.GetDepositAmountOnHostChain(ctx, hc.ChainId) - - // amount unbonded from a validator that has been in the Unbonding state for more than 4 unbonding epochs - totalUnbondingAmount := k.GetAllValidatorUnbondedAmount(ctx, hc) - - // total amount staked - liquidStakedAmount := tokenizedStakedAmount.Add(stakedAmount).Add(amountOnPersistence).Add(amountOnHostChain).Add(totalUnbondingAmount) - - var cValue sdk.Dec - if mintedAmount.IsZero() || liquidStakedAmount.IsZero() { - cValue = sdk.OneDec() - } else { - cValue = sdk.NewDecFromInt(mintedAmount).Quo(sdk.NewDecFromInt(liquidStakedAmount)) - } - - k.Logger(ctx).Info( - fmt.Sprintf( - "Updated CValue for %s. Total minted amount: %v. Total liquid staked amount: %v. Composed of %v staked tokens, %v tokens on Persistence, %v tokens on the host chain, %v tokens from a validator total unbonding. New c_value: %v - Old c_value: %v", - hc.ChainId, - mintedAmount, - liquidStakedAmount, - stakedAmount, - amountOnPersistence, - amountOnHostChain, - totalUnbondingAmount, - cValue, - hc.CValue, - ), - ) +func (k *Keeper) UpdateCValue(ctx sdk.Context, hc *types.HostChain) { + // total stk tokens minted + mintedAmount := k.bankKeeper.GetSupply(ctx, hc.MintDenom()).Amount - hc.LastCValue = hc.CValue - hc.CValue = cValue - k.SetHostChain(ctx, hc) + // total tokenized staked amount + tokenizedStakedAmount := k.GetLSMDepositAmountUntokenized(ctx, hc.ChainId) + + // amount staked by the module in any of the validators of the host chain + stakedAmount := hc.GetHostChainTotalDelegations() + + // amount that is in the staking flow and hasn't left Persistence yet + amountOnPersistence := k.GetDepositAmountOnPersistence(ctx, hc.ChainId) + + // amount that is in the staking flow and has arrived to the host chain, but hasn't been staked yet + amountOnHostChain := k.GetDepositAmountOnHostChain(ctx, hc.ChainId) + + // amount unbonded from a validator that has been in the Unbonding state for more than 4 unbonding epochs + totalUnbondingAmount := k.GetAllValidatorUnbondedAmount(ctx, hc) + + // total amount staked + liquidStakedAmount := tokenizedStakedAmount.Add(stakedAmount).Add(amountOnPersistence).Add(amountOnHostChain).Add(totalUnbondingAmount) + + var cValue sdk.Dec + if mintedAmount.IsZero() || liquidStakedAmount.IsZero() { + cValue = sdk.OneDec() + } else { + cValue = sdk.NewDecFromInt(mintedAmount).Quo(sdk.NewDecFromInt(liquidStakedAmount)) + } + + k.Logger(ctx).Info( + fmt.Sprintf( + "Updated CValue for %s. Total minted amount: %v. Total liquid staked amount: %v. Composed of %v staked tokens, %v tokens on Persistence, %v tokens on the host chain, %v tokens from a validator total unbonding. New c_value: %v - Old c_value: %v", + hc.ChainId, + mintedAmount, + liquidStakedAmount, + stakedAmount, + amountOnPersistence, + amountOnHostChain, + totalUnbondingAmount, + cValue, + hc.CValue, + ), + ) + + hc.LastCValue = hc.CValue + hc.CValue = cValue + k.SetHostChain(ctx, hc) - if err := k.Hooks().PostCValueUpdate(ctx, hc.MintDenom(), hc.HostDenom, hc.CValue); err != nil { - k.Logger(ctx).Error("PostCValueUpdate hook failed with ", "err:", err) - } + if err := k.Hooks().PostCValueUpdate(ctx, hc.MintDenom(), hc.HostDenom, hc.CValue); err != nil { + k.Logger(ctx).Error("PostCValueUpdate hook failed with ", "err:", err) + } + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeCValueUpdate, + sdk.NewAttribute(types.AttributeChainID, hc.ChainId), + sdk.NewAttribute(types.AttributeModuleMintedAmount, sdk.NewCoin(hc.MintDenom(), mintedAmount).String()), + sdk.NewAttribute(types.AttributeModuleLSMTokenizedAmount, sdk.NewCoin(hc.HostDenom, tokenizedStakedAmount).String()), + sdk.NewAttribute(types.AttributeModuleStakedAmount, sdk.NewCoin(hc.HostDenom, stakedAmount).String()), + sdk.NewAttribute(types.AttributeModuleAmountOnPersistence, sdk.NewCoin(hc.HostDenom, amountOnPersistence).String()), + sdk.NewAttribute(types.AttributeModuleAmountOnHostChain, sdk.NewCoin(hc.HostDenom, amountOnHostChain).String()), + sdk.NewAttribute(types.AttributeModuleUnbondingAmount, sdk.NewCoin(hc.HostDenom, totalUnbondingAmount).String()), + sdk.NewAttribute(types.AttributeOldCValue, hc.LastCValue.String()), + sdk.NewAttribute(types.AttributeNewCValue, hc.CValue.String()), + ), + ) + + // if the c value is out of bounds, disable the chain + if !k.CValueWithinLimits(hc) { + hc.Active = false + k.SetHostChain(ctx, hc) + + k.Logger(ctx).Error(fmt.Sprintf("C value out of limits !!! Disabling chain %s with c value %v.", hc.ChainId, hc.CValue)) ctx.EventManager().EmitEvent( sdk.NewEvent( - types.EventTypeCValueUpdate, + types.EventTypeChainDisabled, sdk.NewAttribute(types.AttributeChainID, hc.ChainId), - sdk.NewAttribute(types.AttributeModuleMintedAmount, sdk.NewCoin(hc.MintDenom(), mintedAmount).String()), - sdk.NewAttribute(types.AttributeModuleLSMTokenizedAmount, sdk.NewCoin(hc.HostDenom, tokenizedStakedAmount).String()), - sdk.NewAttribute(types.AttributeModuleStakedAmount, sdk.NewCoin(hc.HostDenom, stakedAmount).String()), - sdk.NewAttribute(types.AttributeModuleAmountOnPersistence, sdk.NewCoin(hc.HostDenom, amountOnPersistence).String()), - sdk.NewAttribute(types.AttributeModuleAmountOnHostChain, sdk.NewCoin(hc.HostDenom, amountOnHostChain).String()), - sdk.NewAttribute(types.AttributeModuleUnbondingAmount, sdk.NewCoin(hc.HostDenom, totalUnbondingAmount).String()), sdk.NewAttribute(types.AttributeOldCValue, hc.LastCValue.String()), sdk.NewAttribute(types.AttributeNewCValue, hc.CValue.String()), ), ) - - // if the c value is out of bounds, disable the chain - if !k.CValueWithinLimits(hc) { - hc.Active = false - k.SetHostChain(ctx, hc) - - k.Logger(ctx).Error(fmt.Sprintf("C value out of limits !!! Disabling chain %s with c value %v.", hc.ChainId, hc.CValue)) - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeChainDisabled, - sdk.NewAttribute(types.AttributeChainID, hc.ChainId), - sdk.NewAttribute(types.AttributeOldCValue, hc.LastCValue.String()), - sdk.NewAttribute(types.AttributeNewCValue, hc.CValue.String()), - ), - ) - } else { - k.RecalculateCValueLimits(ctx, hc, mintedAmount, liquidStakedAmount) - } + } else { + k.RecalculateCValueLimits(ctx, hc, mintedAmount, liquidStakedAmount) } } @@ -348,7 +351,7 @@ func (k *Keeper) CValueWithinLimits(hc *types.HostChain) bool { func (k *Keeper) RecalculateCValueLimits(ctx sdk.Context, hc *types.HostChain, mintedAmount, lsAmount math.Int) { // if there has been no activity on the chain yet, leave the limits as they are - if mintedAmount.IsZero() || lsAmount.IsZero() { + if hc.GetHostChainTotalDelegations().IsZero() { return } diff --git a/x/liquidstakeibc/keeper/keeper_test.go b/x/liquidstakeibc/keeper/keeper_test.go index de31c6678..efa3df3d7 100644 --- a/x/liquidstakeibc/keeper/keeper_test.go +++ b/x/liquidstakeibc/keeper/keeper_test.go @@ -246,13 +246,13 @@ func (suite *IntegrationTestSuite) TestSendICATransfer() { suite.Require().Error(err) } -func (suite *IntegrationTestSuite) TestUpdateCValues() { +func (suite *IntegrationTestSuite) TestUpdateCValue() { pstakeApp, ctx := suite.app, suite.ctx hc, found := pstakeApp.LiquidStakeIBCKeeper.GetHostChain(ctx, suite.chainB.ChainID) suite.Require().Equal(true, found) suite.Require().NotNil(hc) - suite.Require().NotPanics(func() { pstakeApp.LiquidStakeIBCKeeper.UpdateCValues(ctx) }) + suite.Require().NotPanics(func() { pstakeApp.LiquidStakeIBCKeeper.UpdateCValue(ctx, hc) }) { epoch := pstakeApp.EpochsKeeper.GetEpochInfo(suite.chainA.GetContext(), types.DelegationEpoch) @@ -267,12 +267,12 @@ func (suite *IntegrationTestSuite) TestUpdateCValues() { suite.NotNil(result) suite.NoError(err) } - suite.Require().NotPanics(func() { pstakeApp.LiquidStakeIBCKeeper.UpdateCValues(ctx) }) + suite.Require().NotPanics(func() { pstakeApp.LiquidStakeIBCKeeper.UpdateCValue(ctx, hc) }) // lower limits so that chain goes out of limits hc.Params.UpperCValueLimit = sdk.MustNewDecFromStr("0.5") pstakeApp.LiquidStakeIBCKeeper.SetHostChain(ctx, hc) - suite.Require().NotPanics(func() { pstakeApp.LiquidStakeIBCKeeper.UpdateCValues(ctx) }) + suite.Require().NotPanics(func() { pstakeApp.LiquidStakeIBCKeeper.UpdateCValue(ctx, hc) }) hc, _ = pstakeApp.LiquidStakeIBCKeeper.GetHostChain(ctx, suite.chainB.ChainID) suite.Require().Equal(false, hc.Active) }