From 77773c3d3fad75f1d3c4a14cc57f948db3aea65f Mon Sep 17 00:00:00 2001 From: Jakob Herlitz <125316911+jakob-dydx@users.noreply.github.com> Date: Wed, 6 Dec 2023 13:58:54 -0800 Subject: [PATCH] [CLOB-1028] allow settlement at oracle price in ProcessDeleveraging (#835) * add functionality in ProcessDeleveraging to allow settlement at oracle price instead of bankruptcy price * pull deltaQuoteQuantums out of ProcessDeleveraging * fix lint * fix issue with test * pr nits * update getDeleveragingQuoteQuantumsDelta helper --- protocol/x/clob/keeper/deleveraging.go | 103 +++++-- protocol/x/clob/keeper/deleveraging_test.go | 276 ++++++++++++++++++- protocol/x/clob/keeper/process_operations.go | 26 +- 3 files changed, 358 insertions(+), 47 deletions(-) diff --git a/protocol/x/clob/keeper/deleveraging.go b/protocol/x/clob/keeper/deleveraging.go index 794d2ff835..928e79cb49 100644 --- a/protocol/x/clob/keeper/deleveraging.go +++ b/protocol/x/clob/keeper/deleveraging.go @@ -236,6 +236,28 @@ func (k Keeper) OffsetSubaccountPerpetualPosition( deltaQuantums = new(big.Int).Set(deltaQuantumsRemaining) } + // Fetch delta quote quantums. Calculated at bankruptcy price for standard + // deleveraging and at oracle price for final settlement deleveraging. + deltaQuoteQuantums, err := k.getDeleveragingQuoteQuantumsDelta( + ctx, + perpetualId, + liquidatedSubaccountId, + deltaQuantums, + ) + if err != nil { + liquidatedSubaccount := k.subaccountsKeeper.GetSubaccount(ctx, liquidatedSubaccountId) + k.Logger(ctx).Error( + "Encountered error when getting quote quantums for deleveraging", + "error", err, + "blockHeight", ctx.BlockHeight(), + "perpetualId", perpetualId, + "deltaQuantums", deltaQuantums, + "liquidatedSubaccount", liquidatedSubaccount, + "offsettingSubaccount", offsettingSubaccount, + ) + return false + } + // Try to process the deleveraging operation for both subaccounts. if err := k.ProcessDeleveraging( ctx, @@ -243,6 +265,7 @@ func (k Keeper) OffsetSubaccountPerpetualPosition( *offsettingSubaccount.Id, perpetualId, deltaQuantums, + deltaQuoteQuantums, ); err == nil { // Update the remaining liquidatable quantums. deltaQuantumsRemaining = new(big.Int).Sub( @@ -305,6 +328,41 @@ func (k Keeper) OffsetSubaccountPerpetualPosition( return fills, deltaQuantumsRemaining } +// getDeleveragingQuoteQuantums returns the quote quantums delta to apply to a deleveraging operation. +// This returns the bankruptcy price for standard deleveraging operations, and the oracle price for +// final settlement deleveraging operations. The type of deleveraging event is determined by the +// collaterlization status of the subaccount (negative/non-negative TNC) as well as the clob pair +// status for the specified perpetual. +func (k Keeper) getDeleveragingQuoteQuantumsDelta( + ctx sdk.Context, + perpetualId uint32, + subaccountId satypes.SubaccountId, + deltaQuantums *big.Int, +) (*big.Int, error) { + clobPair := k.mustGetClobPairForPerpetualId(ctx, perpetualId) + isFinalSettlement := clobPair.Status == types.ClobPair_STATUS_FINAL_SETTLEMENT + + // If market is in final settlement and the subaccount has non-negative TNC, use the oracle price. + if isFinalSettlement { + hasNegativeTnc, err := k.CanDeleverageSubaccount(ctx, subaccountId) + if err != nil { + return new(big.Int), err + } + + if !hasNegativeTnc { + return k.perpetualsKeeper.GetNetNotional(ctx, perpetualId, deltaQuantums) + } + } + + // For standard deleveraging, use the bankruptcy price. + return k.GetBankruptcyPriceInQuoteQuantums( + ctx, + subaccountId, + perpetualId, + deltaQuantums, + ) +} + // ProcessDeleveraging processes a deleveraging operation by closing both the liquidated subaccount's // position and the offsetting subaccount's position at the bankruptcy price of the _liquidated_ position. // This function takes a `deltaQuantums` argument, which is the delta with respect to the liquidated subaccount's @@ -312,7 +370,7 @@ func (k Keeper) OffsetSubaccountPerpetualPosition( // is successfully written to state. // // This function returns an error if: -// - `deltaQuantums` is not valid with respect to either of the subaccounts. +// - `deltaBaseQuantums` is not valid with respect to either of the subaccounts. // - `GetBankruptcyPriceInQuoteQuantums` returns an error. // - subaccount updates cannot be applied when the bankruptcy prices of both subaccounts don't overlap. func (k Keeper) ProcessDeleveraging( @@ -320,7 +378,8 @@ func (k Keeper) ProcessDeleveraging( liquidatedSubaccountId satypes.SubaccountId, offsettingSubaccountId satypes.SubaccountId, perpetualId uint32, - deltaQuantums *big.Int, + deltaBaseQuantums *big.Int, + deltaQuoteQuantums *big.Int, ) ( err error, ) { @@ -338,36 +397,24 @@ func (k Keeper) ProcessDeleveraging( // by checking that `deltaQuantums` is on the opposite side of the liquidated position side, // the same side as the offsetting subaccount position side, and the magnitude of `deltaQuantums` // is not larger than both positions. - if liquidatedPositionQuantums.Sign()*deltaQuantums.Sign() != -1 || - liquidatedPositionQuantums.CmpAbs(deltaQuantums) == -1 || - offsettingPositionQuantums.Sign()*deltaQuantums.Sign() != 1 || - offsettingPositionQuantums.CmpAbs(deltaQuantums) == -1 { + if liquidatedPositionQuantums.Sign()*deltaBaseQuantums.Sign() != -1 || + liquidatedPositionQuantums.CmpAbs(deltaBaseQuantums) == -1 || + offsettingPositionQuantums.Sign()*deltaBaseQuantums.Sign() != 1 || + offsettingPositionQuantums.CmpAbs(deltaBaseQuantums) == -1 { return errorsmod.Wrapf( types.ErrInvalidPerpetualPositionSizeDelta, "ProcessDeleveraging: liquidated = (%s), offsetting = (%s), perpetual id = (%d), deltaQuantums = (%+v)", lib.MaybeGetJsonString(liquidatedSubaccount), lib.MaybeGetJsonString(offsettingSubaccount), perpetualId, - deltaQuantums, + deltaBaseQuantums, ) } - // Calculate the bankruptcy price of the liquidated position. This is the price at which both positions - // are closed. - bankruptcyPriceQuoteQuantums, err := k.GetBankruptcyPriceInQuoteQuantums( - ctx, - liquidatedSubaccountId, - perpetualId, - deltaQuantums, - ) - if err != nil { - return err - } - - deleveragedSubaccountQuoteBalanceDelta := bankruptcyPriceQuoteQuantums - offsettingSubaccountQuoteBalanceDelta := new(big.Int).Neg(bankruptcyPriceQuoteQuantums) - deleveragedSubaccountPerpetualQuantumsDelta := deltaQuantums - offsettingSubaccountPerpetualQuantumsDelta := new(big.Int).Neg(deltaQuantums) + deleveragedSubaccountQuoteBalanceDelta := deltaQuoteQuantums + offsettingSubaccountQuoteBalanceDelta := new(big.Int).Neg(deltaQuoteQuantums) + deleveragedSubaccountPerpetualQuantumsDelta := deltaBaseQuantums + offsettingSubaccountPerpetualQuantumsDelta := new(big.Int).Neg(deltaBaseQuantums) updates := []satypes.Update{ // Liquidated subaccount update. @@ -419,12 +466,12 @@ func (k Keeper) ProcessDeleveraging( if deleveragedQuoteQuantums, err := k.perpetualsKeeper.GetNetCollateral( ctx, perpetualId, - new(big.Int).Abs(deltaQuantums), + new(big.Int).Abs(deltaBaseQuantums), ); err == nil { labels := []metrics.Label{ metrics.GetLabelForIntValue(metrics.PerpetualId, int(perpetualId)), metrics.GetLabelForBoolValue(metrics.CheckTx, ctx.IsCheckTx()), - metrics.GetLabelForBoolValue(metrics.IsLong, deltaQuantums.Sign() == -1), + metrics.GetLabelForBoolValue(metrics.IsLong, deltaBaseQuantums.Sign() == -1), } metrics.AddSampleWithLabels( @@ -463,9 +510,9 @@ func (k Keeper) ProcessDeleveraging( liquidatedSubaccountId, offsettingSubaccountId, perpetualId, - satypes.BaseQuantums(new(big.Int).Abs(deltaQuantums).Uint64()), - satypes.BaseQuantums(bankruptcyPriceQuoteQuantums.Uint64()), - deltaQuantums.Sign() > 0, + satypes.BaseQuantums(new(big.Int).Abs(deltaBaseQuantums).Uint64()), + satypes.BaseQuantums(deltaQuoteQuantums.Uint64()), + deltaBaseQuantums.Sign() > 0, ), ), ) diff --git a/protocol/x/clob/keeper/deleveraging_test.go b/protocol/x/clob/keeper/deleveraging_test.go index 0f1fd8c15a..788b352d9f 100644 --- a/protocol/x/clob/keeper/deleveraging_test.go +++ b/protocol/x/clob/keeper/deleveraging_test.go @@ -2,13 +2,14 @@ package keeper_test import ( "errors" - indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events" - "github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager" "math" "math/big" "testing" "time" + indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events" + "github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -624,10 +625,11 @@ func TestOffsetSubaccountPerpetualPosition(t *testing.T) { err := keepertest.CreateUsdcAsset(ks.Ctx, ks.AssetsKeeper) require.NoError(t, err) - for _, p := range []perptypes.Perpetual{ + perps := []perptypes.Perpetual{ constants.BtcUsd_100PercentMarginRequirement, constants.EthUsd_100PercentMarginRequirement, - } { + } + for _, p := range perps { _, err := ks.PerpetualsKeeper.CreatePerpetual( ks.Ctx, p.Params.Id, @@ -640,6 +642,43 @@ func TestOffsetSubaccountPerpetualPosition(t *testing.T) { require.NoError(t, err) } + clobPairs := []types.ClobPair{ + constants.ClobPair_Btc, + constants.ClobPair_Eth, + } + for i, clobPair := range clobPairs { + mockIndexerEventManager.On("AddTxnEvent", + ks.Ctx, + indexerevents.SubtypePerpetualMarket, + indexerevents.PerpetualMarketEventVersion, + indexer_manager.GetBytes( + indexerevents.NewPerpetualMarketCreateEvent( + clobPair.MustGetPerpetualId(), + clobPair.Id, + perps[i].Params.Ticker, + perps[i].Params.MarketId, + clobPair.Status, + clobPair.QuantumConversionExponent, + perps[i].Params.AtomicResolution, + clobPair.SubticksPerTick, + clobPair.StepBaseQuantums, + perps[i].Params.LiquidityTier, + ), + ), + ).Once().Return() + + _, err = ks.ClobKeeper.CreatePerpetualClobPair( + ks.Ctx, + clobPair.Id, + clobPair.MustGetPerpetualId(), + satypes.BaseQuantums(clobPair.StepBaseQuantums), + clobPair.QuantumConversionExponent, + clobPair.SubticksPerTick, + clobPair.Status, + ) + require.NoError(t, err) + } + for _, subaccount := range tc.subaccounts { ks.SubaccountsKeeper.SetSubaccount(ks.Ctx, subaccount) } @@ -1065,14 +1104,16 @@ func TestProcessDeleveraging(t *testing.T) { ks.SubaccountsKeeper.SetSubaccount(ks.Ctx, tc.liquidatedSubaccount) ks.SubaccountsKeeper.SetSubaccount(ks.Ctx, tc.offsettingSubaccount) + bankruptcyPriceQuoteQuantums := new(big.Int) if tc.expectedErr == nil { - bankruptcyPriceQuoteQuantums, err := ks.ClobKeeper.GetBankruptcyPriceInQuoteQuantums( + bankruptcyPriceQuoteQuantums, err = ks.ClobKeeper.GetBankruptcyPriceInQuoteQuantums( ks.Ctx, *tc.liquidatedSubaccount.GetId(), uint32(0), tc.deltaQuantums, ) require.NoError(t, err) + mockIndexerEventManager.On("AddTxnEvent", ks.Ctx, indexerevents.SubtypeDeleveraging, @@ -1095,6 +1136,215 @@ func TestProcessDeleveraging(t *testing.T) { *tc.offsettingSubaccount.GetId(), uint32(0), tc.deltaQuantums, + bankruptcyPriceQuoteQuantums, + ) + if tc.expectedErr == nil { + require.NoError(t, err) + + actualLiquidated := ks.SubaccountsKeeper.GetSubaccount(ks.Ctx, *tc.liquidatedSubaccount.GetId()) + require.Equal( + t, + tc.expectedLiquidatedSubaccount, + actualLiquidated, + ) + + actualOffsetting := ks.SubaccountsKeeper.GetSubaccount(ks.Ctx, *tc.offsettingSubaccount.GetId()) + require.Equal( + t, + tc.expectedOffsettingSubaccount, + actualOffsetting, + ) + } else { + require.ErrorContains(t, err, tc.expectedErr.Error()) + } + }) + } +} + +// Note that final settlement matches piggyback off of the deleveraging operation. Because of this +// the pair of subaccounts offsetting each other are still referred to as "liquidated subaccount" and +// "offsetting subaccount" in the test cases below. +func TestProcessDeleveragingAtOraclePrice(t *testing.T) { + tests := map[string]struct { + // Setup. + liquidatedSubaccount satypes.Subaccount + offsettingSubaccount satypes.Subaccount + deltaQuantums *big.Int + + // Expectations. + expectedLiquidatedSubaccount satypes.Subaccount + expectedOffsettingSubaccount satypes.Subaccount + expectedErr error + }{ + "Liquidated: well-collateralized, offsetting: well-collateralized": { + liquidatedSubaccount: constants.Carl_Num0_1BTC_Short_100000USD, + offsettingSubaccount: constants.Dave_Num0_1BTC_Long_50000USD, + deltaQuantums: big.NewInt(100_000_000), // 1 BTC + + expectedLiquidatedSubaccount: satypes.Subaccount{ + Id: &constants.Carl_Num0, + AssetPositions: keepertest.CreateUsdcAssetPosition( + big.NewInt(100_000_000_000 - 50_000_000_000), + ), + }, + expectedOffsettingSubaccount: satypes.Subaccount{ + Id: &constants.Dave_Num0, + AssetPositions: keepertest.CreateUsdcAssetPosition( + big.NewInt(50_000_000_000 + 50_000_000_000), + ), + }, + }, + "Liquidated: well-collateralized, offsetting: under-collateralized, TNC > 0": { + liquidatedSubaccount: constants.Dave_Num0_1BTC_Long_50000USD, + offsettingSubaccount: constants.Carl_Num0_1BTC_Short_54999USD, + deltaQuantums: big.NewInt(-100_000_000), // 1 BTC + + expectedLiquidatedSubaccount: satypes.Subaccount{ + Id: &constants.Dave_Num0, + AssetPositions: keepertest.CreateUsdcAssetPosition( + big.NewInt(50_000_000_000 + 50_000_000_000), + ), + }, + expectedOffsettingSubaccount: satypes.Subaccount{ + Id: &constants.Carl_Num0, + AssetPositions: keepertest.CreateUsdcAssetPosition( + big.NewInt(54_999_000_000 - 50_000_000_000), + ), + }, + }, + "Liquidated: well-collateralized, offsetting: under-collateralized, TNC == 0": { + liquidatedSubaccount: constants.Carl_Num0_1BTC_Short_100000USD, + offsettingSubaccount: constants.Dave_Num0_1BTC_Long_50000USD_Short, + deltaQuantums: big.NewInt(100_000_000), // 1 BTC + + expectedLiquidatedSubaccount: satypes.Subaccount{ + Id: &constants.Carl_Num0, + AssetPositions: keepertest.CreateUsdcAssetPosition( + big.NewInt(100_000_000_000 - 50_000_000_000), + ), + }, + expectedOffsettingSubaccount: satypes.Subaccount{ + Id: &constants.Dave_Num0, + }, + }, + "Liquidated: well-collateralized, offsetting: under-collateralized, TNC < 0": { + liquidatedSubaccount: constants.Carl_Num0_1BTC_Short_100000USD, + offsettingSubaccount: constants.Dave_Num0_1BTC_Long_50001USD_Short, + deltaQuantums: big.NewInt(100_000_000), // 1 BTC + + // Negative TNC account closing at oracle price is an invalid state transition. + expectedErr: satypes.ErrFailedToUpdateSubaccounts, + }, + "Liquidated: under-collateralized, TNC > 0, offsetting: well-collateralized": { + liquidatedSubaccount: constants.Carl_Num0_1BTC_Short_54999USD, + offsettingSubaccount: constants.Dave_Num0_1BTC_Long_50000USD, + deltaQuantums: big.NewInt(100_000_000), // 1 BTC + + expectedLiquidatedSubaccount: satypes.Subaccount{ + Id: &constants.Carl_Num0, + AssetPositions: keepertest.CreateUsdcAssetPosition( + big.NewInt(54_999_000_000 - 50_000_000_000), + ), + }, + expectedOffsettingSubaccount: satypes.Subaccount{ + Id: &constants.Dave_Num0, + AssetPositions: keepertest.CreateUsdcAssetPosition( + big.NewInt(50_000_000_000 + 50_000_000_000), + ), + }, + }, + "Liquidated: under-collateralized, TNC == 0, offsetting: under-collateralized, TNC < 0": { + liquidatedSubaccount: constants.Carl_Num0_1BTC_Short_50000USD, + offsettingSubaccount: constants.Dave_Num0_1BTC_Long_50001USD_Short, + deltaQuantums: big.NewInt(100_000_000), // 1 BTC + + // Negative TNC account closing at oracle price is an invalid state transition. + expectedErr: satypes.ErrFailedToUpdateSubaccounts, + }, + "Liquidated: under-collateralized, TNC < 0, offsetting: under-collateralized, TNC > 0": { + liquidatedSubaccount: constants.Carl_Num0_1BTC_Short_49999USD, + offsettingSubaccount: constants.Dave_Num0_1BTC_Long_45001USD_Short, + deltaQuantums: big.NewInt(100_000_000), // 1 BTC + + // Negative TNC account closing at oracle price is an invalid state transition. + expectedErr: satypes.ErrFailedToUpdateSubaccounts, + }, + "Liquidated: under-collateralized, TNC < 0, offsetting: well-collateralized": { + liquidatedSubaccount: constants.Carl_Num0_1BTC_Short_49999USD, + offsettingSubaccount: constants.Dave_Num0_1BTC_Long_50000USD, + deltaQuantums: big.NewInt(100_000_000), // 1 BTC + + // Negative TNC account closing at oracle price is an invalid state transition. + expectedErr: satypes.ErrFailedToUpdateSubaccounts, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + memClob := memclob.NewMemClobPriceTimePriority(false) + mockIndexerEventManager := &mocks.IndexerEventManager{} + ks := keepertest.NewClobKeepersTestContext(t, memClob, &mocks.BankKeeper{}, mockIndexerEventManager) + + // Create the default markets. + keepertest.CreateTestMarkets(t, ks.Ctx, ks.PricesKeeper) + + // Create liquidity tiers. + keepertest.CreateTestLiquidityTiers(t, ks.Ctx, ks.PerpetualsKeeper) + + err := keepertest.CreateUsdcAsset(ks.Ctx, ks.AssetsKeeper) + require.NoError(t, err) + + for _, p := range []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + constants.EthUsd_20PercentInitial_10PercentMaintenance, + } { + _, err := ks.PerpetualsKeeper.CreatePerpetual( + ks.Ctx, + p.Params.Id, + p.Params.Ticker, + p.Params.MarketId, + p.Params.AtomicResolution, + p.Params.DefaultFundingPpm, + p.Params.LiquidityTier, + ) + require.NoError(t, err) + } + + ks.SubaccountsKeeper.SetSubaccount(ks.Ctx, tc.liquidatedSubaccount) + ks.SubaccountsKeeper.SetSubaccount(ks.Ctx, tc.offsettingSubaccount) + + fillPriceQuoteQuantums, err := ks.PerpetualsKeeper.GetNetNotional( + ks.Ctx, + uint32(0), + tc.deltaQuantums, + ) + fillPriceQuoteQuantums.Neg(fillPriceQuoteQuantums) + require.NoError(t, err) + + if tc.expectedErr == nil { + mockIndexerEventManager.On("AddTxnEvent", + ks.Ctx, + indexerevents.SubtypeDeleveraging, + indexerevents.DeleveragingEventVersion, + indexer_manager.GetBytes( + indexerevents.NewDeleveragingEvent( + *tc.liquidatedSubaccount.GetId(), + *tc.offsettingSubaccount.GetId(), + uint32(0), + satypes.BaseQuantums(new(big.Int).Abs(tc.deltaQuantums).Uint64()), + satypes.BaseQuantums(fillPriceQuoteQuantums.Uint64()), + tc.deltaQuantums.Sign() > 0, + ), + ), + ).Return() + } + err = ks.ClobKeeper.ProcessDeleveraging( + ks.Ctx, + *tc.liquidatedSubaccount.GetId(), + *tc.offsettingSubaccount.GetId(), + uint32(0), + tc.deltaQuantums, + fillPriceQuoteQuantums, ) if tc.expectedErr == nil { require.NoError(t, err) @@ -1222,14 +1472,15 @@ func TestProcessDeleveraging_Rounding(t *testing.T) { ks.SubaccountsKeeper.SetSubaccount(ks.Ctx, tc.liquidatedSubaccount) ks.SubaccountsKeeper.SetSubaccount(ks.Ctx, tc.offsettingSubaccount) + bankruptcyPriceQuoteQuantums, err := ks.ClobKeeper.GetBankruptcyPriceInQuoteQuantums( + ks.Ctx, + *tc.liquidatedSubaccount.GetId(), + uint32(0), + tc.deltaQuantums, + ) + require.NoError(t, err) + if tc.expectedErr == nil { - bankruptcyPriceQuoteQuantums, err := ks.ClobKeeper.GetBankruptcyPriceInQuoteQuantums( - ks.Ctx, - *tc.liquidatedSubaccount.GetId(), - uint32(0), - tc.deltaQuantums, - ) - require.NoError(t, err) mockIndexerEventManager.On("AddTxnEvent", ks.Ctx, indexerevents.SubtypeDeleveraging, @@ -1252,6 +1503,7 @@ func TestProcessDeleveraging_Rounding(t *testing.T) { *tc.offsettingSubaccount.GetId(), uint32(0), tc.deltaQuantums, + bankruptcyPriceQuoteQuantums, ) if tc.expectedErr == nil { require.NoError(t, err) diff --git a/protocol/x/clob/keeper/process_operations.go b/protocol/x/clob/keeper/process_operations.go index ef464f4268..56f2d16649 100644 --- a/protocol/x/clob/keeper/process_operations.go +++ b/protocol/x/clob/keeper/process_operations.go @@ -651,12 +651,22 @@ func (k Keeper) PersistMatchDeleveragingToState( perpetualId, ) } - deltaQuantumsIsNegative := position.GetIsLong() + deltaBaseQuantumsIsNegative := position.GetIsLong() for _, fill := range matchDeleveraging.GetFills() { - deltaQuantums := new(big.Int).SetUint64(fill.FillAmount) - if deltaQuantumsIsNegative { - deltaQuantums.Neg(deltaQuantums) + deltaBaseQuantums := new(big.Int).SetUint64(fill.FillAmount) + if deltaBaseQuantumsIsNegative { + deltaBaseQuantums.Neg(deltaBaseQuantums) + } + + deltaQuoteQuantums, err := k.getDeleveragingQuoteQuantumsDelta( + ctx, + perpetualId, + liquidatedSubaccountId, + deltaBaseQuantums, + ) + if err != nil { + return err } if err := k.ProcessDeleveraging( @@ -664,16 +674,18 @@ func (k Keeper) PersistMatchDeleveragingToState( liquidatedSubaccountId, fill.OffsettingSubaccountId, perpetualId, - deltaQuantums, + deltaBaseQuantums, + deltaQuoteQuantums, ); err != nil { return errorsmod.Wrapf( types.ErrInvalidDeleveragingFill, "Failed to process deleveraging fill: %+v. liquidatedSubaccountId: %+v, "+ - "perpetualId: %v, deltaQuantums: %v, error: %v", + "perpetualId: %v, deltaBaseQuantums: %v, deltaQuoteQuantums: %v, error: %v", fill, liquidatedSubaccountId, perpetualId, - deltaQuantums, + deltaBaseQuantums, + deltaQuoteQuantums, err, ) }