Skip to content

Commit

Permalink
feat: Improve C value updation (#760)
Browse files Browse the repository at this point in the history
* baseline

* keeper test

* add hourly calculation back

* fix dynamic c value limits for new host chains

* changelog
  • Loading branch information
kruspy authored Feb 15, 2024
1 parent adcd4ca commit 3b17353
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 78 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions x/liquidstakeibc/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 3 additions & 0 deletions x/liquidstakeibc/keeper/icq.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
151 changes: 77 additions & 74 deletions x/liquidstakeibc/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand All @@ -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
}

Expand Down
8 changes: 4 additions & 4 deletions x/liquidstakeibc/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}
Expand Down

0 comments on commit 3b17353

Please sign in to comment.