From b126ab1b9b8b1398f56d7be90e1b666e10bad806 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 21:22:05 -0400 Subject: [PATCH] Refactor perpetual upgrade handlers in `v5.0.0` (#1442) (#1444) (cherry picked from commit e2359f32594265439b19df57fe1a191abf85f0e6) Co-authored-by: Teddy Ding --- protocol/app/upgrades/v5.0.0/upgrade.go | 139 ++++++++---------- .../upgrades/v5.0.0/upgrade_container_test.go | 94 +++++++++++- protocol/mocks/PerpetualsKeeper.go | 18 +++ .../containertest/preupgrade_genesis.json | 28 ++++ protocol/x/perpetuals/types/types.go | 4 + 5 files changed, 205 insertions(+), 78 deletions(-) diff --git a/protocol/app/upgrades/v5.0.0/upgrade.go b/protocol/app/upgrades/v5.0.0/upgrade.go index a5a5b1f61e..a37e3ab810 100644 --- a/protocol/app/upgrades/v5.0.0/upgrade.go +++ b/protocol/app/upgrades/v5.0.0/upgrade.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + "github.com/dydxprotocol/v4-chain/protocol/dtypes" clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" @@ -19,20 +20,68 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/lib" ) -// Set all existing perpetuals to cross market type +// perpetuals upgrade handler for v5.0.0 +// - 1. Set all perpetuals to cross market type +// - 2. Initialize perpetual open interest to current OI func perpetualsUpgrade( ctx sdk.Context, perpetualsKeeper perptypes.PerpetualsKeeper, + subaccountsKeeper satypes.SubaccountsKeeper, ) { - // Set all perpetuals to cross market type + perpOIMap := make(map[uint32]*big.Int) + + // Iterate through all subaccounts and perp positions for each subaccount. + // Aggregate open interest for each perpetual market. + subaccounts := subaccountsKeeper.GetAllSubaccount(ctx) + for _, sa := range subaccounts { + for _, perpPosition := range sa.PerpetualPositions { + if perpPosition.Quantums.BigInt().Sign() <= 0 { + // Only record positive positions for total open interest. + // Total negative position size should be equal to total positive position size. + continue + } + if openInterest, exists := perpOIMap[perpPosition.PerpetualId]; exists { + // Already seen this perpetual. Add to open interest. + openInterest.Add( + openInterest, + perpPosition.Quantums.BigInt(), + ) + } else { + // Haven't seen this pereptual. Initialize open interest. + perpOIMap[perpPosition.PerpetualId] = new(big.Int).Set( + perpPosition.Quantums.BigInt(), + ) + } + } + } + perpetuals := perpetualsKeeper.GetAllPerpetuals(ctx) - for _, p := range perpetuals { - _, err := perpetualsKeeper.SetPerpetualMarketType( - ctx, p.GetId(), - perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS) + for _, perp := range perpetuals { + // 1. Set all perpetuals to cross market type + perp.Params.MarketType = perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS + + // 2. Initialize perpetual open interest to current OI + perpOpenInterst := big.NewInt(0) + if oi, exists := perpOIMap[perp.GetId()]; exists { + perpOpenInterst.Set(oi) + } + perp.OpenInterest = dtypes.NewIntFromBigInt(perpOpenInterst) + err := perpetualsKeeper.ValidateAndSetPerpetual( + ctx, + perp, + ) if err != nil { - panic(fmt.Sprintf("failed to set perpetual market type for perpetual %d: %s", p.GetId(), err)) + panic(fmt.Sprintf( + "failed to modify open interest for perpetual, openInterest = %v, perpetual = %+v", + perpOpenInterst, + perp, + )) } + ctx.Logger().Info(fmt.Sprintf( + "Successfully migrated perpetual %d: %+v", + perp.GetId(), + perp, + )) } } @@ -98,8 +147,10 @@ func blockRateLimitConfigUpdate( panic(fmt.Sprintf("failed to update the block rate limit configuration: %s", err)) } ctx.Logger().Info( - "Successfully upgraded block rate limit configuration to: %+v\n", - clobKeeper.GetBlockRateLimitConfiguration(ctx), + fmt.Sprintf( + "Successfully upgraded block rate limit configuration to: %+v\n", + clobKeeper.GetBlockRateLimitConfiguration(ctx), + ), ) } @@ -238,69 +289,6 @@ func voteExtensionsUpgrade( ) } -// Initialize open interest for perpetuals -func initializePerpOpenInterest( - ctx sdk.Context, - perpetualsKeeper perptypes.PerpetualsKeeper, - subaccountsKeeper satypes.SubaccountsKeeper, -) { - perpOIMap := make(map[uint32]*big.Int) - - subaccounts := subaccountsKeeper.GetAllSubaccount(ctx) - - // Iterate through all subaccounts and perp positions for each subaccount. - // Aggregate open interest for each perpetual market. - for _, sa := range subaccounts { - for _, perpPosition := range sa.PerpetualPositions { - if perpPosition.Quantums.BigInt().Sign() <= 0 { - // Only record positive positions for total open interest. - // Total negative position size should be equal to total positive position size. - continue - } - if openInterest, exists := perpOIMap[perpPosition.PerpetualId]; exists { - // Already seen this perpetual. Add to open interest. - openInterest.Add( - openInterest, - perpPosition.Quantums.BigInt(), - ) - } else { - // Haven't seen this pereptual. Initialize open interest. - perpOIMap[perpPosition.PerpetualId] = new(big.Int).Set( - perpPosition.Quantums.BigInt(), - ) - } - } - } - - allPerps := perpetualsKeeper.GetAllPerpetuals(ctx) - for _, perp := range allPerps { - if perp.OpenInterest.BigInt().Sign() != 0 { - panic(fmt.Sprintf("perpetual %d has non-zero OI (%v) before upgrade", perp.GetId(), perp.OpenInterest)) - } - if openInterest, exists := perpOIMap[perp.GetId()]; exists { - err := perpetualsKeeper.ModifyOpenInterest( - ctx, - perp.GetId(), - openInterest, // by default perpetual.OI = 0, so use the total open interest as delta - ) - if err != nil { - panic(fmt.Sprintf( - "failed to modify open interest for perpetual, openInterest = %v, perpetual = %+v", - openInterest, - perp, - )) - } - ctx.Logger().Info(fmt.Sprintf( - "Successfully initialized open interest for perpetual %d = %v", - perp.GetId(), - openInterest, - )) - } else { - ctx.Logger().Info(fmt.Sprintf("Perpetual %d has zero open interest at the time of upgrade", perp.GetId())) - } - } -} - // Initialize vault module parameters. func initializeVaultModuleParams( ctx sdk.Context, @@ -331,11 +319,8 @@ func CreateUpgradeHandler( clobKeeper.MigratePruneableOrders(sdkCtx) sdkCtx.Logger().Info("Successfully migrated pruneable orders") - // Set all perpetuals to cross market type - perpetualsUpgrade(sdkCtx, perpetualsKeeper) - - // Initialize open interest for all perpetuals - initializePerpOpenInterest(sdkCtx, perpetualsKeeper, subaccountsKeeper) + // Set all perpetuals to cross market type and initialize open interest + perpetualsUpgrade(sdkCtx, perpetualsKeeper, subaccountsKeeper) // Set block rate limit configuration blockRateLimitConfigUpdate(sdkCtx, clobKeeper) diff --git a/protocol/app/upgrades/v5.0.0/upgrade_container_test.go b/protocol/app/upgrades/v5.0.0/upgrade_container_test.go index febe20bda3..065f40701d 100644 --- a/protocol/app/upgrades/v5.0.0/upgrade_container_test.go +++ b/protocol/app/upgrades/v5.0.0/upgrade_container_test.go @@ -21,6 +21,8 @@ import ( const ( AliceBobBTCQuantums = 1_000_000 + CarlDaveBTCQuantums = 2_000_000 + CarlDaveETHQuantums = 4_000_000 ) func TestStateUpgrade(t *testing.T) { @@ -119,6 +121,94 @@ func placeOrders(node *containertest.Node, t *testing.T) { }, constants.BobAccAddress.String(), )) + require.NoError(t, containertest.BroadcastTx( + node, + &clobtypes.MsgPlaceOrder{ + Order: clobtypes.Order{ + OrderId: clobtypes.OrderId{ + ClientId: 0, + SubaccountId: satypes.SubaccountId{ + Owner: constants.CarlAccAddress.String(), + Number: 0, + }, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: CarlDaveBTCQuantums, + Subticks: 5_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{ + GoodTilBlock: 20, + }, + }, + }, + constants.CarlAccAddress.String(), + )) + require.NoError(t, containertest.BroadcastTx( + node, + &clobtypes.MsgPlaceOrder{ + Order: clobtypes.Order{ + OrderId: clobtypes.OrderId{ + ClientId: 0, + SubaccountId: satypes.SubaccountId{ + Owner: constants.DaveAccAddress.String(), + Number: 0, + }, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: CarlDaveBTCQuantums, + Subticks: 5_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{ + GoodTilBlock: 20, + }, + }, + }, + constants.DaveAccAddress.String(), + )) + require.NoError(t, containertest.BroadcastTx( + node, + &clobtypes.MsgPlaceOrder{ + Order: clobtypes.Order{ + OrderId: clobtypes.OrderId{ + ClientId: 0, + SubaccountId: satypes.SubaccountId{ + Owner: constants.CarlAccAddress.String(), + Number: 0, + }, + ClobPairId: 1, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: CarlDaveETHQuantums, + Subticks: 5_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{ + GoodTilBlock: 20, + }, + }, + }, + constants.CarlAccAddress.String(), + )) + require.NoError(t, containertest.BroadcastTx( + node, + &clobtypes.MsgPlaceOrder{ + Order: clobtypes.Order{ + OrderId: clobtypes.OrderId{ + ClientId: 0, + SubaccountId: satypes.SubaccountId{ + Owner: constants.DaveAccAddress.String(), + Number: 0, + }, + ClobPairId: 1, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: CarlDaveETHQuantums, + Subticks: 5_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{ + GoodTilBlock: 20, + }, + }, + }, + constants.DaveAccAddress.String(), + )) err := node.Wait(2) require.NoError(t, err) } @@ -186,7 +276,9 @@ func postUpgradePerpetualOIs(node *containertest.Node, t *testing.T) { for _, perp := range allPerpsResp.Perpetual { expectedOI := 0 if perp.Params.Id == 0 { - expectedOI = AliceBobBTCQuantums + expectedOI = AliceBobBTCQuantums + CarlDaveBTCQuantums + } else if perp.Params.Id == 1 { + expectedOI = CarlDaveETHQuantums } assert.Equalf(t, dtypes.NewInt(int64(expectedOI)), diff --git a/protocol/mocks/PerpetualsKeeper.go b/protocol/mocks/PerpetualsKeeper.go index cfe6c21958..9a716a44b1 100644 --- a/protocol/mocks/PerpetualsKeeper.go +++ b/protocol/mocks/PerpetualsKeeper.go @@ -450,6 +450,24 @@ func (_m *PerpetualsKeeper) SetPerpetualMarketType(ctx types.Context, id uint32, return r0, r1 } +// ValidateAndSetPerpetual provides a mock function with given fields: ctx, perpetual +func (_m *PerpetualsKeeper) ValidateAndSetPerpetual(ctx types.Context, perpetual perpetualstypes.Perpetual) error { + ret := _m.Called(ctx, perpetual) + + if len(ret) == 0 { + panic("no return value specified for ValidateAndSetPerpetual") + } + + var r0 error + if rf, ok := ret.Get(0).(func(types.Context, perpetualstypes.Perpetual) error); ok { + r0 = rf(ctx, perpetual) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // NewPerpetualsKeeper creates a new instance of PerpetualsKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewPerpetualsKeeper(t interface { diff --git a/protocol/testing/containertest/preupgrade_genesis.json b/protocol/testing/containertest/preupgrade_genesis.json index d1264fdfb6..df7a3768ec 100644 --- a/protocol/testing/containertest/preupgrade_genesis.json +++ b/protocol/testing/containertest/preupgrade_genesis.json @@ -2158,6 +2158,34 @@ }, "margin_enabled": true }, + { + "asset_positions": [ + { + "asset_id": 0, + "index": 0, + "quantums": "100000000000000000" + } + ], + "id": { + "number": 0, + "owner": "dydx1fjg6zp6vv8t9wvy4lps03r5l4g7tkjw9wvmh70" + }, + "margin_enabled": true + }, + { + "asset_positions": [ + { + "asset_id": 0, + "index": 0, + "quantums": "100000000000000000" + } + ], + "id": { + "number": 0, + "owner": "dydx1wau5mja7j7zdavtfq9lu7ejef05hm6ffenlcsn" + }, + "margin_enabled": true + }, { "asset_positions": [ { diff --git a/protocol/x/perpetuals/types/types.go b/protocol/x/perpetuals/types/types.go index 425d4ca0c6..5dd354107b 100644 --- a/protocol/x/perpetuals/types/types.go +++ b/protocol/x/perpetuals/types/types.go @@ -124,6 +124,10 @@ type PerpetualsKeeper interface { ) []Perpetual GetAllLiquidityTiers(ctx sdk.Context) (list []LiquidityTier) SendOIUpdatesToIndexer(ctx sdk.Context) + ValidateAndSetPerpetual( + ctx sdk.Context, + perpetual Perpetual, + ) error } // OpenInterestDelta represents a (perpId, openInterestDelta) tuple.