Skip to content

Commit

Permalink
fix(perp): by default, new markets are disabled (#1639)
Browse files Browse the repository at this point in the history
* fix(perp): by default, new markets are disabled

* fix tests: by default, disable new markets

---------

Co-authored-by: Unique-Divine <[email protected]>
Co-authored-by: Unique Divine <[email protected]>
  • Loading branch information
3 people authored Oct 24, 2023
1 parent 562209b commit ac000f2
Show file tree
Hide file tree
Showing 20 changed files with 371 additions and 279 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [#1630](https://github.com/NibiruChain/nibiru/pull/1630) - refactor(wasm): clean up wasmbinding/ folder structure
* [#1631](https://github.com/NibiruChain/nibiru/pull/1631) - fix(.goreleaser.yml): Load version for wasmvm dynamically.
* [#1638](https://github.com/NibiruChain/nibiru/pull/1638) - test(tokenfactory): integration test core logic with a real smart contract using `nibiru-std`
* [#1639](https://github.com/NibiruChain/nibiru/pull/1639) - fix(perp): by default, disable new markets until they are toggled on.

### Dependencies
- Bump `github.com/prometheus/client_golang` from 1.16.0 to 1.17.0 ([#1605](https://github.com/NibiruChain/nibiru/pull/1605))
Expand Down
6 changes: 3 additions & 3 deletions wasmbinding/exec_perp.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func (exec *ExecutorPerp) InsuranceFundWithdraw(
return err
}

return exec.PerpV2.Admin().WithdrawFromInsuranceFund(
return exec.PerpV2.Admin.WithdrawFromInsuranceFund(
ctx,
cwMsg.Amount,
to,
Expand All @@ -202,7 +202,7 @@ func (exec *ExecutorPerp) SetMarketEnabled(
return err
}

return exec.PerpV2.CloseMarket(ctx, pair)
return exec.PerpV2.Admin.CloseMarket(ctx, pair)
}

func (exec *ExecutorPerp) CreateMarket(
Expand Down Expand Up @@ -239,7 +239,7 @@ func (exec *ExecutorPerp) CreateMarket(
}
}

return exec.PerpV2.Admin().CreateMarket(ctx, perpv2keeper.ArgsCreateMarket{
return exec.PerpV2.Admin.CreateMarket(ctx, perpv2keeper.ArgsCreateMarket{
Pair: pair,
PriceMultiplier: cwMsg.PegMult,
SqrtDepth: cwMsg.SqrtDepth,
Expand Down
2 changes: 1 addition & 1 deletion wasmbinding/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ func (s *TestSuiteExecutor) TestCreateMarket() {
market, err := s.nibiru.PerpKeeperV2.GetMarket(s.ctx, pair)
s.NoError(err)
s.NoError(market.Validate())
s.True(market.Enabled)
s.False(market.Enabled, "by default, we create a market in a disabled state")
s.EqualValues(pair, market.Pair)

s.T().Log("Executing without permission should fail")
Expand Down
76 changes: 41 additions & 35 deletions x/common/testutil/action/testcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,67 +48,73 @@ func TC(name string) TestCase {
return TestCase{Name: name}
}

func (t TestCase) Given(action ...Action) TestCase {
t.given = append(t.given, action...)
return t
func (tc TestCase) Given(action ...Action) TestCase {
tc.given = append(tc.given, action...)
return tc
}

func (t TestCase) When(action ...Action) TestCase {
t.when = append(t.when, action...)
return t
func (tc TestCase) When(action ...Action) TestCase {
tc.when = append(tc.when, action...)
return tc
}

func (t TestCase) Then(action ...Action) TestCase {
t.then = append(t.then, action...)
return t
func (tc TestCase) Then(action ...Action) TestCase {
tc.then = append(tc.then, action...)
return tc
}

type TestSuite struct {
t *testing.T

testCases []TestCase
}

func NewTestSuite(t *testing.T) *TestSuite {
return &TestSuite{t: t}
}

func (t *TestSuite) WithTestCases(testCase ...TestCase) *TestSuite {
t.testCases = append(t.testCases, testCase...)
return t
}

func (t *TestSuite) Run() {
for _, testCase := range t.testCases {
func (tc TestCase) Run(t *testing.T) {
t.Run(tc.Name, func(t *testing.T) {
app, ctx := testapp.NewNibiruTestAppAndContextAtTime(time.UnixMilli(0))
var err error
var isMandatory bool

for _, action := range testCase.given {
for _, action := range tc.given {
ctx, err, isMandatory = action.Do(app, ctx)
if isMandatory {
require.NoError(t.t, err, "failed to execute given action: %s", testCase.Name)
require.NoError(t, err, "failed to execute given action: %s", tc.Name)
} else {
assert.NoError(t.t, err, "failed to execute given action: %s", testCase.Name)
assert.NoError(t, err, "failed to execute given action: %s", tc.Name)
}
}

for _, action := range testCase.when {
for _, action := range tc.when {
ctx, err, isMandatory = action.Do(app, ctx)
if isMandatory {
require.NoError(t.t, err, "failed to execute when action: %s", testCase.Name)
require.NoError(t, err, "failed to execute when action: %s", tc.Name)
} else {
assert.NoError(t.t, err, "failed to execute when action: %s", testCase.Name)
assert.NoError(t, err, "failed to execute when action: %s", tc.Name)
}
}

for _, action := range testCase.then {
for _, action := range tc.then {
ctx, err, isMandatory = action.Do(app, ctx)
if isMandatory {
require.NoError(t.t, err, "failed to execute then action: %s", testCase.Name)
require.NoError(t, err, "failed to execute then action: %s", tc.Name)
} else {
assert.NoError(t.t, err, "failed to execute then action: %s", testCase.Name)
assert.NoError(t, err, "failed to execute then action: %s", tc.Name)
}
}
})
}

type TestSuite struct {
t *testing.T

testCases []TestCase
}

func NewTestSuite(t *testing.T) *TestSuite {
return &TestSuite{t: t}
}

func (ts *TestSuite) WithTestCases(testCase ...TestCase) *TestSuite {
ts.testCases = append(ts.testCases, testCase...)
return ts
}

func (ts *TestSuite) Run() {
for _, testCase := range ts.testCases {
testCase.Run(ts.t)
}
}
5 changes: 3 additions & 2 deletions x/perp/v2/integration/action/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ type createMarketAction struct {
}

func (c createMarketAction) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
app.PerpKeeperV2.MarketLastVersion.Insert(ctx, c.Market.Pair, types.MarketLastVersion{Version: c.Market.Version})
app.PerpKeeperV2.MarketLastVersion.Insert(
ctx, c.Market.Pair, types.MarketLastVersion{Version: c.Market.Version})
app.PerpKeeperV2.SaveMarket(ctx, c.Market)
app.PerpKeeperV2.SaveAMM(ctx, c.AMM)

Expand Down Expand Up @@ -171,7 +172,7 @@ type createPool struct {
}

func (c createPool) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
err := app.PerpKeeperV2.Admin().CreateMarket(ctx, keeper.ArgsCreateMarket{
err := app.PerpKeeperV2.Admin.CreateMarket(ctx, keeper.ArgsCreateMarket{
Pair: c.pair,
PriceMultiplier: c.amm.PriceMultiplier,
SqrtDepth: c.amm.SqrtDepth,
Expand Down
4 changes: 2 additions & 2 deletions x/perp/v2/integration/action/settlement.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type closeMarket struct {
}

func (c closeMarket) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
err := app.PerpKeeperV2.CloseMarket(ctx, c.pair)
err := app.PerpKeeperV2.Admin.CloseMarket(ctx, c.pair)
if err != nil {
return ctx, err, false
}
Expand All @@ -35,7 +35,7 @@ type closeMarketShouldFail struct {
}

func (c closeMarketShouldFail) Do(app *app.NibiruApp, ctx sdk.Context) (sdk.Context, error, bool) {
err := app.PerpKeeperV2.CloseMarket(ctx, c.pair)
err := app.PerpKeeperV2.Admin.CloseMarket(ctx, c.pair)
if err == nil {
return ctx, err, false
}
Expand Down
50 changes: 40 additions & 10 deletions x/perp/v2/keeper/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,16 @@ import (
types "github.com/NibiruChain/nibiru/x/perp/v2/types"
)

// Admin is syntactic sugar to separate admin calls off from the other Keeper
// methods.
// Extends the Keeper with admin functions. Admin is syntactic sugar to separate
// admin calls off from the other Keeper methods.
//
// These Admin functions should:
// 1. Not be wired into the MsgServer or
// 1. Not be wired into the MsgServer.
// 2. Not be called in other methods in the x/perp module.
// 3. Only be callable from x/wasm/binding via sudo contracts.
// 3. Only be callable from nibiru/wasmbinding via sudo contracts.
//
// The intention here is to make it more obvious to the developer that an unsafe
// function is being used when it's called on the Admin() struct.
func (k Keeper) Admin() admin {
return admin{&k}
}

// Extends the Keeper with admin functions.
// function is being used when it's called from the PerpKeeper.Admin struct.
type admin struct{ *Keeper }

/*
Expand Down Expand Up @@ -63,6 +58,9 @@ type ArgsCreateMarket struct {
PriceMultiplier sdk.Dec
SqrtDepth sdk.Dec
Market *types.Market // pointer makes it optional
// EnableMarket: Optionally enable the default market without explicitly passing
// in each field as an argument. If 'Market' is present, this field is ignored.
EnableMarket bool
}

// CreateMarket creates a pool for a specific pair.
Expand All @@ -82,6 +80,7 @@ func (k admin) CreateMarket(
baseReserve := sqrtDepth
if args.Market == nil {
market = types.DefaultMarket(pair)
market.Enabled = args.EnableMarket
} else {
market = *args.Market
}
Expand Down Expand Up @@ -114,3 +113,34 @@ func (k admin) CreateMarket(

return nil
}

// CloseMarket closes the market. From now on, no new position can be opened on
// this market or closed. Only the open positions can be settled by calling
// SettlePosition.
func (k admin) CloseMarket(ctx sdk.Context, pair asset.Pair) (err error) {
market, err := k.GetMarket(ctx, pair)
if err != nil {
return err
}
if !market.Enabled {
return types.ErrMarketNotEnabled
}

amm, err := k.GetAMM(ctx, pair)
if err != nil {
return err
}

settlementPrice, _, err := amm.ComputeSettlementPrice()
if err != nil {
return
}

amm.SettlementPrice = settlementPrice
market.Enabled = false

k.SaveAMM(ctx, amm)
k.SaveMarket(ctx, market)

return nil
}
Loading

0 comments on commit ac000f2

Please sign in to comment.