Skip to content

Commit 1de376a

Browse files
authored
splitting perps liquidation in 2 parts (#1414)
* splitting perps liquidation in 2 parts * splitting perps liquidation in 2 parts * fixing for closing ratio on liquidation * fixing for closing ratio on liquidation * splitting liquidation in 2 * splitting liquidation in 2 * Correcting minimum liabilities ratio check for perps * Correcting minimum liabilities ratio check for perps * refund * fixing for SUT * updating queries * adding new price feeders * adding new price feeders * making first liqudiation closing ratio as param
1 parent bec60a2 commit 1de376a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1653
-1028
lines changed

api/elys/perpetual/params.pulsar.go

Lines changed: 185 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/elys/perpetual/query.pulsar.go

Lines changed: 411 additions & 331 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/elys/perpetual/types.pulsar.go

Lines changed: 133 additions & 66 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/setup_handlers.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package app
22

33
import (
44
"context"
5+
"cosmossdk.io/math"
56
"fmt"
7+
oracletypes "github.com/elys-network/elys/v7/x/oracle/types"
68
"strings"
79

810
storetypes "cosmossdk.io/store/types"
@@ -62,13 +64,40 @@ func (app *ElysApp) setUpgradeHandler() {
6264

6365
vm, vmErr := app.mm.RunMigrations(ctx, app.configurator, vm)
6466

65-
//oracleParams := app.OracleKeeper.GetParams(ctx)
66-
//if len(oracleParams.MandatoryList) == 0 {
67-
// err := app.ojoOracleMigration(ctx, plan.Height+1)
68-
// if err != nil {
69-
// return nil, err
70-
// }
71-
//}
67+
if ctx.ChainID() == "elys-1" {
68+
newPriceFeeders := []sdk.AccAddress{
69+
sdk.MustAccAddressFromBech32("elys1y5jeztqtf7vwqe6wd7tv2z8mzjg38hn6zkpmvq"),
70+
sdk.MustAccAddressFromBech32("elys16r4y6hdehvntgg70avg6f0s2x55k3wekeyw8vw"),
71+
}
72+
73+
for _, accAddress := range newPriceFeeders {
74+
v := oracletypes.PriceFeeder{
75+
Feeder: accAddress.String(),
76+
IsActive: true,
77+
}
78+
app.OracleKeeper.SetPriceFeeder(ctx, v)
79+
}
80+
}
81+
82+
receiver := sdk.MustAccAddressFromBech32("elys1kxgan4uq0m8gqztd09n6qm627p6v4ayzngxyx8")
83+
sender := sdk.MustAccAddressFromBech32("elys1p2fhrn9zfra9lv5062nvzkmp9hduhm9hkk6kz6a3ucwktjvuzv9smry5sk")
84+
amount := sdk.NewCoins(
85+
sdk.NewInt64Coin("ibc/F082B65C88E4B6D5EF1DB243CDA1D331D002759E938A0F5CD3FFDC5D53B3E349", 85_000_000), //USDC
86+
sdk.NewInt64Coin("ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9", 1_036_566_310), // ATOM
87+
sdk.NewInt64Coin("ibc/8BFE59DCD5A7054F0A97CF91F3E3ABCA8C5BA454E548FA512B729D4004584D47", 3_000_000), // NTRN
88+
sdk.NewCoin("ibc/8464A63954C0350A26C8588E20719F3A0AC8705E4CA0F7450B60C3F16B2D3421", math.LegacyMustNewDecFromStr("10000000000000000000").TruncateInt()), // XRP
89+
sdk.NewCoin("ibc/ADF401C952ADD9EE232D52C8303B8BE17FE7953C8D420F20769AF77240BD0C58", math.LegacyMustNewDecFromStr("51059039885106925").TruncateInt()), // INJ
90+
sdk.NewInt64Coin("ibc/45D6B52CAD911A15BD9C2F5FFDA80E26AFCB05C7CD520070790ABC86D2B24229", 9_960_000), // TIA
91+
)
92+
if ctx.ChainID() == "elysicstestnet-1" {
93+
sender = sdk.MustAccAddressFromBech32("elys1jeqlq99ustyug8rxgsrtmf3awzl83x535v3svykfwkkkr7049wxqgdt6ss")
94+
amount = sdk.NewCoins(sdk.NewInt64Coin("ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9", 100000000), sdk.NewInt64Coin("uelys", 1000000000))
95+
}
96+
cacheCtx, write := ctx.CacheContext()
97+
err := app.BankKeeper.SendCoins(cacheCtx, sender, receiver, amount)
98+
if err == nil {
99+
write()
100+
}
72101

73102
return vm, vmErr
74103
},

proto/elys/perpetual/params.proto

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,14 @@ message Params {
130130
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
131131
(gogoproto.nullable) = false
132132
];
133+
string second_liquidation_trigger_ratio = 26 [
134+
(cosmos_proto.scalar) = "cosmos.Dec",
135+
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
136+
(gogoproto.nullable) = false
137+
];
138+
string first_liquidation_closing_ratio = 27 [
139+
(cosmos_proto.scalar) = "cosmos.Dec",
140+
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
141+
(gogoproto.nullable) = false
142+
];
133143
}

proto/elys/perpetual/query.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,11 @@ message PoolResponse {
389389
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
390390
(gogoproto.nullable) = false
391391
];
392+
string mtp_safety_factor = 16 [
393+
(cosmos_proto.scalar) = "cosmos.Dec",
394+
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
395+
(gogoproto.nullable) = false
396+
];
392397
}
393398

394399
message QueryCloseEstimationRequest {

proto/elys/perpetual/types.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ message MTP {
8686
uint64 last_interest_calc_block = 23;
8787
uint64 last_funding_calc_time = 24;
8888
uint64 last_funding_calc_block = 25;
89+
bool partial_liquidation_done = 26;
8990
}
9091

9192
message InterestBlock {
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package keeper
2+
3+
import (
4+
errorsmod "cosmossdk.io/errors"
5+
"cosmossdk.io/math"
6+
"errors"
7+
"fmt"
8+
sdk "github.com/cosmos/cosmos-sdk/types"
9+
ammtypes "github.com/elys-network/elys/v7/x/amm/types"
10+
assetprofiletypes "github.com/elys-network/elys/v7/x/assetprofile/types"
11+
ptypes "github.com/elys-network/elys/v7/x/parameter/types"
12+
"github.com/elys-network/elys/v7/x/perpetual/types"
13+
)
14+
15+
func (k Keeper) AddCollateral(ctx sdk.Context, mtp *types.MTP, pool *types.Pool, collateral sdk.Coin, ammPool *ammtypes.Pool) (sdk.Coin, error) {
16+
entry, found := k.assetProfileKeeper.GetEntry(ctx, ptypes.BaseCurrency)
17+
if !found {
18+
return sdk.Coin{}, errorsmod.Wrapf(assetprofiletypes.ErrAssetProfileNotFound, "asset %s not found", ptypes.BaseCurrency)
19+
}
20+
baseCurrency := entry.Denom
21+
22+
if mtp.Position == types.Position_LONG && mtp.CollateralAsset == baseCurrency {
23+
if collateral.Denom != mtp.CollateralAsset {
24+
return sdk.Coin{}, errors.New("denom not same as collateral asset")
25+
}
26+
27+
if collateral.Denom != mtp.LiabilitiesAsset {
28+
return sdk.Coin{}, errors.New("denom not same as liabilities asset")
29+
}
30+
31+
creator := mtp.GetAccountAddress()
32+
33+
// interest amount has been paid from custody
34+
params := k.GetParams(ctx)
35+
maxAmount := mtp.Liabilities.Sub(params.LongMinimumLiabilityAmount)
36+
if !maxAmount.IsPositive() {
37+
return sdk.Coin{}, fmt.Errorf("cannot reduce liabilties less than %s", params.LongMinimumLiabilityAmount.String())
38+
}
39+
40+
var finalAmount math.Int
41+
if collateral.Amount.LT(maxAmount) {
42+
finalAmount = collateral.Amount
43+
} else {
44+
finalAmount = maxAmount
45+
}
46+
47+
mtp.Liabilities = mtp.Liabilities.Sub(finalAmount)
48+
err := pool.UpdateLiabilities(mtp.LiabilitiesAsset, finalAmount, false, mtp.Position)
49+
if err != nil {
50+
return sdk.Coin{}, err
51+
}
52+
53+
mtp.Collateral = mtp.Collateral.Add(finalAmount)
54+
err = pool.UpdateCollateral(mtp.CollateralAsset, finalAmount, true, mtp.Position)
55+
if err != nil {
56+
return sdk.Coin{}, err
57+
}
58+
59+
finalCollateralCoin := sdk.NewCoin(collateral.Denom, finalAmount)
60+
err = k.SendToAmmPool(ctx, creator, ammPool, sdk.NewCoins(finalCollateralCoin))
61+
if err != nil {
62+
return sdk.Coin{}, err
63+
}
64+
65+
err = k.SetMTP(ctx, mtp)
66+
if err != nil {
67+
return sdk.Coin{}, err
68+
}
69+
k.SetPool(ctx, *pool)
70+
71+
if k.hooks != nil {
72+
err = k.hooks.AfterPerpetualPositionModified(ctx, *ammPool, *pool, creator)
73+
if err != nil {
74+
return sdk.Coin{}, err
75+
}
76+
}
77+
return finalCollateralCoin, nil
78+
79+
} else {
80+
msgOpen := types.MsgOpen{
81+
Creator: mtp.Address,
82+
Position: mtp.Position,
83+
Leverage: math.LegacyZeroDec(),
84+
Collateral: collateral,
85+
TakeProfitPrice: math.LegacyZeroDec(),
86+
StopLossPrice: math.LegacyZeroDec(),
87+
PoolId: mtp.AmmPoolId,
88+
}
89+
if err := msgOpen.ValidateBasic(); err != nil {
90+
return sdk.Coin{}, err
91+
}
92+
_, err := k.Open(ctx, &msgOpen)
93+
if err != nil {
94+
return sdk.Coin{}, err
95+
}
96+
return sdk.Coin{}, nil
97+
}
98+
}

x/perpetual/keeper/close_position.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,17 @@ func (k Keeper) ClosePosition(ctx sdk.Context, msg *types.MsgClose) (types.MTP,
3434
}
3535

3636
// this also handles edge case where bot is unable to close position in time.
37-
repayAmt, returnAmt, fundingFeeAmt, fundingAmtDistributed, interestAmt, insuranceAmt, allInterestsPaid, forceClosed, perpetualFeesCoins, closingPrice, err := k.MTPTriggerChecksAndUpdates(ctx, &mtp, &pool, &ammPool)
37+
repayAmt, returnAmt, fundingFeeAmt, fundingAmtDistributed, interestAmt, insuranceAmt, allInterestsPaid, forceClosed, perpetualFeesCoins, closingPrice, closingRatio, err := k.MTPTriggerChecksAndUpdates(ctx, &mtp, &pool, &ammPool)
3838
if err != nil {
3939
return types.MTP{}, math.ZeroInt(), math.LegacyZeroDec(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), false, false, zeroPerpFees, math.LegacyZeroDec(), initialCollateral, initialCustody, initialLiabilities, err
4040
}
4141

4242
if forceClosed {
43-
return mtp, repayAmt, math.LegacyOneDec(), returnAmt, fundingFeeAmt, fundingAmtDistributed, interestAmt, insuranceAmt, allInterestsPaid, forceClosed, perpetualFeesCoins, closingPrice, initialCollateral, initialCustody, initialLiabilities, nil
43+
return mtp, repayAmt, closingRatio, returnAmt, fundingFeeAmt, fundingAmtDistributed, interestAmt, insuranceAmt, allInterestsPaid, forceClosed, perpetualFeesCoins, closingPrice, initialCollateral, initialCustody, initialLiabilities, nil
4444
}
4545

46-
// Should be declared after SettleMTPBorrowInterestUnpaidLiability and settling funding
47-
closingRatio := msg.Amount.ToLegacyDec().Quo(mtp.Custody.ToLegacyDec())
46+
// Should be reset after MTPTriggerChecksAndUpdates
47+
closingRatio = msg.Amount.ToLegacyDec().Quo(mtp.Custody.ToLegacyDec())
4848
if mtp.Position == types.Position_SHORT {
4949
closingRatio = msg.Amount.ToLegacyDec().Quo(mtp.Liabilities.ToLegacyDec())
5050
}
@@ -53,7 +53,7 @@ func (k Keeper) ClosePosition(ctx sdk.Context, msg *types.MsgClose) (types.MTP,
5353
}
5454

5555
// Estimate swap and repay
56-
repayAmt, returnAmt, perpFees, closingPrice, err := k.EstimateAndRepay(ctx, &mtp, &pool, &ammPool, closingRatio)
56+
repayAmt, returnAmt, perpFees, closingPrice, _, err := k.EstimateAndRepay(ctx, &mtp, &pool, &ammPool, closingRatio, false)
5757
if err != nil {
5858
return mtp, math.ZeroInt(), math.LegacyZeroDec(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), allInterestsPaid, forceClosed, perpetualFeesCoins, math.LegacyZeroDec(), initialCollateral, initialCustody, initialLiabilities, err
5959
}

x/perpetual/keeper/close_test.go

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -136,86 +136,6 @@ func (suite *PerpetualKeeperTestSuite) TestClose() {
136136
"",
137137
math.NewInt(30), // less than at the same price
138138
},
139-
{
140-
"Close at take profit price",
141-
func() *types.MsgClose {
142-
suite.ResetSuite()
143-
144-
addr := suite.AddAccounts(1, nil)
145-
positionCreator := addr[0]
146-
_, _, ammPool := suite.SetPerpetualPool(1)
147-
tradingAssetPrice, _, err := suite.app.PerpetualKeeper.GetAssetPriceAndAssetUsdcDenomRatio(suite.ctx, ptypes.ATOM)
148-
suite.Require().NoError(err)
149-
openPositionMsg := &types.MsgOpen{
150-
Creator: positionCreator.String(),
151-
Leverage: math.LegacyNewDec(2),
152-
Position: types.Position_LONG,
153-
PoolId: ammPool.PoolId,
154-
Collateral: sdk.NewCoin(ptypes.BaseCurrency, math.NewInt(1000)),
155-
TakeProfitPrice: tradingAssetPrice.MulInt64(4),
156-
StopLossPrice: math.LegacyZeroDec(),
157-
}
158-
159-
position, err := suite.app.PerpetualKeeper.Open(suite.ctx, openPositionMsg)
160-
suite.Require().NoError(err)
161-
162-
suite.app.OracleKeeper.SetPrice(suite.ctx, oracletypes.Price{
163-
Asset: "ATOM",
164-
Price: tradingAssetPrice.MulInt64(4),
165-
Provider: oracleProvider.String(),
166-
Timestamp: uint64(suite.ctx.BlockTime().Unix()),
167-
})
168-
169-
return &types.MsgClose{
170-
Creator: positionCreator.String(),
171-
Id: position.Id,
172-
Amount: math.NewInt(699),
173-
PoolId: ammPool.PoolId,
174-
}
175-
},
176-
"",
177-
math.NewInt(91),
178-
},
179-
{
180-
"Close at stopLoss price",
181-
func() *types.MsgClose {
182-
suite.ResetSuite()
183-
184-
addr := suite.AddAccounts(1, nil)
185-
positionCreator := addr[0]
186-
_, _, ammPool := suite.SetPerpetualPool(1)
187-
tradingAssetPrice, _, err := suite.app.PerpetualKeeper.GetAssetPriceAndAssetUsdcDenomRatio(suite.ctx, ptypes.ATOM)
188-
suite.Require().NoError(err)
189-
openPositionMsg := &types.MsgOpen{
190-
Creator: positionCreator.String(),
191-
Leverage: math.LegacyNewDec(2),
192-
Position: types.Position_LONG,
193-
PoolId: ammPool.PoolId,
194-
Collateral: sdk.NewCoin(ptypes.BaseCurrency, math.NewInt(1000)),
195-
TakeProfitPrice: tradingAssetPrice.MulInt64(4),
196-
StopLossPrice: math.LegacyMustNewDecFromStr("2.0"),
197-
}
198-
199-
position, err := suite.app.PerpetualKeeper.Open(suite.ctx, openPositionMsg)
200-
suite.Require().NoError(err)
201-
202-
suite.app.OracleKeeper.SetPrice(suite.ctx, oracletypes.Price{
203-
Asset: "ATOM",
204-
Price: math.LegacyMustNewDecFromStr("2.0"),
205-
Provider: oracleProvider.String(),
206-
Timestamp: uint64(suite.ctx.BlockTime().Unix()),
207-
})
208-
209-
return &types.MsgClose{
210-
Creator: positionCreator.String(),
211-
Id: position.Id,
212-
Amount: math.NewInt(699),
213-
PoolId: ammPool.PoolId,
214-
}
215-
},
216-
"",
217-
math.NewInt(501),
218-
},
219139
{
220140
"Success: close long position,at same price as open price",
221141
func() *types.MsgClose {

0 commit comments

Comments
 (0)