diff --git a/protocol/mocks/PerpetualsKeeper.go b/protocol/mocks/PerpetualsKeeper.go index 921b9a486e0..46a4afac45e 100644 --- a/protocol/mocks/PerpetualsKeeper.go +++ b/protocol/mocks/PerpetualsKeeper.go @@ -245,23 +245,23 @@ func (_m *PerpetualsKeeper) PerformStatefulPremiumVotesValidation(ctx types.Cont return r0 } -// SetLiquidityTier provides a mock function with given fields: ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional -func (_m *PerpetualsKeeper) SetLiquidityTier(ctx types.Context, id uint32, name string, initialMarginPpm uint32, maintenanceFractionPpm uint32, impactNotional uint64) (perpetualstypes.LiquidityTier, error) { - ret := _m.Called(ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional) +// SetLiquidityTier provides a mock function with given fields: ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional, openInterestLowerCap, openInterestUpperCap +func (_m *PerpetualsKeeper) SetLiquidityTier(ctx types.Context, id uint32, name string, initialMarginPpm uint32, maintenanceFractionPpm uint32, impactNotional uint64, openInterestLowerCap uint64, openInterestUpperCap uint64) (perpetualstypes.LiquidityTier, error) { + ret := _m.Called(ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional, openInterestLowerCap, openInterestUpperCap) var r0 perpetualstypes.LiquidityTier var r1 error - if rf, ok := ret.Get(0).(func(types.Context, uint32, string, uint32, uint32, uint64) (perpetualstypes.LiquidityTier, error)); ok { - return rf(ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional) + if rf, ok := ret.Get(0).(func(types.Context, uint32, string, uint32, uint32, uint64, uint64, uint64) (perpetualstypes.LiquidityTier, error)); ok { + return rf(ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional, openInterestLowerCap, openInterestUpperCap) } - if rf, ok := ret.Get(0).(func(types.Context, uint32, string, uint32, uint32, uint64) perpetualstypes.LiquidityTier); ok { - r0 = rf(ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional) + if rf, ok := ret.Get(0).(func(types.Context, uint32, string, uint32, uint32, uint64, uint64, uint64) perpetualstypes.LiquidityTier); ok { + r0 = rf(ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional, openInterestLowerCap, openInterestUpperCap) } else { r0 = ret.Get(0).(perpetualstypes.LiquidityTier) } - if rf, ok := ret.Get(1).(func(types.Context, uint32, string, uint32, uint32, uint64) error); ok { - r1 = rf(ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional) + if rf, ok := ret.Get(1).(func(types.Context, uint32, string, uint32, uint32, uint64, uint64, uint64) error); ok { + r1 = rf(ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional, openInterestLowerCap, openInterestUpperCap) } else { r1 = ret.Error(1) } diff --git a/protocol/scripts/genesis/sample_pregenesis.json b/protocol/scripts/genesis/sample_pregenesis.json index 7caf67d5990..bec862f06da 100644 --- a/protocol/scripts/genesis/sample_pregenesis.json +++ b/protocol/scripts/genesis/sample_pregenesis.json @@ -862,7 +862,9 @@ "impact_notional": 10000000000, "initial_margin_ppm": 50000, "maintenance_fraction_ppm": 600000, - "name": "Large-Cap" + "name": "Large-Cap", + "open_interest_lower_cap": 0, + "open_interest_upper_cap": 0 }, { "base_position_notional": 250000000000, @@ -870,7 +872,8 @@ "impact_notional": 5000000000, "initial_margin_ppm": 100000, "maintenance_fraction_ppm": 500000, - "name": "Mid-Cap" + "name": "Mid-Cap", + "open_interest_lower_cap": 50000000000000 }, { "base_position_notional": 100000000000, @@ -878,7 +881,8 @@ "impact_notional": 2500000000, "initial_margin_ppm": 200000, "maintenance_fraction_ppm": 500000, - "name": "Long-Tail" + "name": "Long-Tail", + "open_interest_lower_cap": 20000000000000 }, { "base_position_notional": 1000000000, @@ -886,7 +890,9 @@ "impact_notional": 2500000000, "initial_margin_ppm": 1000000, "maintenance_fraction_ppm": 200000, - "name": "Safety" + "name": "Safety", + "open_interest_lower_cap": 0, + "open_interest_upper_cap": 0 } ], "params": { @@ -1815,7 +1821,7 @@ ] } }, - "app_version": "4.0.0-dev0-59-g5530ea29", + "app_version": "4.0.0-dev0-60-geb100ae0", "chain_id": "dydx-sample-1", "consensus": { "params": { diff --git a/protocol/testing/genesis.sh b/protocol/testing/genesis.sh index 452ad0ae0e4..9cd90f2cf1b 100755 --- a/protocol/testing/genesis.sh +++ b/protocol/testing/genesis.sh @@ -125,6 +125,7 @@ function edit_genesis() { # Update perpetuals module. # Liquidity Tiers. + # TODO(OTE-208): Finalize default values for open interest caps. dasel put -t json -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers' -v "[]" # Liquidity Tier: Large-Cap dasel put -t json -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[]' -v "{}" @@ -134,6 +135,8 @@ function edit_genesis() { dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[0].maintenance_fraction_ppm' -v '600000' # 60% of IM dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[0].base_position_notional' -v '1000000000000' # 1_000_000 USDC dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[0].impact_notional' -v '10000000000' # 10_000 USDC (500 USDC / 5%) + dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[0].open_interest_lower_cap' -v '0' # OIMF doesn't apply to Large-Cap + dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[0].open_interest_upper_cap' -v '0' # OIMF doesn't apply to Large-Cap # Liquidity Tier: Mid-Cap dasel put -t json -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[]' -v "{}" @@ -143,6 +146,8 @@ function edit_genesis() { dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[1].maintenance_fraction_ppm' -v '500000' # 50% of IM dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[1].base_position_notional' -v '250000000000' # 250_000 USDC dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[1].impact_notional' -v '5000000000' # 5_000 USDC (500 USDC / 10%) + dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[1].open_interest_lower_cap' -v '25000000000000' # 25 million USDC + dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[1].open_interest_lower_cap' -v '50000000000000' # 50 million USDC # Liquidity Tier: Long-Tail dasel put -t json -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[]' -v "{}" @@ -152,6 +157,8 @@ function edit_genesis() { dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[2].maintenance_fraction_ppm' -v '500000' # 50% of IM dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[2].base_position_notional' -v '100000000000' # 100_000 USDC dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[2].impact_notional' -v '2500000000' # 2_500 USDC (500 USDC / 20%) + dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[2].open_interest_lower_cap' -v '10000000000000' # 10 million USDC + dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[2].open_interest_lower_cap' -v '20000000000000' # 20 million USDC # Liquidity Tier: Safety dasel put -t json -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[]' -v "{}" @@ -161,6 +168,10 @@ function edit_genesis() { dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[3].maintenance_fraction_ppm' -v '200000' # 20% of IM dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[3].base_position_notional' -v '1000000000' # 1_000 USDC dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[3].impact_notional' -v '2500000000' # 2_500 USDC (2_500 USDC / 100%) + # OIMF doesn't apply to `Safety`, since IMF is already at 100%. + dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[3].open_interest_lower_cap' -v '0' + dasel put -t int -f "$GENESIS" '.app_state.perpetuals.liquidity_tiers.[3].open_interest_upper_cap' -v '0' + # Params. dasel put -t int -f "$GENESIS" '.app_state.perpetuals.params.funding_rate_clamp_factor_ppm' -v '6000000' # 600 % (same as 75% on hourly rate) diff --git a/protocol/testutil/constants/perpetuals.go b/protocol/testutil/constants/perpetuals.go index 2fc10cbb3c3..e3769687ff9 100644 --- a/protocol/testutil/constants/perpetuals.go +++ b/protocol/testutil/constants/perpetuals.go @@ -339,6 +339,8 @@ var ( InitialMarginPpm: 200_000, MaintenanceFractionPpm: 500_000, ImpactNotional: 2_500_000_000, + OpenInterestLowerCap: 0, + OpenInterestUpperCap: 0, }, { Id: uint32(1), @@ -346,6 +348,8 @@ var ( InitialMarginPpm: 300_000, MaintenanceFractionPpm: 600_000, ImpactNotional: 1_667_000_000, + OpenInterestLowerCap: 25_000_000_000_000, + OpenInterestUpperCap: 50_000_000_000_000, }, { Id: uint32(2), @@ -353,6 +357,8 @@ var ( InitialMarginPpm: 400_000, MaintenanceFractionPpm: 700_000, ImpactNotional: 1_250_000_000, + OpenInterestLowerCap: 10_000_000_000_000, + OpenInterestUpperCap: 20_000_000_000_000, }, }, Params: PerpetualsGenesisParams, @@ -365,6 +371,7 @@ var ( MarketType: perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS, }, FundingIndex: dtypes.ZeroInt(), + OpenInterest: dtypes.ZeroInt(), }, { Params: perptypes.PerpetualParams{ @@ -374,6 +381,7 @@ var ( MarketType: perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS, }, FundingIndex: dtypes.ZeroInt(), + OpenInterest: dtypes.ZeroInt(), }, }, } diff --git a/protocol/testutil/keeper/perpetuals.go b/protocol/testutil/keeper/perpetuals.go index a7c9c3bef73..0db6fc47bfb 100644 --- a/protocol/testutil/keeper/perpetuals.go +++ b/protocol/testutil/keeper/perpetuals.go @@ -176,6 +176,8 @@ func CreateTestLiquidityTiers(t *testing.T, ctx sdk.Context, k *keeper.Keeper) { l.InitialMarginPpm, l.MaintenanceFractionPpm, l.ImpactNotional, + l.OpenInterestLowerCap, + l.OpenInterestUpperCap, ) require.NoError(t, err) diff --git a/protocol/testutil/perpetuals/perpetuals.go b/protocol/testutil/perpetuals/perpetuals.go index 451da22b7b9..dbcce922291 100644 --- a/protocol/testutil/perpetuals/perpetuals.go +++ b/protocol/testutil/perpetuals/perpetuals.go @@ -67,6 +67,7 @@ func GeneratePerpetual(optionalModifications ...PerpetualModifierOption) *perpty MarketType: perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS, }, FundingIndex: dtypes.ZeroInt(), + OpenInterest: dtypes.ZeroInt(), } for _, opt := range optionalModifications { diff --git a/protocol/x/perpetuals/genesis.go b/protocol/x/perpetuals/genesis.go index fc9e4c9ab69..db9650d9165 100644 --- a/protocol/x/perpetuals/genesis.go +++ b/protocol/x/perpetuals/genesis.go @@ -26,6 +26,8 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) elem.InitialMarginPpm, elem.MaintenanceFractionPpm, elem.ImpactNotional, + elem.OpenInterestLowerCap, + elem.OpenInterestUpperCap, ) if err != nil { diff --git a/protocol/x/perpetuals/keeper/msg_server_set_liquidity_tier.go b/protocol/x/perpetuals/keeper/msg_server_set_liquidity_tier.go index e2be739d827..a2e6e75f897 100644 --- a/protocol/x/perpetuals/keeper/msg_server_set_liquidity_tier.go +++ b/protocol/x/perpetuals/keeper/msg_server_set_liquidity_tier.go @@ -30,6 +30,8 @@ func (k msgServer) SetLiquidityTier( msg.LiquidityTier.InitialMarginPpm, msg.LiquidityTier.MaintenanceFractionPpm, msg.LiquidityTier.ImpactNotional, + msg.LiquidityTier.OpenInterestLowerCap, + msg.LiquidityTier.OpenInterestUpperCap, ); err != nil { return nil, err } diff --git a/protocol/x/perpetuals/keeper/msg_server_set_liquidity_tier_test.go b/protocol/x/perpetuals/keeper/msg_server_set_liquidity_tier_test.go index 111aff9d7a1..cf608028338 100644 --- a/protocol/x/perpetuals/keeper/msg_server_set_liquidity_tier_test.go +++ b/protocol/x/perpetuals/keeper/msg_server_set_liquidity_tier_test.go @@ -114,6 +114,21 @@ func TestSetLiquidityTier(t *testing.T) { }, expectedErr: "invalid authority", }, + "Failure: invalid open interest caps": { + msg: &types.MsgSetLiquidityTier{ + Authority: lib.GovModuleAddress.String(), + LiquidityTier: types.LiquidityTier{ + Id: testLt.Id, + Name: "medium-cap", + InitialMarginPpm: 567_123, + MaintenanceFractionPpm: 500_001, + ImpactNotional: 1_300_303, + OpenInterestLowerCap: 100, + OpenInterestUpperCap: 50, + }, + }, + expectedErr: "open interest lower cap is larger than upper cap", + }, } for name, tc := range tests { @@ -126,6 +141,8 @@ func TestSetLiquidityTier(t *testing.T) { testLt.InitialMarginPpm, testLt.MaintenanceFractionPpm, testLt.ImpactNotional, + testLt.OpenInterestLowerCap, + testLt.OpenInterestUpperCap, ) require.NoError(t, err) diff --git a/protocol/x/perpetuals/keeper/perpetual.go b/protocol/x/perpetuals/keeper/perpetual.go index 01fbda8e5e7..97c0b7ff9e0 100644 --- a/protocol/x/perpetuals/keeper/perpetual.go +++ b/protocol/x/perpetuals/keeper/perpetual.go @@ -71,6 +71,7 @@ func (k Keeper) CreatePerpetual( MarketType: marketType, }, FundingIndex: dtypes.ZeroInt(), + OpenInterest: dtypes.ZeroInt(), } if err := k.validatePerpetual( @@ -1377,6 +1378,8 @@ func (k Keeper) SetLiquidityTier( initialMarginPpm uint32, maintenanceFractionPpm uint32, impactNotional uint64, + openInterestLowerCap uint64, + openInterestUpperCap uint64, ) ( liquidityTier types.LiquidityTier, err error, @@ -1388,6 +1391,8 @@ func (k Keeper) SetLiquidityTier( InitialMarginPpm: initialMarginPpm, MaintenanceFractionPpm: maintenanceFractionPpm, ImpactNotional: impactNotional, + OpenInterestLowerCap: openInterestLowerCap, + OpenInterestUpperCap: openInterestUpperCap, } // Validate liquidity tier's fields. diff --git a/protocol/x/perpetuals/keeper/perpetual_test.go b/protocol/x/perpetuals/keeper/perpetual_test.go index 95e569892e6..fbae544a07c 100644 --- a/protocol/x/perpetuals/keeper/perpetual_test.go +++ b/protocol/x/perpetuals/keeper/perpetual_test.go @@ -685,6 +685,8 @@ func TestGetMarginRequirements_Success(t *testing.T) { tc.initialMarginPpm, tc.maintenanceFractionPpm, 1, // dummy impact notional value + 0, // dummy open interest lower cap + 0, // dummy open interest upper cap ) require.NoError(t, err) @@ -2825,6 +2827,8 @@ func TestGetAllLiquidityTiers_Sorted(t *testing.T) { lt.InitialMarginPpm, lt.MaintenanceFractionPpm, lt.ImpactNotional, + lt.OpenInterestLowerCap, + lt.OpenInterestUpperCap, ) require.NoError(t, err) } @@ -2863,6 +2867,8 @@ func TestHasLiquidityTier(t *testing.T) { lt.InitialMarginPpm, lt.MaintenanceFractionPpm, lt.ImpactNotional, + lt.OpenInterestLowerCap, + lt.OpenInterestUpperCap, ) require.NoError(t, err) } @@ -2887,6 +2893,8 @@ func TestCreateLiquidityTier_Success(t *testing.T) { lt.InitialMarginPpm, lt.MaintenanceFractionPpm, lt.ImpactNotional, + lt.OpenInterestLowerCap, + lt.OpenInterestUpperCap, ) require.NoError(t, err) @@ -2911,6 +2919,8 @@ func TestSetLiquidityTier_New_Failure(t *testing.T) { initialMarginPpm uint32 maintenanceFractionPpm uint32 impactNotional uint64 + openInterestLowerCap uint64 + openInterestUpperCap uint64 expectedError error }{ "Initial Margin Ppm exceeds maximum": { @@ -2919,6 +2929,8 @@ func TestSetLiquidityTier_New_Failure(t *testing.T) { initialMarginPpm: lib.OneMillion + 1, maintenanceFractionPpm: 500_000, impactNotional: uint64(lib.OneMillion), + openInterestLowerCap: 0, + openInterestUpperCap: 0, expectedError: errorsmod.Wrap(types.ErrInitialMarginPpmExceedsMax, fmt.Sprint(lib.OneMillion+1)), }, "Maintenance Fraction Ppm exceeds maximum": { @@ -2927,6 +2939,8 @@ func TestSetLiquidityTier_New_Failure(t *testing.T) { initialMarginPpm: 500_000, maintenanceFractionPpm: lib.OneMillion + 1, impactNotional: uint64(lib.OneMillion), + openInterestLowerCap: 0, + openInterestUpperCap: 0, expectedError: errorsmod.Wrap(types.ErrMaintenanceFractionPpmExceedsMax, fmt.Sprint(lib.OneMillion+1)), }, "Impact Notional is zero": { @@ -2935,6 +2949,8 @@ func TestSetLiquidityTier_New_Failure(t *testing.T) { initialMarginPpm: 500_000, maintenanceFractionPpm: lib.OneMillion, impactNotional: uint64(0), + openInterestLowerCap: 0, + openInterestUpperCap: 0, expectedError: types.ErrImpactNotionalIsZero, }, } @@ -2952,6 +2968,8 @@ func TestSetLiquidityTier_New_Failure(t *testing.T) { tc.initialMarginPpm, tc.maintenanceFractionPpm, tc.impactNotional, + tc.openInterestLowerCap, + tc.openInterestUpperCap, ) require.Error(t, err) @@ -2970,6 +2988,8 @@ func TestModifyLiquidityTier_Success(t *testing.T) { lt.InitialMarginPpm, lt.MaintenanceFractionPpm, lt.ImpactNotional, + lt.OpenInterestLowerCap, + lt.OpenInterestUpperCap, ) require.NoError(t, err) } @@ -2981,6 +3001,8 @@ func TestModifyLiquidityTier_Success(t *testing.T) { initialMarginPpm := uint32(i * 2) maintenanceFractionPpm := uint32(i * 2) impactNotional := uint64((i + 1) * 500_000_000) + openInterestLowerCap := uint64(0) + openInterestUpperCap := uint64(0) modifiedLt, err := pc.PerpetualsKeeper.SetLiquidityTier( pc.Ctx, lt.Id, @@ -2988,6 +3010,8 @@ func TestModifyLiquidityTier_Success(t *testing.T) { initialMarginPpm, maintenanceFractionPpm, impactNotional, + openInterestLowerCap, + openInterestUpperCap, ) require.NoError(t, err) obtainedLt, err := pc.PerpetualsKeeper.GetLiquidityTier(pc.Ctx, lt.Id) @@ -3029,6 +3053,8 @@ func TestSetLiquidityTier_Existing_Failure(t *testing.T) { initialMarginPpm uint32 maintenanceFractionPpm uint32 impactNotional uint64 + openInterestLowerCap uint64 + openInterestUpperCap uint64 expectedError error }{ "Initial Margin Ppm exceeds maximum": { @@ -3037,6 +3063,8 @@ func TestSetLiquidityTier_Existing_Failure(t *testing.T) { initialMarginPpm: lib.OneMillion + 1, maintenanceFractionPpm: 500_000, impactNotional: uint64(lib.OneMillion), + openInterestLowerCap: 0, + openInterestUpperCap: 0, expectedError: errorsmod.Wrap(types.ErrInitialMarginPpmExceedsMax, fmt.Sprint(lib.OneMillion+1)), }, "Maintenance Fraction Ppm exceeds maximum": { @@ -3045,6 +3073,8 @@ func TestSetLiquidityTier_Existing_Failure(t *testing.T) { initialMarginPpm: 500_000, maintenanceFractionPpm: lib.OneMillion + 1, impactNotional: uint64(lib.OneMillion), + openInterestLowerCap: 0, + openInterestUpperCap: 0, expectedError: errorsmod.Wrap(types.ErrMaintenanceFractionPpmExceedsMax, fmt.Sprint(lib.OneMillion+1)), }, "Impact Notional is zero": { @@ -3053,8 +3083,25 @@ func TestSetLiquidityTier_Existing_Failure(t *testing.T) { initialMarginPpm: 500_000, maintenanceFractionPpm: lib.OneMillion, impactNotional: uint64(0), + openInterestLowerCap: 0, + openInterestUpperCap: 0, expectedError: types.ErrImpactNotionalIsZero, }, + "Invalid open interest caps": { + id: 1, + name: "Small-Cap", + initialMarginPpm: 500_000, + maintenanceFractionPpm: lib.OneMillion, + impactNotional: uint64(lib.OneMillion), + openInterestLowerCap: 50_000_000_000_000, + openInterestUpperCap: 25_000_000_000_000, + expectedError: errorsmod.Wrapf( + types.ErrOpenInterestLowerCapLargerThanUpperCap, + "open_interest_lower_cap: %d, open_interest_upper_cap: %d", + 50_000_000_000_000, + 25_000_000_000_000, + ), + }, } // Test setup. @@ -3071,6 +3118,8 @@ func TestSetLiquidityTier_Existing_Failure(t *testing.T) { tc.initialMarginPpm, tc.maintenanceFractionPpm, tc.impactNotional, + tc.openInterestLowerCap, + tc.openInterestUpperCap, ) require.Error(t, err) diff --git a/protocol/x/perpetuals/module_test.go b/protocol/x/perpetuals/module_test.go index 7802a105d19..68147a9afc4 100644 --- a/protocol/x/perpetuals/module_test.go +++ b/protocol/x/perpetuals/module_test.go @@ -282,7 +282,9 @@ func TestAppModule_InitExportGenesis(t *testing.T) { "name":"Large-Cap", "initial_margin_ppm":50000, "maintenance_fraction_ppm":500000, - "impact_notional":10000000000 + "impact_notional":10000000000, + "open_interest_lower_cap":25000000000000, + "open_interest_upper_cap":50000000000000 } ], "params":{ @@ -314,7 +316,8 @@ func TestAppModule_InitExportGenesis(t *testing.T) { "liquidity_tier":0, "market_type":"PERPETUAL_MARKET_TYPE_CROSS" }, - "funding_index":"0" + "funding_index":"0", + "open_interest":"0" } ], "liquidity_tiers":[ @@ -324,7 +327,9 @@ func TestAppModule_InitExportGenesis(t *testing.T) { "initial_margin_ppm":50000, "maintenance_fraction_ppm":500000, "base_position_notional":"0", - "impact_notional":"10000000000" + "impact_notional":"10000000000", + "open_interest_lower_cap":"25000000000000", + "open_interest_upper_cap":"50000000000000" } ], "params":{ diff --git a/protocol/x/perpetuals/simulation/genesis.go b/protocol/x/perpetuals/simulation/genesis.go index 7addb088180..df6d8ecec05 100644 --- a/protocol/x/perpetuals/simulation/genesis.go +++ b/protocol/x/perpetuals/simulation/genesis.go @@ -205,6 +205,7 @@ func RandomizedGenState(simState *module.SimulationState) { MarketType: marketType, }, FundingIndex: dtypes.ZeroInt(), + OpenInterest: dtypes.ZeroInt(), } } diff --git a/protocol/x/perpetuals/types/errors.go b/protocol/x/perpetuals/types/errors.go index e7e0aed8c8b..50f04d5c40c 100644 --- a/protocol/x/perpetuals/types/errors.go +++ b/protocol/x/perpetuals/types/errors.go @@ -112,6 +112,11 @@ var ( 23, "Market type is invalid", ) + ErrOpenInterestLowerCapLargerThanUpperCap = errorsmod.Register( + ModuleName, + 24, + "open interest lower cap is larger than upper cap", + ) // Errors for Not Implemented ErrNotImplementedFunding = errorsmod.Register(ModuleName, 1001, "Not Implemented: Perpetuals Funding") diff --git a/protocol/x/perpetuals/types/liquidity_tier.go b/protocol/x/perpetuals/types/liquidity_tier.go index 957e5dfd879..2af218d04c3 100644 --- a/protocol/x/perpetuals/types/liquidity_tier.go +++ b/protocol/x/perpetuals/types/liquidity_tier.go @@ -24,6 +24,15 @@ func (liquidityTier LiquidityTier) Validate() error { return ErrImpactNotionalIsZero } + if liquidityTier.OpenInterestLowerCap > liquidityTier.OpenInterestUpperCap { + return errorsmod.Wrapf( + ErrOpenInterestLowerCapLargerThanUpperCap, + "open_interest_lower_cap: %d, open_interest_upper_cap: %d", + liquidityTier.OpenInterestLowerCap, + liquidityTier.OpenInterestUpperCap, + ) + } + return nil } diff --git a/protocol/x/perpetuals/types/types.go b/protocol/x/perpetuals/types/types.go index 7b7489cf065..48634398a4e 100644 --- a/protocol/x/perpetuals/types/types.go +++ b/protocol/x/perpetuals/types/types.go @@ -93,6 +93,8 @@ type PerpetualsKeeper interface { initialMarginPpm uint32, maintenanceFractionPpm uint32, impactNotional uint64, + openInterestLowerCap uint64, + openInterestUpperCap uint64, ) ( liquidityTier LiquidityTier, err error,