Skip to content

Commit

Permalink
feat(perp): settle markets (#1573)
Browse files Browse the repository at this point in the history
* feat(perp): impl AMM.SettlementPrice func #wip

* refactor(perp): rename amm.MarkPrice -> amm.InstMarkPrice to reflect that it's instantaneous

* remove unused param

* add settlement

* open position on disabled market

* close market and don't allow to close positions on close market

* add settlement tests part 1

* add get amm

* temp commit for refactoring position to include versioning

* make versioned positions

* add check when position does not exist

* feat: implement setllement price calculation on close market

* fix: changelog

* fix: lint

* fix: lint

---------

Co-authored-by: Jonathan Gimeno <[email protected]>
Co-authored-by: Matthias <[email protected]>
  • Loading branch information
3 people authored Oct 10, 2023
1 parent ef307eb commit a7c9447
Show file tree
Hide file tree
Showing 60 changed files with 8,940 additions and 363 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
transaction messages for CreateDenom, ChangeAdmin, and UpdateModuleParams
* [#1620](https://github.com/NibiruChain/nibiru/pull/1620) - Token factory
transaction messages for Mint and Burn
* [#1573](https://github.com/NibiruChain/nibiru/pull/1573) - feat(perp): Close markets and compute settlement price

### State Machine Breaking

Expand Down
2 changes: 1 addition & 1 deletion contrib/make/lint.mk
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

.PHONY: lint
lint:
docker run -v $(CURDIR):/code --rm -w /code golangci/golangci-lint:v1.52.2-alpine golangci-lint run --timeout 10m
docker run -v $(CURDIR):/code --rm -w /code golangci/golangci-lint:v1.52.2-alpine golangci-lint run --timeout 30m
15 changes: 13 additions & 2 deletions proto/nibiru/perp/v2/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ message GenesisState {

repeated nibiru.perp.v2.AMM amms = 3 [ (gogoproto.nullable) = false ];

repeated nibiru.perp.v2.Position positions = 4
[ (gogoproto.nullable) = false ];
repeated GenesisPosition positions = 4 [ (gogoproto.nullable) = false ];

repeated nibiru.perp.v2.ReserveSnapshot reserve_snapshots = 5
[ (gogoproto.nullable) = false ];
Expand Down Expand Up @@ -71,3 +70,15 @@ message GenesisMarketLastVersion {

uint64 version = 2;
}

message GenesisPosition {
string pair = 1 [
(gogoproto.customtype) =
"github.com/NibiruChain/nibiru/x/common/asset.Pair",
(gogoproto.nullable) = false
];

uint64 version = 2;

Position position = 3 [ (gogoproto.nullable) = false ];
}
7 changes: 7 additions & 0 deletions proto/nibiru/perp/v2/state.proto
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ message AMM {
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];

// The settlement price if the AMM is settled.
string settlement_price = 9 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.stdduration) = true,
(gogoproto.nullable) = false
];
}

message Position {
Expand Down
2 changes: 1 addition & 1 deletion wasmbinding/bindings/marshalling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (s *TestSuiteBindingJsonTypes) TestToAppMarket() {
ammMarket.Market,
ammMarket.Amm,
"index price",
ammMarket.Amm.MarkPrice().String(),
ammMarket.Amm.InstMarkPrice().String(),
dummyBlockHeight,
)

Expand Down
2 changes: 1 addition & 1 deletion wasmbinding/bindings/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func NewMarket(appMarket perpv2types.Market, appAmm perpv2types.AMM, indexPrice,
MaintenanceMarginRatio: appMarket.MaintenanceMarginRatio,
MaxLeverage: appMarket.MaxLeverage,
},
MarkPrice: appAmm.MarkPrice(),
MarkPrice: appAmm.InstMarkPrice(),
IndexPrice: indexPrice,
TwapMark: twapMark,
BlockNumber: sdk.NewInt(blockNumber),
Expand Down
3 changes: 2 additions & 1 deletion wasmbinding/exec_perp.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ func (exec *ExecutorPerp) InsuranceFundWithdraw(
)
}

// TODO: rename to CloseMarket
func (exec *ExecutorPerp) SetMarketEnabled(
cwMsg *bindings.SetMarketEnabled, ctx sdk.Context,
) (err error) {
Expand All @@ -201,7 +202,7 @@ func (exec *ExecutorPerp) SetMarketEnabled(
return err
}

return exec.PerpV2.ChangeMarketEnabledParameter(ctx, pair, cwMsg.Enabled)
return exec.PerpV2.CloseMarket(ctx, pair)
}

func (exec *ExecutorPerp) CreateMarket(
Expand Down
5 changes: 1 addition & 4 deletions wasmbinding/exec_perp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"time"

sdkmath "cosmossdk.io/math"
"github.com/NibiruChain/collections"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"
Expand Down Expand Up @@ -114,9 +113,7 @@ func (s *TestSuitePerpExecutor) DoMarketOrderTest(pair asset.Pair) error {
}

// Verify position exists with PerpKeeper
_, err = s.exec.PerpV2.Positions.Get(
s.ctx, collections.Join(pair, s.contractPerp),
)
_, err = s.exec.PerpV2.GetPosition(s.ctx, pair, 1, s.contractPerp)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion wasmbinding/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func (perpExt *PerpQuerier) AllMarkets(
MaintenanceMarginRatio: pbMarket.Market.MaintenanceMarginRatio,
MaxLeverage: pbMarket.Market.MaxLeverage,
},
MarkPrice: pbMarket.Amm.MarkPrice(),
MarkPrice: pbMarket.Amm.InstMarkPrice(),
BlockNumber: sdk.NewInt(ctx.BlockHeight()),
}
}
Expand Down
2 changes: 1 addition & 1 deletion wasmbinding/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func (s *TestSuiteQuerier) TestQueryAllMarkets() {
s.Assert().EqualValues(marketAmm.Amm.TotalLong, cwMarket.TotalLong)
s.Assert().EqualValues(marketAmm.Amm.TotalShort, cwMarket.TotalShort)
s.Assert().EqualValues(marketAmm.Amm.PriceMultiplier.String(), cwMarket.PegMult.String())
s.Assert().EqualValues(marketAmm.Amm.MarkPrice().String(), cwMarket.MarkPrice.String())
s.Assert().EqualValues(marketAmm.Amm.InstMarkPrice().String(), cwMarket.MarkPrice.String())
s.Assert().EqualValues(s.ctx.BlockHeight(), cwMarket.BlockNumber.Int64())
}
}
Expand Down
4 changes: 2 additions & 2 deletions x/common/testutil/genesis/perp_genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func AddPerpV2Genesis(gen app.GenesisState) app.GenesisState {
Markets: marketsv2,
MarketLastVersions: marketLastVersions,
Amms: ammsv2,
Positions: []perpv2types.Position{},
Positions: []perpv2types.GenesisPosition{},
ReserveSnapshots: []perpv2types.ReserveSnapshot{},
}

Expand Down Expand Up @@ -226,7 +226,7 @@ func PerpV2Genesis() *perpv2types.GenesisState {
TotalShort: sdk.ZeroDec(),
},
},
Positions: []perpv2types.Position{},
Positions: []perpv2types.GenesisPosition{},
ReserveSnapshots: []perpv2types.ReserveSnapshot{},
}
}
1 change: 1 addition & 0 deletions x/common/testutil/mock/perp_amm.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func TestAMMDefault() *types.AMM {
PriceMultiplier: sdk.OneDec(),
TotalLong: sdk.ZeroDec(),
TotalShort: sdk.ZeroDec(),
SettlementPrice: sdk.ZeroDec(),
}
}

Expand Down
16 changes: 6 additions & 10 deletions x/perp/v2/client/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import (
"fmt"
"testing"

"github.com/NibiruChain/collections"
"cosmossdk.io/errors"

abcitypes "github.com/cometbft/cometbft/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
"github.com/stretchr/testify/suite"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/x/common"
Expand Down Expand Up @@ -364,11 +363,8 @@ func (s *IntegrationTestSuite) TestMarketOrdersAndCloseCmd() {
s.T().Log("F. check trader position")
queryResp, err = testutilcli.QueryPositionV2(val.ClientCtx, asset.Registry.Pair(denoms.BTC, denoms.NUSD), user)
s.Error(err)
errors.IsOf(err, types.ErrPositionNotFound)
s.T().Logf("query response: %+v", queryResp)

status, ok := status.FromError(err)
s.True(ok)
s.EqualValues(codes.InvalidArgument, status.Code())
}

func (s *IntegrationTestSuite) TestPartialCloseCmd() {
Expand Down Expand Up @@ -469,7 +465,7 @@ func (s *IntegrationTestSuite) TestPositionEmptyAndClose() {
_, err = s.network.ExecTxCmd(cli.ClosePositionCmd(), user, []string{
asset.Registry.Pair(denoms.ETH, denoms.NUSD).String(),
})
s.Contains(err.Error(), collections.ErrNotFound.Error())
s.Contains(err.Error(), types.ErrPositionNotFound.Error())
}

// user[0] opens a position and removes margin to trigger bad debt
Expand Down Expand Up @@ -540,7 +536,7 @@ func (s *IntegrationTestSuite) TestX_AddMargin() {
asset.Registry.Pair(denoms.BTC, denoms.NUSD).String(),
fmt.Sprintf("10000%s", denoms.NUSD),
},
expectedCode: 1,
expectedCode: types.ErrPositionNotFound.ABCICode(),
expectFail: false,
},
{
Expand Down Expand Up @@ -642,7 +638,7 @@ func (s *IntegrationTestSuite) TestX_RemoveMargin() {
asset.Registry.Pair(denoms.BTC, denoms.NUSD).String(),
fmt.Sprintf("10000%s", denoms.NUSD),
},
expectedCode: 1,
expectedCode: types.ErrPositionNotFound.ABCICode(),
expectFail: false,
},
{
Expand Down
5 changes: 3 additions & 2 deletions x/perp/v2/integration/action/margin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package action

import (
"errors"
"fmt"

sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -68,7 +69,7 @@ func (a addMarginFailAction) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Contex
ctx, a.Pair, a.Account, sdk.NewCoin(a.Pair.QuoteDenom(), a.Margin),
)
if !errors.Is(err, a.ExpectedErr) {
return ctx, err, false
return ctx, fmt.Errorf("expected error %v, got %v", a.ExpectedErr, err), false
}

return ctx, nil, false
Expand Down Expand Up @@ -129,7 +130,7 @@ func (a removeMarginActionFail) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Con
ctx, a.Pair, a.Account, sdk.NewCoin(a.Pair.QuoteDenom(), a.Margin),
)
if !errors.Is(err, a.ExpectedErr) {
return ctx, err, false
return ctx, fmt.Errorf("expected error %v, got %v", a.ExpectedErr, err), false
}

return ctx, nil, false
Expand Down
1 change: 0 additions & 1 deletion x/perp/v2/integration/action/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/NibiruChain/nibiru/x/perp/v2/types"
)

// Logger
type logger struct {
log string
}
Expand Down
23 changes: 0 additions & 23 deletions x/perp/v2/integration/action/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,13 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/x/common/asset"
"github.com/NibiruChain/nibiru/x/common/testutil/action"
)

type changeLiquidationFeeRatio struct {
LiquidationFeeRatio sdk.Dec
}

type setMarketEnabled struct {
Enable bool
Pair asset.Pair
}

func (c changeLiquidationFeeRatio) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
// TODO(k-yang): implement

Expand All @@ -28,20 +22,3 @@ func ChangeLiquidationFeeRatio(liquidationFeeRatio sdk.Dec) action.Action {
LiquidationFeeRatio: liquidationFeeRatio,
}
}

// Enable market
func SetMarketEnabled(pair asset.Pair, enable bool) action.Action {
return setMarketEnabled{
Enable: enable,
Pair: pair,
}
}

func (c setMarketEnabled) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
err := app.PerpKeeperV2.ChangeMarketEnabledParameter(ctx, c.Pair, c.Enable)
if err != nil {
return ctx, err, true
}

return ctx, nil, true
}
30 changes: 26 additions & 4 deletions x/perp/v2/integration/action/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/NibiruChain/collections"

"github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/x/common/asset"
"github.com/NibiruChain/nibiru/x/common/denoms"
Expand Down Expand Up @@ -238,15 +236,39 @@ func ClosePosition(account sdk.AccAddress, pair asset.Pair) action.Action {
}
}

// Manually insert position, skipping open position logic
type closePositionFailsAction struct {
Account sdk.AccAddress
Pair asset.Pair

expectedErr error
}

func (c closePositionFailsAction) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
_, err := app.PerpKeeperV2.ClosePosition(ctx, c.Pair, c.Account)

if !errors.Is(err, c.expectedErr) {
return ctx, fmt.Errorf("expected error %s, got %s", c.expectedErr, err), true
}

return ctx, nil, true
}

func ClosePositionFails(account sdk.AccAddress, pair asset.Pair, expectedErr error) action.Action {
return &closePositionFailsAction{
Account: account,
Pair: pair,
expectedErr: expectedErr,
}
}

// Manually insert position, skipping open position logic
type insertPosition struct {
position types.Position
}

func (i insertPosition) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
traderAddr := sdk.MustAccAddressFromBech32(i.position.TraderAddress)
app.PerpKeeperV2.Positions.Insert(ctx, collections.Join(i.position.Pair, traderAddr), i.position)
app.PerpKeeperV2.SavePosition(ctx, i.position.Pair, 1, traderAddr, i.position)
return ctx, nil, true
}

Expand Down
4 changes: 1 addition & 3 deletions x/perp/v2/integration/action/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkquery "github.com/cosmos/cosmos-sdk/types/query"

"github.com/NibiruChain/collections"

"github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/x/common/asset"
"github.com/NibiruChain/nibiru/x/common/testutil/action"
Expand Down Expand Up @@ -130,7 +128,7 @@ func (q queryPositionNotFound) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Cont
Pair: q.pair,
Trader: q.traderAddress.String(),
})
if !errors.Is(err, collections.ErrNotFound) {
if !errors.Is(err, types.ErrPositionNotFound) {
return ctx, fmt.Errorf(
"expected position not found, but found a position for pair %s, trader %s",
q.pair,
Expand Down
26 changes: 26 additions & 0 deletions x/perp/v2/integration/action/settlement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package action

import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/x/common/asset"
"github.com/NibiruChain/nibiru/x/common/testutil/action"
)

type closeMarket struct {
pair asset.Pair
}

func (c closeMarket) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
err := app.PerpKeeperV2.CloseMarket(ctx, c.pair)
if err != nil {
return ctx, err, false
}

return ctx, nil, true
}

func CloseMarket(pair asset.Pair) action.Action {
return closeMarket{pair: pair}
}
9 changes: 9 additions & 0 deletions x/perp/v2/integration/assertion/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,12 @@ func AMM_VersionShouldBeEqual(expectedVersion uint64) AMMChecker {
return nil
}
}

func AMM_SettlementPriceShoulBeEqual(expectedPrice sdk.Dec) AMMChecker {
return func(amm types.AMM) error {
if !amm.SettlementPrice.Equal(expectedPrice) {
return fmt.Errorf("expected settlement price to be %s, got %s", expectedPrice, amm.SettlementPrice)
}
return nil
}
}
Loading

0 comments on commit a7c9447

Please sign in to comment.