Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement stable stake hooks for leverage pools and enhance unbonding… #1065

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,8 @@ func NewAppKeeper(
app.StablestakeKeeper.SetHooks(stablestakekeeper.NewMultiStableStakeHooks(
app.MasterchefKeeper.StableStakeHooks(),
app.TierKeeper.StableStakeHooks(),
// unbonding checks leverage pool health
app.LeveragelpKeeper.StableStakeHooks(),
))

app.LeveragelpKeeper.SetHooks(leveragelpmoduletypes.NewMultiLeverageLpHooks(
Expand Down
49 changes: 49 additions & 0 deletions x/leveragelp/keeper/hooks_stablestake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package keeper

import (
"fmt"

errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/elys-network/elys/x/leveragelp/types"
stablestaketypes "github.com/elys-network/elys/x/stablestake/types"
)

func (k Keeper) AfterBond(ctx sdk.Context, sender sdk.AccAddress, shareAmount math.Int) error {
return nil
}

func (k Keeper) AfterUnbond(ctx sdk.Context, sender sdk.AccAddress, shareAmount math.Int) error {
// loop over all leverage pools and throw error if any leverage pool is unhealthy
for _, pool := range k.GetAllPools(ctx) {
// print that after unbound is called and checking pool health of pool id
fmt.Printf("after unbound is called and checking pool health of pool id %d\n", pool.AmmPoolId)
if err := k.CheckPoolHealth(ctx, pool.AmmPoolId); err != nil {
return errorsmod.Wrapf(types.ErrUnbondingPoolHealth, "pool health too low to unbond for pool %d", pool.AmmPoolId)
}

Check warning on line 24 in x/leveragelp/keeper/hooks_stablestake.go

View check run for this annotation

Codecov / codecov/patch

x/leveragelp/keeper/hooks_stablestake.go#L23-L24

Added lines #L23 - L24 were not covered by tests
}

return nil
}

type StableStakeHooks struct {
k Keeper
}

var _ stablestaketypes.StableStakeHooks = StableStakeHooks{}

// Return the wrapper struct
func (k Keeper) StableStakeHooks() StableStakeHooks {
return StableStakeHooks{k}
}

// AfterPoolCreated is called after CreatePool
func (h StableStakeHooks) AfterBond(ctx sdk.Context, sender sdk.AccAddress, shareAmount math.Int) error {
return h.k.AfterBond(ctx, sender, shareAmount)
}

// AfterJoinPool is called after JoinPool, JoinSwapExternAmountIn, and JoinSwapShareAmountOut
func (h StableStakeHooks) AfterUnbond(ctx sdk.Context, sender sdk.AccAddress, shareAmount math.Int) error {
return h.k.AfterUnbond(ctx, sender, shareAmount)
}
50 changes: 50 additions & 0 deletions x/leveragelp/keeper/msg_server_open_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
expectErr bool
expectErrMsg string
prerequisiteFunction func()
postValidateFunc func(msg *types.MsgOpen)
}{
{"failed user authorization",
&types.MsgOpen{
Expand All @@ -109,6 +110,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
func() {
suite.EnableWhiteListing()
},
func(msg *types.MsgOpen) {},
},
{"no positions allowed",
&types.MsgOpen{
Expand All @@ -125,6 +127,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
suite.DisableWhiteListing()
suite.SetMaxOpenPositions(0)
},
func(msg *types.MsgOpen) {},
},
{name: "Max positions reached",
input: &types.MsgOpen{
Expand All @@ -139,6 +142,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
expectErrMsg: "cannot open new positions, open positions 0 - max positions 0: max open",
prerequisiteFunction: func() {
},
postValidateFunc: func(msg *types.MsgOpen) {},
},
{name: "Pool not found",
input: &types.MsgOpen{
Expand All @@ -154,6 +158,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
prerequisiteFunction: func() {
suite.SetMaxOpenPositions(3)
},
postValidateFunc: func(msg *types.MsgOpen) {},
},
{name: "base currency not found",
input: &types.MsgOpen{
Expand All @@ -172,6 +177,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
suite.RemovePrices(suite.ctx, []string{"uusdc"})
suite.SetMaxOpenPositions(20)
},
postValidateFunc: func(msg *types.MsgOpen) {},
},
{name: "AMM Pool not found",
input: &types.MsgOpen{
Expand All @@ -187,6 +193,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
prerequisiteFunction: func() {
suite.SetupCoinPrices(suite.ctx)
},
postValidateFunc: func(msg *types.MsgOpen) {},
},
{name: "Pool not enabled",
input: &types.MsgOpen{
Expand All @@ -205,6 +212,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
amm_pool := ammtypes.Pool{PoolId: 2, Address: ammtypes.NewPoolAddress(2).String(), TotalShares: sdk.Coin{Amount: sdkmath.NewInt(100)}}
suite.app.AmmKeeper.SetPool(suite.ctx, amm_pool)
},
postValidateFunc: func(msg *types.MsgOpen) {},
},
{"Collateral asset not equal to base currency",
&types.MsgOpen{
Expand All @@ -220,6 +228,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
func() {
suite.SetPoolThreshold(sdkmath.LegacyMustNewDecFromStr("0.2"))
},
func(msg *types.MsgOpen) {},
},
{"Base currency not found",
&types.MsgOpen{
Expand All @@ -234,6 +243,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
types.ErrOnlyBaseCurrencyAllowed.Error(),
func() {
},
func(msg *types.MsgOpen) {},
},
{"First open position but leads to low pool health",
&types.MsgOpen{
Expand All @@ -250,6 +260,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
suite.AddCoinPrices(suite.ctx, []string{ptypes.BaseCurrency})
suite.SetPoolThreshold(sdkmath.LegacyOneDec())
},
func(msg *types.MsgOpen) {},
},
{"Low Balance of creator",
&types.MsgOpen{
Expand All @@ -265,6 +276,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
func() {
suite.SetPoolThreshold(sdkmath.LegacyMustNewDecFromStr("0.2"))
},
func(msg *types.MsgOpen) {},
},
{"Borrowing more than allowed",
&types.MsgOpen{
Expand All @@ -280,6 +292,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
func() {
suite.SetPoolThreshold(sdkmath.LegacyMustNewDecFromStr("0.2"))
},
func(msg *types.MsgOpen) {},
},
{"Position safety factor too low",
&types.MsgOpen{
Expand All @@ -295,6 +308,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
func() {
suite.SetSafetyFactor(sdkmath.LegacyOneDec().MulInt64(10))
},
func(msg *types.MsgOpen) {},
},
{"Open new Position with leverage <=1",
&types.MsgOpen{
Expand All @@ -309,6 +323,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
"",
func() {
},
func(msg *types.MsgOpen) {},
},
{"Open Position",
&types.MsgOpen{
Expand All @@ -328,6 +343,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
suite.SetSafetyFactor(sdkmath.LegacyMustNewDecFromStr("1.1"))
suite.SetPoolThreshold(sdkmath.LegacyMustNewDecFromStr("0.2"))
},
func(msg *types.MsgOpen) {},
},
{"Add on already open position Long but with different leverage 10",
&types.MsgOpen{
Expand All @@ -342,6 +358,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
"",
func() {
},
func(msg *types.MsgOpen) {},
},
{"Add on already open position Long but with different leverage 20",
&types.MsgOpen{
Expand All @@ -356,6 +373,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
"",
func() {
},
func(msg *types.MsgOpen) {},
},
{"Add on already open position Long but with different leverage 1, increase position health",
&types.MsgOpen{
Expand All @@ -371,6 +389,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
func() {
suite.SetSafetyFactor(sdkmath.LegacyMustNewDecFromStr("2.0"))
},
func(msg *types.MsgOpen) {},
},
{"Add on already open position Long but with different leverage 30",
&types.MsgOpen{
Expand All @@ -385,6 +404,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
types.ErrPositionUnhealthy.Error(),
func() {
},
func(msg *types.MsgOpen) {},
},
{"Low pool health to open position",
&types.MsgOpen{
Expand All @@ -401,6 +421,35 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
suite.SetSafetyFactor(sdkmath.LegacyMustNewDecFromStr("1.0"))
suite.SetPoolThreshold(sdkmath.LegacyOneDec())
},
func(msg *types.MsgOpen) {},
},
{"Unbond with position open",
&types.MsgOpen{
Creator: addresses[0].String(),
CollateralAsset: ptypes.BaseCurrency,
CollateralAmount: sdkmath.NewInt(100_000_000),
AmmPoolId: 1,
Leverage: sdkmath.LegacyMustNewDecFromStr("10.0"),
StopLossPrice: sdkmath.LegacyMustNewDecFromStr("50.0"),
},
false,
"",
func() {
suite.ResetSuite()
suite.SetupCoinPrices(suite.ctx)
initializeForOpen(suite, addresses, asset1, asset2)
suite.SetSafetyFactor(sdkmath.LegacyMustNewDecFromStr("0.1"))
suite.SetPoolThreshold(sdkmath.LegacyMustNewDecFromStr("0.2"))
},
func(msg *types.MsgOpen) {
amount := sdkmath.NewInt(500_000_000_000_000)
stableStakeMsgServer := stablekeeper.NewMsgServerImpl(*suite.app.StablestakeKeeper)
_, err := stableStakeMsgServer.Unbond(suite.ctx, &stabletypes.MsgUnbond{
Creator: addresses[1].String(),
Amount: amount,
})
suite.Require().NoError(err)
},
},
}

Expand All @@ -413,6 +462,7 @@ func (suite *KeeperTestSuite) TestOpen_PoolWithBaseCurrencyAsset() {
suite.Require().Error(err)
suite.Require().Contains(err.Error(), tc.expectErrMsg)
} else {
tc.postValidateFunc(tc.input)
// The new value of the portfolio after the hook is called.
portfolio_new, _ := suite.app.TierKeeper.GetPortfolio(suite.ctx, sdk.MustAccAddressFromBech32(tc.input.Creator), suite.app.TierKeeper.GetDateFromContext(suite.ctx))
// Initially, there were no entries for the portfolio
Expand Down
3 changes: 3 additions & 0 deletions x/leveragelp/keeper/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ func (k Keeper) CheckPoolHealth(ctx sdk.Context, poolId uint64) error {
return errorsmod.Wrap(types.ErrInvalidBorrowingAsset, "invalid collateral asset")
}

// print pool health
fmt.Printf("pool health: %s\n", pool.Health.String())

if !pool.Health.IsNil() && pool.Health.LTE(k.GetPoolOpenThreshold(ctx)) {
return errorsmod.Wrap(types.ErrInvalidPosition, "pool health too low to open new positions")
}
Expand Down
1 change: 1 addition & 0 deletions x/leveragelp/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ var (
ErrPoolLeverageAmountNotZero = errorsmod.Register(ModuleName, 41, "pool leverage amount is greater than zero")
ErrLeverageTooSmall = errorsmod.Register(ModuleName, 42, "leverage should be more than or equal to 1")
ErrMaxLeverageLpExists = errorsmod.Register(ModuleName, 43, "pool is already leveraged at maximum value")
ErrUnbondingPoolHealth = errorsmod.Register(ModuleName, 44, "pool health too low to unbond")
)
5 changes: 5 additions & 0 deletions x/stablestake/keeper/msg_server_unbond.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ package keeper

import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/elys-network/elys/x/stablestake/types"
)

func (k msgServer) Unbond(goCtx context.Context, msg *types.MsgUnbond) (*types.MsgUnbondResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

return k.Keeper.Unbond(ctx, msg)
}

func (k Keeper) Unbond(ctx sdk.Context, msg *types.MsgUnbond) (*types.MsgUnbondResponse, error) {
params := k.GetParams(ctx)
creator := sdk.MustAccAddressFromBech32(msg.Creator)
redemptionRate := k.GetRedemptionRate(ctx)
Expand Down
1 change: 1 addition & 0 deletions x/stablestake/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"context"

sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
assetprofiletypes "github.com/elys-network/elys/x/assetprofile/types"
Expand Down
Loading