Skip to content

Commit

Permalink
Dev 1954 user cannot partially close a position (#833)
Browse files Browse the repository at this point in the history
* feat: fix bug

* update

* test

---------

Co-authored-by: Cosmic Vagabond <[email protected]>
  • Loading branch information
cryptokage1996 and cosmic-vagabond authored Oct 2, 2024
1 parent 26b8c39 commit 92f5a14
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 6 deletions.
47 changes: 47 additions & 0 deletions x/perpetual/keeper/close_long_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,50 @@ func TestCloseLong_SuccessfulClosingLongPosition(t *testing.T) {
assert.Equal(t, mtp, *mtpOut)
mockChecker.AssertExpectations(t)
}

func TestPartiallyClosing_LongPosition(t *testing.T) {
// Setup the mock checker
mockChecker := new(mocks.CloseLongChecker)

// Create an instance of Keeper with the mock checker
k := keeper.Keeper{
CloseLongChecker: mockChecker,
}

var (
ctx = sdk.Context{} // Mock or setup a context
msg = &types.MsgClose{
Creator: "cosmos1xv9tklw7d82sezh9haa573wufgy59vmwe6xxe5",
Id: 1,
Amount: sdk.NewInt(100),
}
mtp = types.MTP{
AmmPoolId: 2,
CollateralAsset: ptypes.BaseCurrency,
CustodyAsset: "uatom",
Collateral: sdk.NewInt(100),
Custody: sdk.NewInt(10000),
}
pool = types.Pool{
BorrowInterestRate: math.LegacyNewDec(2),
}
ammPool = ammtypes.Pool{}
repayAmount = math.NewInt(100)
)

// Mock behavior
mockChecker.On("GetMTP", ctx, sdk.MustAccAddressFromBech32(msg.Creator), msg.Id).Return(mtp, nil)
mockChecker.On("GetPool", ctx, mtp.AmmPoolId).Return(pool, true)
mockChecker.On("GetAmmPool", ctx, mtp.AmmPoolId, mtp.CustodyAsset).Return(ammPool, nil)
mockChecker.On("SettleBorrowInterest", ctx, &mtp, &pool, ammPool).Return(sdk.ZeroInt(), nil)
mockChecker.On("TakeOutCustody", ctx, mtp, &pool, msg.Amount).Return(nil)
mockChecker.On("EstimateAndRepay", ctx, mtp, pool, ammPool, msg.Amount, ptypes.BaseCurrency).Return(repayAmount, nil)

mtpOut, repayAmountOut, err := k.CloseLong(ctx, msg, ptypes.BaseCurrency)

// Expect no error
assert.Nil(t, err)
assert.Equal(t, repayAmount, repayAmountOut)
assert.Equal(t, mtp, *mtpOut)
mockChecker.AssertExpectations(t)
}
45 changes: 45 additions & 0 deletions x/perpetual/keeper/close_short_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,48 @@ func TestCloseShort_SuccessfulClosingLongPosition(t *testing.T) {
assert.Equal(t, mtp, *mtpOut)
mockChecker.AssertExpectations(t)
}

func TestCloseShort_PartiallyClosingShortPosition(t *testing.T) {
// Setup the mock checker
mockChecker := new(mocks.CloseShortChecker)

// Create an instance of Keeper with the mock checker
k := keeper.Keeper{
CloseShortChecker: mockChecker,
}

var (
ctx = sdk.Context{} // Mock or setup a context
msg = &types.MsgClose{
Creator: "cosmos10duudma7ef9849ee42zhe5q4t4fmk0z99uuh92",
Id: 1,
Amount: sdk.NewInt(100),
}
mtp = types.MTP{
AmmPoolId: 2,
Custody: sdk.NewInt(10000),
Collateral: sdk.NewInt(100),
}
pool = types.Pool{
BorrowInterestRate: math.LegacyNewDec(2),
}
ammPool = ammtypes.Pool{}
repayAmount = math.NewInt(100)
)

// Mock behavior
mockChecker.On("GetMTP", ctx, sdk.MustAccAddressFromBech32(msg.Creator), msg.Id).Return(mtp, nil)
mockChecker.On("GetPool", ctx, mtp.AmmPoolId).Return(pool, true)
mockChecker.On("GetAmmPool", ctx, mtp.AmmPoolId, mtp.CustodyAsset).Return(ammPool, nil)
mockChecker.On("SettleBorrowInterest", ctx, &mtp, &pool, ammPool).Return(sdk.ZeroInt(), nil)
mockChecker.On("TakeOutCustody", ctx, mtp, &pool, msg.Amount).Return(nil)
mockChecker.On("EstimateAndRepay", ctx, mtp, pool, ammPool, msg.Amount, ptypes.BaseCurrency).Return(repayAmount, nil)

mtpOut, repayAmountOut, err := k.CloseShort(ctx, msg, ptypes.BaseCurrency)

// Expect no error
assert.Nil(t, err)
assert.Equal(t, repayAmount, repayAmountOut)
assert.Equal(t, mtp, *mtpOut)
mockChecker.AssertExpectations(t)
}
13 changes: 7 additions & 6 deletions x/perpetual/keeper/repay.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package keeper

import (
"fmt"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
ammtypes "github.com/elys-network/elys/x/amm/types"
Expand Down Expand Up @@ -46,17 +48,15 @@ func (k Keeper) Repay(ctx sdk.Context, mtp *types.MTP, pool *types.Pool, ammPool
// if short both repay amount and liablities are trading asset

have := repayAmount
owe := Liabilities.Add(BorrowInterestUnpaid)
owe := Liabilities.Add(BorrowInterestUnpaid).Mul(amount.Quo(mtp.Custody))

if have.LT(Liabilities) {
// can't afford principle liability
returnAmount = sdk.ZeroInt()
} else if have.LT(owe) {
if have.LT(owe) {
// v principle liability; x excess liability
returnAmount = sdk.ZeroInt()
} else {
// can afford both
returnAmount = have.Sub(Liabilities).Sub(BorrowInterestUnpaid)
returnAmount = have.Sub(owe)
mtp.Liabilities = mtp.Liabilities.Sub(Liabilities.Mul(amount).Quo(mtp.Custody))
}

if returnAmount.IsPositive() {
Expand Down Expand Up @@ -128,6 +128,7 @@ func (k Keeper) Repay(ctx sdk.Context, mtp *types.MTP, pool *types.Pool, ammPool
// because so far returnAmount is in base currency.
if mtp.Position == types.Position_SHORT {
// swap to collateral asset
fmt.Print("returnAmount: ", returnAmount, "\n")
amtTokenIn := sdk.NewCoin(mtp.TradingAsset, returnAmount)
C, err := k.EstimateSwapGivenOut(ctx, amtTokenIn, mtp.CollateralAsset, ammPool)
if err != nil {
Expand Down

0 comments on commit 92f5a14

Please sign in to comment.