Skip to content

Commit

Permalink
fixing funding rate for collateral amt LONG same as trading asset (#888)
Browse files Browse the repository at this point in the history
* fixing funding rate for collateral amt LONG same as trading asset

* Adding check for minimum custody amount

* improving code structure
  • Loading branch information
avkr003 authored Oct 28, 2024
1 parent d5d5f24 commit 9aec191
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 85 deletions.
4 changes: 4 additions & 0 deletions proto/elys/perpetual/pool.proto
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ message PoolAsset {
(gogoproto.nullable) = false
];
string asset_denom = 5;
string collateral = 6 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}

message Pool {
Expand Down
2 changes: 1 addition & 1 deletion x/leveragelp/types/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func NewPool(poolId uint64, maxLeverage math.LegacyDec) Pool {
AmmPoolId: poolId,
Health: sdk.NewDec(100),
LeveragedLpAmount: sdk.ZeroInt(),
LeverageMax: sdk.NewDec(10),
LeverageMax: maxLeverage,
MaxLeveragelpRatio: sdk.MustNewDecFromStr("0.6"),
}
}
Expand Down
10 changes: 9 additions & 1 deletion x/perpetual/keeper/begin_blocker.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,15 @@ func (k Keeper) ComputeFundingRate(ctx sdk.Context, pool types.Pool) (sdk.Dec, s
// popular_rate = fixed_rate * abs(Custody-Liability) / (Custody+Liability)
totalCustodyLong := sdk.ZeroInt()
for _, asset := range pool.PoolAssetsLong {
totalCustodyLong = totalCustodyLong.Add(asset.Custody)
// We subtract asset.Collateral from totalCustodyLong because for long with collateral same as trading asset and user will
// be charged for that the collateral as well even though they have already given that amount to the pool.
// For LONG, asset.Custody will be 0 only for base currency but asset.Collateral won't be zero for base currency and trading asset
// We subtract asset.Collateral only when asset is trading asset and in that case asset.Custody won't be zero
// For base currency, asset.Collateral might not be 0 but asset.Custody will be 0 in LONG
// !asset.Custody.IsZero() ensures that asset is trading asset for LONG
if !asset.Custody.IsZero() {
totalCustodyLong = totalCustodyLong.Add(asset.Custody).Sub(asset.Collateral)
}
}

totalLiabilitiesShort := sdk.ZeroInt()
Expand Down
2 changes: 0 additions & 2 deletions x/perpetual/keeper/close_position.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ func (k Keeper) ClosePosition(ctx sdk.Context, msg *types.MsgClose, baseCurrency
return nil, math.ZeroInt(), err
}

// it get sets in KV store in Repay function
mtp.Collateral = mtp.Collateral.ToLegacyDec().Mul(math.LegacyOneDec().Sub(closingRatio)).TruncateInt()
// Estimate swap and repay
repayAmt, err := k.EstimateAndRepay(ctx, &mtp, &pool, &ammPool, baseCurrency, closingRatio)
if err != nil {
Expand Down
19 changes: 9 additions & 10 deletions x/perpetual/keeper/hooks_amm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package keeper

import (
"cosmossdk.io/math"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
ammtypes "github.com/elys-network/elys/x/amm/types"
)
Expand Down Expand Up @@ -40,9 +39,9 @@ func (h AmmHooks) AfterJoinPool(ctx sdk.Context, sender sdk.AccAddress, ammPool
return err
}

params := h.k.GetParams(ctx)
if perpetualPool.Health.LT(params.PoolOpenThreshold) {
return fmt.Errorf("perpetual pool health (%d) got too low", ammPool.PoolId)
err = h.k.CheckLowPoolHealth(ctx, ammPool.PoolId)
if err != nil {
return err
}

return nil
Expand All @@ -62,9 +61,9 @@ func (h AmmHooks) AfterExitPool(ctx sdk.Context, sender sdk.AccAddress, ammPool
return err
}

params := h.k.GetParams(ctx)
if perpetualPool.Health.LT(params.PoolOpenThreshold) {
return fmt.Errorf("perpetual pool health (%d) got too low", ammPool.PoolId)
err = h.k.CheckLowPoolHealth(ctx, ammPool.PoolId)
if err != nil {
return err
}

return nil
Expand All @@ -83,9 +82,9 @@ func (h AmmHooks) AfterSwap(ctx sdk.Context, sender sdk.AccAddress, ammPool ammt
return err
}

params := h.k.GetParams(ctx)
if perpetualPool.Health.LT(params.PoolOpenThreshold) {
return fmt.Errorf("perpetual pool health (%d) got too low", ammPool.PoolId)
err = h.k.CheckLowPoolHealth(ctx, ammPool.PoolId)
if err != nil {
return err
}

return nil
Expand Down
56 changes: 40 additions & 16 deletions x/perpetual/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,13 @@ func (k Keeper) Borrow(ctx sdk.Context, collateralAmount math.Int, custodyAmount
}
mtp.MtpHealth = h

ammPoolAddr, err := sdk.AccAddressFromBech32(ammPool.Address)
if err != nil {
return err
}

collateralCoins := sdk.NewCoins(collateralCoin)
err = k.bankKeeper.SendCoins(ctx, senderAddress, ammPoolAddr, collateralCoins)
err = k.SendToAmmPool(ctx, senderAddress, ammPool, collateralCoins)
if err != nil {
return err
}
err = k.amm.AddToPoolBalance(ctx, ammPool, math.ZeroInt(), collateralCoins)

err = pool.UpdateCollateral(mtp.CollateralAsset, mtp.Collateral, true, mtp.Position)
if err != nil {
return err
}
Expand Down Expand Up @@ -171,6 +167,42 @@ func (k Keeper) Borrow(ctx sdk.Context, collateralAmount math.Int, custodyAmount
return k.SetMTP(ctx, mtp)
}

func (k Keeper) SendToAmmPool(ctx sdk.Context, senderAddress sdk.AccAddress, ammPool *ammtypes.Pool, coins sdk.Coins) error {
ammPoolAddr, err := sdk.AccAddressFromBech32(ammPool.Address)
if err != nil {
return err
}

err = k.bankKeeper.SendCoins(ctx, senderAddress, ammPoolAddr, coins)
if err != nil {
return err
}
err = k.amm.AddToPoolBalance(ctx, ammPool, math.ZeroInt(), coins)
if err != nil {
return err
}

return nil
}

func (k Keeper) SendFromAmmPool(ctx sdk.Context, ammPool *ammtypes.Pool, receiverAddress sdk.AccAddress, coins sdk.Coins) error {
ammPoolAddr, err := sdk.AccAddressFromBech32(ammPool.Address)
if err != nil {
return err
}

err = k.bankKeeper.SendCoins(ctx, ammPoolAddr, receiverAddress, coins)
if err != nil {
return err
}
err = k.amm.RemoveFromPoolBalance(ctx, ammPool, math.ZeroInt(), coins)
if err != nil {
return err
}

return nil
}

func (k Keeper) TakeInCustody(ctx sdk.Context, mtp types.MTP, pool *types.Pool) error {
err := pool.UpdateCustody(mtp.CustodyAsset, mtp.Custody, true, mtp.Position)
if err != nil {
Expand Down Expand Up @@ -263,18 +295,10 @@ func (k Keeper) TakeFundPayment(ctx sdk.Context, amount math.Int, returnAsset st
if !takeAmount.IsZero() {
takeCoins := sdk.NewCoins(sdk.NewCoin(returnAsset, takeAmount))

ammPoolAddr, err := sdk.AccAddressFromBech32(ammPool.Address)
err := k.SendFromAmmPool(ctx, ammPool, fundAddr, takeCoins)
if err != nil {
return sdk.ZeroInt(), err
}
err = k.bankKeeper.SendCoins(ctx, ammPoolAddr, fundAddr, takeCoins)
if err != nil {
return sdk.ZeroInt(), err
}
err = k.amm.RemoveFromPoolBalance(ctx, ammPool, math.ZeroInt(), takeCoins)
if err != nil {
return math.ZeroInt(), err
}

}
return takeAmount, nil
Expand Down
19 changes: 12 additions & 7 deletions x/perpetual/keeper/open.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,24 @@ func (k Keeper) Open(ctx sdk.Context, msg *types.MsgOpen, isBroker bool) (*types
//}
}

if err := k.CheckUserAuthorization(ctx, msg); err != nil {
if err = k.CheckUserAuthorization(ctx, msg); err != nil {
return nil, err
}

// check if existing mtp to consolidate
existingMtp := k.CheckSameAssetPosition(ctx, msg)

if existingMtp == nil && msg.Leverage.Equal(math.LegacyOneDec()) {
return nil, fmt.Errorf("cannot open new position with leverage 1")
}

if err := k.CheckMaxOpenPositions(ctx); err != nil {
return nil, err
if existingMtp == nil {
if msg.Leverage.Equal(math.LegacyOneDec()) {
return nil, fmt.Errorf("cannot open new position with leverage 1")
}
// Check if max positions are exceeded as we are opening new position, not updating old position
if err = k.CheckMaxOpenPositions(ctx); err != nil {
return nil, err
}
} else if msg.Leverage.Equal(math.LegacyOneDec()) {
// Enforce collateral addition (for leverage 1) without modifying take profit price
msg.TakeProfitPrice = existingMtp.TakeProfitPrice
}

poolId := msg.PoolId
Expand Down
27 changes: 26 additions & 1 deletion x/perpetual/keeper/pool_health.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
errorsmod "cosmossdk.io/errors"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
ammtypes "github.com/elys-network/elys/x/amm/types"
"github.com/elys-network/elys/x/perpetual/types"
Expand All @@ -13,9 +14,14 @@ func (k Keeper) CheckLowPoolHealth(ctx sdk.Context, poolId uint64) error {
return errorsmod.Wrapf(types.ErrPoolDoesNotExist, "pool id %d", poolId)
}

if !pool.Health.IsNil() && pool.Health.LTE(k.GetPoolOpenThreshold(ctx)) {
minimumThreshold := k.GetPoolOpenThreshold(ctx)
if !pool.Health.IsNil() && pool.Health.LTE(minimumThreshold) {
return errorsmod.Wrapf(types.ErrInvalidPosition, "pool (%d) health too low to open new positions", poolId)
}
err := k.CheckMinimumCustodyAmt(ctx, poolId)
if err != nil {
return err
}
return nil
}

Expand Down Expand Up @@ -60,3 +66,22 @@ func (k Keeper) UpdatePoolHealth(ctx sdk.Context, pool *types.Pool) error {

return nil
}

// CheckMinimumCustodyAmt Should be called after opening positions and when real pool balance changes
func (k Keeper) CheckMinimumCustodyAmt(ctx sdk.Context, poolId uint64) error {
pool, found := k.GetPool(ctx, poolId)
if !found {
return errorsmod.Wrapf(types.ErrPoolDoesNotExist, "pool id %d", poolId)
}
ammPool, err := k.GetAmmPool(ctx, pool.AmmPoolId)
if err != nil {
return err
}
for _, ammPoolAsset := range ammPool.PoolAssets {
_, totalCustody, _, _ := pool.GetPerpetualPoolBalances(ammPoolAsset.Token.Denom)
if ammPoolAsset.Token.Amount.LT(totalCustody) {
return fmt.Errorf("real amm pool (id: %d) balance (%s) is less than total custody (%s)", poolId, ammPoolAsset.Token.String(), totalCustody.String())
}
}
return nil
}
21 changes: 10 additions & 11 deletions x/perpetual/keeper/repay.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,7 @@ import (
func (k Keeper) Repay(ctx sdk.Context, mtp *types.MTP, pool *types.Pool, ammPool *ammtypes.Pool, returnAmount math.Int, payingLiabilities math.Int, closingRatio sdk.Dec) error {
if returnAmount.IsPositive() {
returnCoins := sdk.NewCoins(sdk.NewCoin(mtp.CustodyAsset, returnAmount))
ammPoolAddr, err := sdk.AccAddressFromBech32(ammPool.Address)
if err != nil {
return err
}

err = k.bankKeeper.SendCoins(ctx, ammPoolAddr, mtp.GetAccountAddress(), returnCoins)
if err != nil {
return err
}
err = k.amm.RemoveFromPoolBalance(ctx, ammPool, math.ZeroInt(), returnCoins)
err := k.SendFromAmmPool(ctx, ammPool, mtp.GetAccountAddress(), returnCoins)
if err != nil {
return err
}
Expand All @@ -31,7 +22,15 @@ func (k Keeper) Repay(ctx sdk.Context, mtp *types.MTP, pool *types.Pool, ammPool
return err
}

mtp.Custody = mtp.Custody.Sub(mtp.Custody.ToLegacyDec().Mul(closingRatio).TruncateInt())
reducingCollateralAmt := closingRatio.Mul(mtp.Collateral.ToLegacyDec()).TruncateInt()
err = pool.UpdateCollateral(mtp.CollateralAsset, reducingCollateralAmt, false, mtp.Position)
if err != nil {
return err
}

// Custody = Custody * (1 - closingRatio)
mtp.Custody = mtp.Custody.ToLegacyDec().Mul(math.LegacyOneDec().Sub(closingRatio)).TruncateInt()
mtp.Collateral = mtp.Collateral.Sub(reducingCollateralAmt)

oldTakeProfitCustody := mtp.TakeProfitCustody
mtp.TakeProfitCustody = mtp.TakeProfitCustody.Sub(mtp.TakeProfitCustody.ToLegacyDec().Mul(closingRatio).TruncateInt())
Expand Down
11 changes: 11 additions & 0 deletions x/perpetual/migrations/v11_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ import (
)

func (m Migrator) V11Migration(ctx sdk.Context) error {
mtps := m.keeper.GetAllMTPs(ctx)
for _, mtp := range mtps {
ammPool, err := m.keeper.GetAmmPool(ctx, mtp.AmmPoolId)
if err != nil {
return err
}
err = m.keeper.SendFromAmmPool(ctx, &ammPool, mtp.GetAccountAddress(), sdk.NewCoins(sdk.NewCoin(mtp.CollateralAsset, mtp.Collateral)))
if err != nil {
return err
}
}
m.keeper.NukeDB(ctx)
params := types.DefaultParams()
err := m.keeper.SetParams(ctx, &params)
Expand Down
16 changes: 16 additions & 0 deletions x/perpetual/types/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@ func (p *Pool) UpdateLiabilities(assetDenom string, amount math.Int, isIncrease
return nil
}

// Update the asset collateral
func (p *Pool) UpdateCollateral(assetDenom string, amount math.Int, isIncrease bool, position Position) error {
poolAsset := p.GetPoolAsset(position, assetDenom)
if poolAsset == nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, "invalid asset denom")
}

if isIncrease {
poolAsset.Collateral = poolAsset.Collateral.Add(amount)
} else {
poolAsset.Collateral = poolAsset.Collateral.Sub(amount)
}

return nil
}

// Update the asset take profit liabilities
func (p *Pool) UpdateTakeProfitLiabilities(assetDenom string, amount math.Int, isIncrease bool, position Position) error {
poolAsset := p.GetPoolAsset(position, assetDenom)
Expand Down
Loading

0 comments on commit 9aec191

Please sign in to comment.