Skip to content

Commit

Permalink
Margin consolidation (#192)
Browse files Browse the repository at this point in the history
* fix: conflict with main

* chore: update same asset position checker

* chore: multiple assets per position close, handle interest

* chore: update open consolidate long to manage assets

* fix: go mod that breaks build

* fix: unit tests in close long, short and open long

* fix: nullify for mtp position initialization and compare

* fix: remove nullify in preparing mtp testdata

* refactor: additional short logic + test fix

* fix: unit margin unit test failure

---------

Co-authored-by: Cosmic Vagabond <121588426+cosmic-vagabond@users.noreply.github.com>
  • Loading branch information
kenta-elys and cosmic-vagabond authored Sep 15, 2023
1 parent 93225cf commit efafc3e
Show file tree
Hide file tree
Showing 46 changed files with 3,051 additions and 813 deletions.
468 changes: 324 additions & 144 deletions docs/static/openapi.yml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ require (
go.etcd.io/bbolt v1.3.7 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc
golang.org/x/net v0.12.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/sys v0.10.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions proto/elys/margin/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ message MsgOpenResponse {}
message MsgClose {
string creator = 1;
uint64 id = 2;
string collateralAsset = 3;
string custodyAsset = 4;
}

message MsgCloseResponse {}
Expand Down
24 changes: 16 additions & 8 deletions proto/elys/margin/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,33 @@ enum Position {

message MTP {
string address = 1;
string collateral_asset = 2;
string collateral_amount = 3 [
repeated string collateral_assets = 2;
repeated string collateral_amounts = 3 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
string liabilities = 4 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
string interest_paid_collateral = 5 [
repeated string interest_paid_collaterals = 5 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
string interest_paid_custody = 6 [
repeated string interest_paid_custodys = 6 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
string interest_unpaid_collateral = 7 [
repeated string interest_unpaid_collaterals = 7 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
string custody_asset = 8;
string custody_amount = 9 [
repeated string custody_assets = 8;
repeated string custody_amounts = 9 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
string leverage = 10 [
repeated string leverages = 10 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
Expand All @@ -51,6 +51,14 @@ message MTP {
Position position = 12;
uint64 id = 13;
uint64 amm_pool_id = 14;
string consolidate_leverage = 15 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
string sum_collateral = 16 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}

message WhiteList {
Expand Down
37 changes: 19 additions & 18 deletions x/margin/client/cli/query_mtp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
simapp "github.com/elys-network/elys/app"
"github.com/elys-network/elys/testutil/network"
"github.com/elys-network/elys/testutil/nullify"
"github.com/elys-network/elys/x/margin/client/cli"
"github.com/elys-network/elys/x/margin/types"
paramtypes "github.com/elys-network/elys/x/parameter/types"
Expand All @@ -37,22 +36,24 @@ func networkWithMTPObjects(t *testing.T, n int) (*network.Network, []*types.MTP)
cfg := network.DefaultConfig()
for i := 0; i < n; i++ {
mtp := types.MTP{
Address: addr[i].String(),
CollateralAsset: paramtypes.USDC,
CollateralAmount: sdk.NewInt(0),
Liabilities: sdk.NewInt(0),
InterestPaidCollateral: sdk.NewInt(0),
InterestPaidCustody: sdk.NewInt(0),
InterestUnpaidCollateral: sdk.NewInt(0),
CustodyAsset: "ATOM",
CustodyAmount: sdk.NewInt(0),
Leverage: sdk.NewDec(0),
MtpHealth: sdk.NewDec(0),
Position: types.Position_LONG,
Id: (uint64)(i + 1),
AmmPoolId: (uint64)(i + 1),
Address: addr[i].String(),
CollateralAssets: []string{paramtypes.USDC},
CollateralAmounts: []sdk.Int{sdk.NewInt(0)},
Liabilities: sdk.NewInt(0),
InterestPaidCollaterals: []sdk.Int{sdk.NewInt(0)},
InterestPaidCustodys: []sdk.Int{sdk.NewInt(0)},
InterestUnpaidCollaterals: []sdk.Int{sdk.NewInt(0)},
CustodyAssets: []string{"ATOM"},
CustodyAmounts: []sdk.Int{sdk.NewInt(0)},
Leverages: []sdk.Dec{sdk.NewDec(0)},
MtpHealth: sdk.NewDec(0),
Position: types.Position_LONG,
Id: (uint64)(i + 1),
AmmPoolId: (uint64)(i + 1),
ConsolidateLeverage: sdk.ZeroDec(),
SumCollateral: sdk.ZeroInt(),
}
nullify.Fill(&mtp)

mtps = append(mtps, &mtp)
state.MtpList = append(state.MtpList, mtp)
}
Expand Down Expand Up @@ -115,8 +116,8 @@ func TestShowMTP(t *testing.T) {
require.NoError(t, net.Config.Codec.UnmarshalJSON(out.Bytes(), &resp))
require.NotNil(t, resp.Mtp)
require.Equal(t,
nullify.Fill(&tc.obj),
nullify.Fill(resp.Mtp),
tc.obj,
resp.Mtp,
)
}
})
Expand Down
34 changes: 34 additions & 0 deletions x/margin/keeper/calc_mtp_consolidate_collateral.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/elys-network/elys/x/margin/types"
ptypes "github.com/elys-network/elys/x/parameter/types"
)

func (k Keeper) CalcMTPConsolidateCollateral(ctx sdk.Context, mtp *types.MTP) error {
consolidateCollateral := sdk.ZeroInt()
for i, asset := range mtp.CollateralAssets {
if asset == ptypes.USDC {
consolidateCollateral = consolidateCollateral.Add(mtp.CollateralAmounts[i])
} else {
// swap into usdc
_, ammPool, _, err := k.OpenChecker.PreparePools(ctx, asset)
if err != nil {
return err
}

collateralAmtIn := sdk.NewCoin(asset, mtp.CollateralAmounts[i])
C, err := k.EstimateSwapGivenOut(ctx, collateralAmtIn, ptypes.USDC, ammPool)
if err != nil {
return err
}

consolidateCollateral = consolidateCollateral.Add(C)
}
}

mtp.SumCollateral = consolidateCollateral

return nil
}
15 changes: 15 additions & 0 deletions x/margin/keeper/calc_mtp_consolidate_leverage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/elys-network/elys/x/margin/types"
)

func (k Keeper) CalcMTPConsolidateLiability(ctx sdk.Context, mtp *types.MTP) {
if mtp.SumCollateral.IsZero() {
return
}

leverage := mtp.Liabilities.Quo(mtp.SumCollateral)
mtp.ConsolidateLeverage = sdk.NewDecFromInt(leverage)
}
24 changes: 21 additions & 3 deletions x/margin/keeper/calc_mtp_interest_liabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,33 @@ import (
"math/big"

sdk "github.com/cosmos/cosmos-sdk/types"
ammtypes "github.com/elys-network/elys/x/amm/types"
"github.com/elys-network/elys/x/margin/types"
ptypes "github.com/elys-network/elys/x/parameter/types"
)

func (k Keeper) CalcMTPInterestLiabilities(mtp *types.MTP, interestRate sdk.Dec, epochPosition, epochLength int64) sdk.Int {
func (k Keeper) CalcMTPInterestLiabilities(ctx sdk.Context, mtp *types.MTP, interestRate sdk.Dec, epochPosition, epochLength int64, ammPool ammtypes.Pool, collateralAsset string) sdk.Int {
var interestRational, liabilitiesRational, rate, epochPositionRational, epochLengthRational big.Rat

rate.SetFloat64(interestRate.MustFloat64())

liabilitiesRational.SetInt(mtp.Liabilities.BigInt().Add(mtp.Liabilities.BigInt(), mtp.InterestUnpaidCollateral.BigInt()))
collateralIndex, _ := k.GetMTPAssetIndex(mtp, collateralAsset, "")
unpaidCollaterals := sdk.ZeroInt()
// Calculate collateral interests in usdc
if mtp.CollateralAssets[collateralIndex] == ptypes.USDC {
unpaidCollaterals = unpaidCollaterals.Add(mtp.InterestUnpaidCollaterals[collateralIndex])
} else {
// Liability is in usdc, so convert it to usdc
unpaidCollateralIn := sdk.NewCoin(mtp.CollateralAssets[collateralIndex], mtp.InterestUnpaidCollaterals[collateralIndex])
C, err := k.EstimateSwapGivenOut(ctx, unpaidCollateralIn, ptypes.USDC, ammPool)
if err != nil {
return sdk.ZeroInt()
}

unpaidCollaterals = unpaidCollaterals.Add(C)
}

liabilitiesRational.SetInt(mtp.Liabilities.BigInt().Add(mtp.Liabilities.BigInt(), unpaidCollaterals.BigInt()))
interestRational.Mul(&rate, &liabilitiesRational)

if epochPosition > 0 { // prorate interest if within epoch
Expand All @@ -24,7 +42,7 @@ func (k Keeper) CalcMTPInterestLiabilities(mtp *types.MTP, interestRate sdk.Dec,

interestNew := interestRational.Num().Quo(interestRational.Num(), interestRational.Denom())

interestNewInt := sdk.NewIntFromBigInt(interestNew.Add(interestNew, mtp.InterestUnpaidCollateral.BigInt()))
interestNewInt := sdk.NewIntFromBigInt(interestNew.Add(interestNew, unpaidCollaterals.BigInt()))
// round up to lowest digit if interest too low and rate not 0
if interestNewInt.IsZero() && !interestRate.IsZero() {
interestNewInt = sdk.NewInt(1)
Expand Down
17 changes: 17 additions & 0 deletions x/margin/keeper/check_same_asset_position.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/elys-network/elys/x/margin/types"
)

func (k Keeper) CheckSamePosition(ctx sdk.Context, msg *types.MsgOpen) *types.MTP {
mtps := k.GetAllMTPs(ctx)
for _, mtp := range mtps {
if mtp.Address == msg.Creator && mtp.Position == msg.Position {
return &mtp
}
}

return nil
}
36 changes: 36 additions & 0 deletions x/margin/keeper/check_same_asset_position_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package keeper_test

import (
"testing"

tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
simapp "github.com/elys-network/elys/app"
"github.com/elys-network/elys/x/margin/types"
ptypes "github.com/elys-network/elys/x/parameter/types"
"github.com/stretchr/testify/assert"
)

func TestCheckSameAssets_NewPosition(t *testing.T) {
app := simapp.InitElysTestApp(true)
ctx := app.BaseApp.NewContext(true, tmproto.Header{})

k := app.MarginKeeper

mtp := types.NewMTP("creator", ptypes.USDC, ptypes.ATOM, types.Position_LONG, sdk.NewDec(5), 1)
k.SetMTP(ctx, mtp)

msg := &types.MsgOpen{
Creator: "creator",
CollateralAsset: ptypes.ATOM,
CollateralAmount: sdk.NewInt(100),
BorrowAsset: ptypes.ATOM,
Position: types.Position_SHORT,
Leverage: sdk.NewDec(1),
}

mtp = k.CheckSamePosition(ctx, msg)

// Expect no error
assert.Nil(t, mtp)
}
16 changes: 8 additions & 8 deletions x/margin/keeper/close.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ func (k Keeper) Close(ctx sdk.Context, msg *types.MsgClose) (*types.MsgCloseResp
sdk.NewAttribute("id", strconv.FormatInt(int64(closedMtp.Id), 10)),
sdk.NewAttribute("position", closedMtp.Position.String()),
sdk.NewAttribute("address", closedMtp.Address),
sdk.NewAttribute("collateral_asset", closedMtp.CollateralAsset),
sdk.NewAttribute("collateral_amount", closedMtp.CollateralAmount.String()),
sdk.NewAttribute("custody_asset", closedMtp.CustodyAsset),
sdk.NewAttribute("custody_amount", closedMtp.CustodyAmount.String()),
sdk.NewAttribute("collateral_asset", closedMtp.CollateralAssets[0]),
sdk.NewAttribute("collateral_amount", closedMtp.CollateralAmounts[0].String()),
sdk.NewAttribute("custody_asset", closedMtp.CustodyAssets[0]),
sdk.NewAttribute("custody_amount", closedMtp.CustodyAmounts[0].String()),
sdk.NewAttribute("repay_amount", repayAmount.String()),
sdk.NewAttribute("leverage", closedMtp.Leverage.String()),
sdk.NewAttribute("leverage", closedMtp.Leverages[0].String()),
sdk.NewAttribute("liabilities", closedMtp.Liabilities.String()),
sdk.NewAttribute("interest_paid_collateral", mtp.InterestPaidCollateral.String()),
sdk.NewAttribute("interest_paid_custody", mtp.InterestPaidCustody.String()),
sdk.NewAttribute("interest_unpaid_collateral", closedMtp.InterestUnpaidCollateral.String()),
sdk.NewAttribute("interest_paid_collateral", mtp.InterestPaidCollaterals[0].String()),
sdk.NewAttribute("interest_paid_custody", mtp.InterestPaidCustodys[0].String()),
sdk.NewAttribute("interest_unpaid_collateral", closedMtp.InterestUnpaidCollaterals[0].String()),
sdk.NewAttribute("health", closedMtp.MtpHealth.String()),
))

Expand Down
61 changes: 35 additions & 26 deletions x/margin/keeper/close_long.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,41 @@ func (k Keeper) CloseLong(ctx sdk.Context, msg *types.MsgClose) (*types.MTP, sdk
return nil, sdk.ZeroInt(), sdkerrors.Wrap(types.ErrInvalidBorrowingAsset, "invalid pool id")
}

// Retrieve AmmPool
ammPool, err := k.CloseLongChecker.GetAmmPool(ctx, mtp.AmmPoolId, mtp.CustodyAsset)
if err != nil {
return nil, sdk.ZeroInt(), err
}

// Handle Interest if within epoch position
if err := k.CloseLongChecker.HandleInterest(ctx, &mtp, &pool, ammPool); err != nil {
return nil, sdk.ZeroInt(), err
}

// Take out custody
err = k.CloseLongChecker.TakeOutCustody(ctx, mtp, &pool)
if err != nil {
return nil, sdk.ZeroInt(), err
}

// Estimate swap and repay
repayAmount, err := k.CloseLongChecker.EstimateAndRepay(ctx, mtp, pool, ammPool)
if err != nil {
return nil, sdk.ZeroInt(), err
}

// Hooks after margin position closed
if k.hooks != nil {
k.hooks.AfterMarginPositionClosed(ctx, ammPool, pool)
repayAmount := sdk.ZeroInt()
for _, custodyAsset := range mtp.CustodyAssets {
// Retrieve AmmPool
ammPool, err := k.CloseLongChecker.GetAmmPool(ctx, mtp.AmmPoolId, custodyAsset)
if err != nil {
return nil, sdk.ZeroInt(), err
}

for _, collateralAsset := range mtp.CollateralAssets {
// Handle Interest if within epoch position
if err := k.CloseLongChecker.HandleInterest(ctx, &mtp, &pool, ammPool, collateralAsset, custodyAsset); err != nil {
return nil, sdk.ZeroInt(), err
}
}

// Take out custody
err = k.CloseLongChecker.TakeOutCustody(ctx, mtp, &pool, custodyAsset)
if err != nil {
return nil, sdk.ZeroInt(), err
}

for _, collateralAsset := range mtp.CollateralAssets {
// Estimate swap and repay
repayAmt, err := k.CloseLongChecker.EstimateAndRepay(ctx, mtp, pool, ammPool, collateralAsset, custodyAsset)
if err != nil {
return nil, sdk.ZeroInt(), err
}

repayAmount = repayAmount.Add(repayAmt)
}

// Hooks after margin position closed
if k.hooks != nil {
k.hooks.AfterMarginPositionClosed(ctx, ammPool, pool)
}
}

return &mtp, repayAmount, nil
Expand Down
Loading

0 comments on commit efafc3e

Please sign in to comment.