Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Margin consolidation #192

Merged
merged 11 commits into from
Sep 15, 2023
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
Loading