Skip to content

Commit f8cfe79

Browse files
Update TWAP Parent Store when Suborder is a Maker (backport #3094) (#3236)
Co-authored-by: anmolagrawal345 <anmolagrawal345@gmail.com>
1 parent e87b758 commit f8cfe79

File tree

2 files changed

+191
-17
lines changed

2 files changed

+191
-17
lines changed

protocol/x/clob/e2e/twap_orders_test.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,170 @@ func TestTWAPOrderWithMatchingOrders(t *testing.T) {
344344
require.Equal(t, ctx.BlockTime().Unix()+int64(twapOrder.TwapParameters.Interval), triggerTime)
345345
}
346346

347+
func TestTWAPOrderWithMatchingOrdersWhereTWAPOrderIsMaker(t *testing.T) {
348+
tApp := testapp.NewTestAppBuilder(t).Build()
349+
ctx := tApp.InitChain()
350+
351+
// Place a TWAP order to buy 100B quantums over 4 legs
352+
twapOrder := clobtypes.Order{
353+
OrderId: clobtypes.OrderId{
354+
SubaccountId: constants.Alice_Num0,
355+
ClientId: 0,
356+
OrderFlags: clobtypes.OrderIdFlags_Twap,
357+
ClobPairId: 0,
358+
},
359+
Side: clobtypes.Order_SIDE_BUY,
360+
Quantums: 100_000_000_000, // 100B quantums
361+
Subticks: 200_000_000, // $20,000 per oracle price
362+
TwapParameters: &clobtypes.TwapParameters{
363+
Duration: 320,
364+
Interval: 80,
365+
PriceTolerance: 10_000,
366+
},
367+
GoodTilOneof: &clobtypes.Order_GoodTilBlockTime{
368+
GoodTilBlockTime: uint32(ctx.BlockTime().Unix() + 100),
369+
},
370+
}
371+
372+
// Place a matching sell order at the same price
373+
matchingOrder := clobtypes.Order{
374+
OrderId: clobtypes.OrderId{
375+
SubaccountId: constants.Bob_Num0,
376+
ClientId: 0,
377+
OrderFlags: clobtypes.OrderIdFlags_LongTerm,
378+
ClobPairId: 0,
379+
},
380+
Side: clobtypes.Order_SIDE_SELL,
381+
Quantums: 50_000_000_000, // 50B quantums
382+
Subticks: 200_000_000, // $20,000 per oracle price
383+
GoodTilOneof: &clobtypes.Order_GoodTilBlockTime{
384+
GoodTilBlockTime: uint32(ctx.BlockTime().Unix() + 3600),
385+
},
386+
}
387+
388+
// Place the TWAP order so the first suborder is resting on the book
389+
for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(
390+
ctx,
391+
tApp.App,
392+
*clobtypes.NewMsgPlaceOrder(twapOrder),
393+
) {
394+
resp := tApp.CheckTx(checkTx)
395+
require.True(t, resp.IsOK(), "Expected CheckTx to succeed. Response: %+v", resp)
396+
}
397+
398+
suborderId := clobtypes.OrderId{
399+
SubaccountId: constants.Alice_Num0,
400+
ClientId: 0,
401+
OrderFlags: clobtypes.OrderIdFlags_TwapSuborder,
402+
ClobPairId: 0,
403+
}
404+
405+
ctx = tApp.AdvanceToBlock(2, testapp.AdvanceToBlockOptions{})
406+
407+
// Place market order sell order as a taker order
408+
for _, checkTx := range testapp.MustMakeCheckTxsWithClobMsg(
409+
ctx,
410+
tApp.App,
411+
*clobtypes.NewMsgPlaceOrder(matchingOrder),
412+
) {
413+
resp := tApp.CheckTx(checkTx)
414+
require.True(t, resp.IsOK(), "Expected CheckTx to succeed. Response: %+v", resp)
415+
}
416+
417+
ctx = tApp.AdvanceToBlock(3, testapp.AdvanceToBlockOptions{})
418+
419+
ctx = tApp.AdvanceToBlock(4, testapp.AdvanceToBlockOptions{
420+
BlockTime: ctx.BlockTime().Add(time.Second * 40),
421+
RequestPrepareProposalTxsOverride: [][]byte{
422+
testtx.MustGetTxBytes(&clobtypes.MsgProposedOperations{
423+
OperationsQueue: []clobtypes.OperationRaw{
424+
clobtestutils.NewMatchOperationRaw(
425+
&clobtypes.Order{
426+
OrderId: matchingOrder.OrderId,
427+
Side: clobtypes.Order_SIDE_SELL,
428+
Quantums: 50_000_000_000,
429+
Subticks: 200_000_000,
430+
},
431+
[]clobtypes.MakerFill{
432+
{
433+
FillAmount: 25_000_000_000,
434+
MakerOrderId: suborderId,
435+
},
436+
},
437+
),
438+
},
439+
}),
440+
},
441+
})
442+
443+
// Verify TWAP order state is updated
444+
twapOrderPlacement, found := tApp.App.ClobKeeper.GetTwapOrderPlacement(
445+
ctx,
446+
twapOrder.OrderId,
447+
)
448+
require.True(t, found, "TWAP order placement should exist")
449+
require.Equal(t, uint32(3), twapOrderPlacement.RemainingLegs)
450+
require.Equal(t, uint64(75_000_000_000), twapOrderPlacement.RemainingQuantums) // 100B - 25B filled
451+
452+
_, triggerTime, found1 := tApp.App.ClobKeeper.GetTwapTriggerPlacement(
453+
ctx,
454+
suborderId,
455+
)
456+
require.True(t, found1, "Second suborder should exist in trigger store")
457+
458+
require.Equal(t, int64(80), triggerTime)
459+
460+
_, placed_suborder1_found := tApp.App.ClobKeeper.MemClob.GetOrder(suborderId)
461+
require.False(t, placed_suborder1_found, "Second suborder should not have been placed yet")
462+
463+
ctx = tApp.AdvanceToBlock(5, testapp.AdvanceToBlockOptions{
464+
BlockTime: ctx.BlockTime().Add(time.Second * 40),
465+
})
466+
467+
filled_amount := tApp.App.ClobKeeper.MemClob.GetOrderFilledAmount(ctx, suborderId)
468+
require.Equal(t, uint64(25_000_000_000), filled_amount.ToUint64())
469+
470+
ctx = tApp.AdvanceToBlock(6, testapp.AdvanceToBlockOptions{
471+
BlockTime: ctx.BlockTime().Add(time.Second * 0),
472+
RequestPrepareProposalTxsOverride: [][]byte{
473+
testtx.MustGetTxBytes(&clobtypes.MsgProposedOperations{
474+
OperationsQueue: []clobtypes.OperationRaw{
475+
clobtestutils.NewMatchOperationRaw(
476+
&clobtypes.Order{
477+
OrderId: suborderId,
478+
Side: clobtypes.Order_SIDE_BUY,
479+
Quantums: 25_000_000_000,
480+
Subticks: 200_000_000,
481+
},
482+
[]clobtypes.MakerFill{
483+
{
484+
FillAmount: 25_000_000_000,
485+
MakerOrderId: matchingOrder.OrderId,
486+
},
487+
},
488+
),
489+
},
490+
}),
491+
},
492+
})
493+
494+
// Verify TWAP order state is updated again
495+
twapOrderPlacement, found = tApp.App.ClobKeeper.GetTwapOrderPlacement(
496+
ctx,
497+
twapOrder.OrderId,
498+
)
499+
require.True(t, found, "TWAP order placement should still exist")
500+
require.Equal(t, uint32(2), twapOrderPlacement.RemainingLegs)
501+
require.Equal(t, uint64(50_000_000_000), twapOrderPlacement.RemainingQuantums) // 75B - 25B filled
502+
503+
_, triggerTime, found2 := tApp.App.ClobKeeper.GetTwapTriggerPlacement(
504+
ctx,
505+
suborderId,
506+
)
507+
require.True(t, found2, "Third suborder should exist in trigger store")
508+
require.Equal(t, ctx.BlockTime().Unix()+int64(twapOrder.TwapParameters.Interval), triggerTime)
509+
}
510+
347511
func TestTwapOrderStopsPlacingSubordersWhenCollateralIsDepleted(t *testing.T) {
348512
tApp := testapp.NewTestAppBuilder(t).Build()
349513
ctx := tApp.InitChain()

protocol/x/clob/keeper/process_single_match.go

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -299,25 +299,16 @@ func (k Keeper) ProcessSingleMatch(
299299
curMakerPruneableBlockHeight,
300300
)
301301

302+
// Check and update the remaining TWAP quantity for both maker and taker orders.
303+
makerOrder := matchWithOrders.MakerOrder.MustGetOrder()
304+
if err := k.checkAndUpdateTWAPOrderRemainingQuantity(ctx, makerOrder.OrderId, fillAmount); err != nil {
305+
return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err
306+
}
307+
302308
if !matchWithOrders.TakerOrder.IsLiquidation() {
303309
takerOrder := matchWithOrders.TakerOrder.MustGetOrder()
304-
if takerOrder.OrderId.IsTwapSuborder() {
305-
// Get the parent order ID by removing the TWAP suborder flag
306-
parentOrderId := types.OrderId{
307-
SubaccountId: takerOrder.OrderId.SubaccountId,
308-
ClientId: takerOrder.OrderId.ClientId,
309-
OrderFlags: types.OrderIdFlags_Twap, // Set directly to TWAP
310-
ClobPairId: takerOrder.OrderId.ClobPairId,
311-
}
312-
313-
// Update the parent TWAP order state
314-
if err := k.UpdateTWAPOrderRemainingQuantityOnFill(
315-
ctx,
316-
parentOrderId,
317-
fillAmount.ToUint64(),
318-
); err != nil {
319-
return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err
320-
}
310+
if err := k.checkAndUpdateTWAPOrderRemainingQuantity(ctx, takerOrder.OrderId, fillAmount); err != nil {
311+
return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err
321312
}
322313
}
323314

@@ -707,3 +698,22 @@ func getUpdatedOrderFillAmount(
707698

708699
return satypes.BaseQuantums(bigNewFillAmount.Uint64()), nil
709700
}
701+
702+
func (k Keeper) checkAndUpdateTWAPOrderRemainingQuantity(
703+
ctx sdk.Context,
704+
orderId types.OrderId,
705+
fillAmount satypes.BaseQuantums,
706+
) error {
707+
if orderId.IsTwapSuborder() {
708+
parentOrderId := types.OrderId{
709+
SubaccountId: orderId.SubaccountId,
710+
ClientId: orderId.ClientId,
711+
OrderFlags: types.OrderIdFlags_Twap, // Set directly to TWAP
712+
ClobPairId: orderId.ClobPairId,
713+
}
714+
if err := k.UpdateTWAPOrderRemainingQuantityOnFill(ctx, parentOrderId, fillAmount.ToUint64()); err != nil {
715+
return err
716+
}
717+
}
718+
return nil
719+
}

0 commit comments

Comments
 (0)