Skip to content

Commit

Permalink
Implement partial close on leveragelp (#270)
Browse files Browse the repository at this point in the history
* implement partial close on leveragelp

* implement ForceCloseLongPartial
  • Loading branch information
jelysn authored Nov 24, 2023
1 parent 321868b commit b810796
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 55 deletions.
4 changes: 4 additions & 0 deletions proto/elys/leveragelp/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ message MsgOpenResponse {}
message MsgClose {
string creator = 1;
uint64 id = 2;
string lp_amount = 3 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}

message MsgCloseResponse {}
Expand Down
2 changes: 1 addition & 1 deletion x/leveragelp/keeper/begin_blocker.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (k Keeper) LiquidatePositionIfUnhealthy(ctx sdk.Context, position *types.Po
return
}

repayAmount, err := k.ForceCloseLong(ctx, *position, pool)
repayAmount, err := k.ForceCloseLong(ctx, *position, pool, position.LeveragedLpAmount)
if err == nil {
ctx.EventManager().EmitEvent(sdk.NewEvent(types.EventClose,
sdk.NewAttribute("id", strconv.FormatInt(int64(position.Id), 10)),
Expand Down
31 changes: 23 additions & 8 deletions x/leveragelp/keeper/position_close.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ import (
"github.com/elys-network/elys/x/leveragelp/types"
)

func (k Keeper) ForceCloseLong(ctx sdk.Context, position types.Position, pool types.Pool) (sdk.Int, error) {
func (k Keeper) ForceCloseLong(ctx sdk.Context, position types.Position, pool types.Pool, lpAmount sdk.Int) (sdk.Int, error) {
if lpAmount.GT(position.LeveragedLpAmount) || lpAmount.IsNegative() {
return sdk.ZeroInt(), types.ErrInvalidCloseSize
}

// Exit liquidity with collateral token
exitCoins, err := k.amm.ExitPool(ctx, position.GetPositionAddress(), position.AmmPoolId, position.LeveragedLpAmount, sdk.Coins{}, position.Collateral.Denom)
exitCoins, err := k.amm.ExitPool(ctx, position.GetPositionAddress(), position.AmmPoolId, lpAmount, sdk.Coins{}, position.Collateral.Denom)
if err != nil {
return sdk.ZeroInt(), err
}

// Repay with interest
debt := k.stableKeeper.UpdateInterestStackedByAddress(ctx, position.GetPositionAddress())
repayAmount := debt.Borrowed.Add(debt.InterestStacked).Sub(debt.InterestPaid)
repayAmount := debt.Borrowed.Add(debt.InterestStacked).Sub(debt.InterestPaid).Mul(lpAmount).Quo(position.LeveragedLpAmount)
if err != nil {
return sdk.ZeroInt(), err
}
Expand All @@ -32,12 +36,17 @@ func (k Keeper) ForceCloseLong(ctx sdk.Context, position types.Position, pool ty
return sdk.ZeroInt(), err
}

err = k.DestroyPosition(ctx, position.Address, position.Id)
if err != nil {
return sdk.ZeroInt(), err
position.LeveragedLpAmount = position.LeveragedLpAmount.Sub(lpAmount)
if position.LeveragedLpAmount.IsZero() {
err = k.DestroyPosition(ctx, position.Address, position.Id)
if err != nil {
return sdk.ZeroInt(), err
}
} else {
k.SetPosition(ctx, &position)
}

pool.LeveragedLpAmount = pool.LeveragedLpAmount.Sub(position.LeveragedLpAmount)
pool.LeveragedLpAmount = pool.LeveragedLpAmount.Sub(lpAmount)
k.UpdatePoolHealth(ctx, &pool)

// Hooks after leveragelp position closed
Expand All @@ -60,6 +69,12 @@ func (k Keeper) CloseLong(ctx sdk.Context, msg *types.MsgClose) (*types.Position
return nil, sdk.ZeroInt(), sdkerrors.Wrap(types.ErrInvalidBorrowingAsset, "invalid pool id")
}

repayAmount, err := k.ForceCloseLong(ctx, position, pool)
// If lpAmount is lower than zero, close full amount
lpAmount := msg.LpAmount
if lpAmount.LTE(sdk.ZeroInt()) {
lpAmount = position.LeveragedLpAmount
}

repayAmount, err := k.ForceCloseLong(ctx, position, pool, lpAmount)
return &position, repayAmount, err
}
20 changes: 17 additions & 3 deletions x/leveragelp/keeper/position_close_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ func (suite KeeperTestSuite) TestCloseLong() {

var (
msg = &types.MsgClose{
Creator: addr.String(),
Id: 1,
Creator: addr.String(),
Id: 1,
LpAmount: sdk.ZeroInt(),
}
repayAmount = math.NewInt(0)
)
Expand All @@ -121,11 +122,24 @@ func (suite KeeperTestSuite) TestForceCloseLong() {
repayAmount := math.NewInt(4000)

suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Hour))
repayAmountOut, err := k.ForceCloseLong(suite.ctx, *position, pool)
repayAmountOut, err := k.ForceCloseLong(suite.ctx, *position, pool, position.LeveragedLpAmount)
suite.Require().NoError(err)
suite.Require().Equal(repayAmount.String(), repayAmountOut.String())
}

func (suite KeeperTestSuite) TestForceCloseLongPartial() {
k := suite.app.LeveragelpKeeper
addr := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
position, pool := suite.OpenPosition(addr)
repayAmount := math.NewInt(4000)

suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Hour))
// close 50%
repayAmountOut, err := k.ForceCloseLong(suite.ctx, *position, pool, position.LeveragedLpAmount.Quo(sdk.NewInt(2)))
suite.Require().NoError(err)
suite.Require().Equal(repayAmount.Quo(sdk.NewInt(2)).String(), repayAmountOut.String())
}

func (suite KeeperTestSuite) TestHealthDecreaseForInterest() {
k := suite.app.LeveragelpKeeper
addr := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
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 @@ -27,4 +27,5 @@ var (
ErrAmmPoolNotFound = sdkerrors.Register(ModuleName, 34, "amm pool not found")
ErrOnlyBaseCurrencyAllowed = sdkerrors.Register(ModuleName, 35, "only base currency is allowed for leverage lp")
ErrInsufficientUsdcAfterOp = sdkerrors.Register(ModuleName, 36, "insufficient amount of usdc after the operation for leveragelp withdrawal")
ErrInvalidCloseSize = sdkerrors.Register(ModuleName, 37, "invalid close size")
)
134 changes: 91 additions & 43 deletions x/leveragelp/types/tx.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b810796

Please sign in to comment.