@@ -3859,6 +3859,289 @@ describe('DLOB Perp Tests', () => {
3859
3859
'wrong maker orderId'
3860
3860
) . to . equal ( 5 ) ;
3861
3861
} ) ;
3862
+
3863
+ it ( 'DLOB signedMsgOrder filtering - taking vs resting orders' , ( ) => {
3864
+ const vAsk = new BN ( 15 ) ;
3865
+ const vBid = new BN ( 10 ) ;
3866
+ const slot = 1 ;
3867
+ const oracle = {
3868
+ price : vBid . add ( vAsk ) . div ( new BN ( 2 ) ) ,
3869
+ slot : new BN ( slot ) ,
3870
+ confidence : new BN ( 1 ) ,
3871
+ hasSufficientNumberOfDataPoints : true ,
3872
+ } ;
3873
+
3874
+ const users = [
3875
+ Keypair . generate ( ) ,
3876
+ Keypair . generate ( ) ,
3877
+ Keypair . generate ( ) ,
3878
+ Keypair . generate ( ) ,
3879
+ ] ;
3880
+ const dlob = new DLOB ( ) ;
3881
+ const marketIndex = 0 ;
3882
+ const marketType = MarketType . PERP ;
3883
+
3884
+ // Create orders for both directions (LONG for bids, SHORT for asks)
3885
+ const directions = [ PositionDirection . LONG , PositionDirection . SHORT ] ;
3886
+ const orderConfigs = [
3887
+ {
3888
+ orderId : 1 ,
3889
+ price : 11 ,
3890
+ postOnly : false ,
3891
+ auctionComplete : false ,
3892
+ orderType : OrderType . LIMIT ,
3893
+ } , // taking order 1
3894
+ {
3895
+ orderId : 2 ,
3896
+ price : 12 ,
3897
+ postOnly : false ,
3898
+ auctionComplete : false ,
3899
+ orderType : OrderType . MARKET ,
3900
+ } , // taking order 2
3901
+ {
3902
+ orderId : 3 ,
3903
+ price : 13 ,
3904
+ postOnly : true ,
3905
+ auctionComplete : false ,
3906
+ orderType : OrderType . LIMIT ,
3907
+ } , // resting order 1
3908
+ {
3909
+ orderId : 4 ,
3910
+ price : 14 ,
3911
+ postOnly : false ,
3912
+ auctionComplete : true ,
3913
+ orderType : OrderType . LIMIT ,
3914
+ } , // resting order 2
3915
+ ] ;
3916
+
3917
+ directions . forEach ( ( direction , dirIndex ) => {
3918
+ orderConfigs . forEach ( ( config , orderIndex ) => {
3919
+ const orderSlot = config . auctionComplete ? slot - 11 : slot ;
3920
+ const order : Order = {
3921
+ status : OrderStatus . OPEN ,
3922
+ orderId : config . orderId + dirIndex * 10 , // unique orderId per direction
3923
+ marketType : MarketType . PERP ,
3924
+ marketIndex : 0 ,
3925
+ price : new BN ( config . price ) . mul ( PRICE_PRECISION ) ,
3926
+ baseAssetAmount : BASE_PRECISION ,
3927
+ direction,
3928
+ orderType : config . orderType ,
3929
+ postOnly : config . postOnly ,
3930
+ auctionDuration : 10 ,
3931
+ slot : new BN ( orderSlot ) ,
3932
+ auctionStartPrice : vBid ,
3933
+ auctionEndPrice : vAsk ,
3934
+ bitFlags : 1 , // isSignedMsg flag
3935
+ userOrderId : 0 ,
3936
+ baseAssetAmountFilled : new BN ( 0 ) ,
3937
+ quoteAssetAmountFilled : new BN ( 0 ) ,
3938
+ quoteAssetAmount : new BN ( 0 ) ,
3939
+ reduceOnly : false ,
3940
+ triggerPrice : new BN ( 0 ) ,
3941
+ triggerCondition : OrderTriggerCondition . ABOVE ,
3942
+ existingPositionDirection : direction ,
3943
+ immediateOrCancel : false ,
3944
+ oraclePriceOffset : 0 ,
3945
+ maxTs : ZERO ,
3946
+ postedSlotTail : 0 ,
3947
+ } ;
3948
+ dlob . insertSignedMsgOrder (
3949
+ order ,
3950
+ users [ orderIndex ] . publicKey . toString ( ) ,
3951
+ false
3952
+ ) ;
3953
+ } ) ;
3954
+ } ) ;
3955
+
3956
+ // Test taking bids - should only include taking orders (1, 2) in LONG direction
3957
+ const takingBids = Array . from (
3958
+ dlob . getTakingBids ( marketIndex , marketType , slot , oracle )
3959
+ ) ;
3960
+
3961
+ const signedMsgTakingBids = takingBids . filter ( ( node ) => node . isSignedMsg ) ;
3962
+ expect ( signedMsgTakingBids . length ) . to . equal ( 2 ) ;
3963
+ expect ( signedMsgTakingBids . some ( ( node ) => node . order . orderId === 1 ) ) . to . be
3964
+ . true ; // taking order 1
3965
+ expect ( signedMsgTakingBids . some ( ( node ) => node . order . orderId === 2 ) ) . to . be
3966
+ . true ; // taking order 2
3967
+
3968
+ // Test resting limit bids - should only include resting orders (3, 4) in LONG direction
3969
+ const restingBids = Array . from (
3970
+ dlob . getRestingLimitBids ( marketIndex , slot , marketType , oracle )
3971
+ ) ;
3972
+
3973
+ const signedMsgRestingBids = restingBids . filter ( ( node ) => node . isSignedMsg ) ;
3974
+ expect ( signedMsgRestingBids . length ) . to . equal ( 2 ) ;
3975
+ expect ( signedMsgRestingBids . some ( ( node ) => node . order . orderId === 3 ) ) . to . be
3976
+ . true ; // resting order 1
3977
+ expect ( signedMsgRestingBids . some ( ( node ) => node . order . orderId === 4 ) ) . to . be
3978
+ . true ; // resting order 2
3979
+
3980
+ // Test taking asks - should only include taking orders (11, 12) in SHORT direction
3981
+ const takingAsks = Array . from (
3982
+ dlob . getTakingAsks ( marketIndex , marketType , slot , oracle )
3983
+ ) ;
3984
+
3985
+ const signedMsgTakingAsks = takingAsks . filter ( ( node ) => node . isSignedMsg ) ;
3986
+ expect ( signedMsgTakingAsks . length ) . to . equal ( 2 ) ;
3987
+ expect ( signedMsgTakingAsks . some ( ( node ) => node . order . orderId === 11 ) ) . to . be
3988
+ . true ; // taking order 1
3989
+ expect ( signedMsgTakingAsks . some ( ( node ) => node . order . orderId === 12 ) ) . to . be
3990
+ . true ; // taking order 2
3991
+
3992
+ // Test resting limit asks - should only include resting orders (13, 14) in SHORT direction
3993
+ const restingAsks = Array . from (
3994
+ dlob . getRestingLimitAsks ( marketIndex , slot , marketType , oracle )
3995
+ ) ;
3996
+
3997
+ const signedMsgRestingAsks = restingAsks . filter ( ( node ) => node . isSignedMsg ) ;
3998
+ expect ( signedMsgRestingAsks . length ) . to . equal ( 2 ) ;
3999
+ expect ( signedMsgRestingAsks . some ( ( node ) => node . order . orderId === 13 ) ) . to . be
4000
+ . true ; // resting order 1
4001
+ expect ( signedMsgRestingAsks . some ( ( node ) => node . order . orderId === 14 ) ) . to . be
4002
+ . true ; // resting order 2
4003
+ } ) ;
4004
+
4005
+ it ( 'DLOB signedMsgOrder filtering - auction completion transition' , ( ) => {
4006
+ const vAsk = new BN ( 15 ) ;
4007
+ const vBid = new BN ( 10 ) ;
4008
+ let slot = 1 ;
4009
+ const oracle = {
4010
+ price : vBid . add ( vAsk ) . div ( new BN ( 2 ) ) ,
4011
+ slot : new BN ( slot ) ,
4012
+ confidence : new BN ( 1 ) ,
4013
+ hasSufficientNumberOfDataPoints : true ,
4014
+ } ;
4015
+
4016
+ const user0 = Keypair . generate ( ) ;
4017
+ const dlob = new DLOB ( ) ;
4018
+ const marketIndex = 0 ;
4019
+ const marketType = MarketType . PERP ;
4020
+
4021
+ // Insert a limit order that starts as taking (in auction) and becomes resting (auction complete)
4022
+ const limitOrder : Order = {
4023
+ status : OrderStatus . OPEN ,
4024
+ orderId : 1 ,
4025
+ marketType : MarketType . PERP ,
4026
+ marketIndex : 0 ,
4027
+ price : new BN ( 11 ) . mul ( PRICE_PRECISION ) ,
4028
+ baseAssetAmount : BASE_PRECISION ,
4029
+ direction : PositionDirection . LONG ,
4030
+ orderType : OrderType . LIMIT ,
4031
+ postOnly : false ,
4032
+ auctionDuration : 10 ,
4033
+ slot : new BN ( slot ) ,
4034
+ auctionStartPrice : vBid ,
4035
+ auctionEndPrice : vAsk ,
4036
+ bitFlags : 1 , // isSignedMsg flag
4037
+ userOrderId : 0 ,
4038
+ baseAssetAmountFilled : new BN ( 0 ) ,
4039
+ quoteAssetAmountFilled : new BN ( 0 ) ,
4040
+ quoteAssetAmount : new BN ( 0 ) ,
4041
+ reduceOnly : false ,
4042
+ triggerPrice : new BN ( 0 ) ,
4043
+ triggerCondition : OrderTriggerCondition . ABOVE ,
4044
+ existingPositionDirection : PositionDirection . LONG ,
4045
+ immediateOrCancel : false ,
4046
+ oraclePriceOffset : 0 ,
4047
+ maxTs : ZERO ,
4048
+ postedSlotTail : 0 ,
4049
+ } ;
4050
+ dlob . insertSignedMsgOrder ( limitOrder , user0 . publicKey . toString ( ) , false ) ;
4051
+
4052
+ // Initially, the order should be in taking orders (auction not complete)
4053
+ let takingBids = Array . from (
4054
+ dlob . getTakingBids ( marketIndex , marketType , slot , oracle )
4055
+ ) ;
4056
+ let signedMsgTakingBids = takingBids . filter ( ( node ) => node . isSignedMsg ) ;
4057
+ expect ( signedMsgTakingBids . length ) . to . equal ( 1 ) ;
4058
+ expect ( signedMsgTakingBids [ 0 ] . order . orderId ) . to . equal ( 1 ) ;
4059
+
4060
+ let restingBids = Array . from (
4061
+ dlob . getRestingLimitBids ( marketIndex , slot , marketType , oracle )
4062
+ ) ;
4063
+ let signedMsgRestingBids = restingBids . filter ( ( node ) => node . isSignedMsg ) ;
4064
+ expect ( signedMsgRestingBids . length ) . to . equal ( 0 ) ;
4065
+
4066
+ // After auction duration, the order should move to resting orders
4067
+ slot = 12 ; // slot + auctionDuration + 1
4068
+ oracle . slot = new BN ( slot ) ;
4069
+
4070
+ takingBids = Array . from (
4071
+ dlob . getTakingBids ( marketIndex , marketType , slot , oracle )
4072
+ ) ;
4073
+ signedMsgTakingBids = takingBids . filter ( ( node ) => node . isSignedMsg ) ;
4074
+ expect ( signedMsgTakingBids . length ) . to . equal ( 0 ) ;
4075
+
4076
+ restingBids = Array . from (
4077
+ dlob . getRestingLimitBids ( marketIndex , slot , marketType , oracle )
4078
+ ) ;
4079
+ signedMsgRestingBids = restingBids . filter ( ( node ) => node . isSignedMsg ) ;
4080
+ expect ( signedMsgRestingBids . length ) . to . equal ( 1 ) ;
4081
+ expect ( signedMsgRestingBids [ 0 ] . order . orderId ) . to . equal ( 1 ) ;
4082
+ } ) ;
4083
+
4084
+ it ( 'DLOB signedMsgOrder filtering - postOnly orders always resting' , ( ) => {
4085
+ const vAsk = new BN ( 15 ) ;
4086
+ const vBid = new BN ( 10 ) ;
4087
+ const slot = 1 ;
4088
+ const oracle = {
4089
+ price : vBid . add ( vAsk ) . div ( new BN ( 2 ) ) ,
4090
+ slot : new BN ( slot ) ,
4091
+ confidence : new BN ( 1 ) ,
4092
+ hasSufficientNumberOfDataPoints : true ,
4093
+ } ;
4094
+
4095
+ const user0 = Keypair . generate ( ) ;
4096
+ const dlob = new DLOB ( ) ;
4097
+ const marketIndex = 0 ;
4098
+ const marketType = MarketType . PERP ;
4099
+
4100
+ // Insert a postOnly limit order (should always be resting regardless of auction state)
4101
+ const postOnlyOrder : Order = {
4102
+ status : OrderStatus . OPEN ,
4103
+ orderId : 1 ,
4104
+ marketType : MarketType . PERP ,
4105
+ marketIndex : 0 ,
4106
+ price : new BN ( 11 ) . mul ( PRICE_PRECISION ) ,
4107
+ baseAssetAmount : BASE_PRECISION ,
4108
+ direction : PositionDirection . LONG ,
4109
+ orderType : OrderType . LIMIT ,
4110
+ postOnly : true ,
4111
+ auctionDuration : 10 ,
4112
+ slot : new BN ( slot ) ,
4113
+ auctionStartPrice : vBid ,
4114
+ auctionEndPrice : vAsk ,
4115
+ bitFlags : 1 , // isSignedMsg flag
4116
+ userOrderId : 0 ,
4117
+ baseAssetAmountFilled : new BN ( 0 ) ,
4118
+ quoteAssetAmountFilled : new BN ( 0 ) ,
4119
+ quoteAssetAmount : new BN ( 0 ) ,
4120
+ reduceOnly : false ,
4121
+ triggerPrice : new BN ( 0 ) ,
4122
+ triggerCondition : OrderTriggerCondition . ABOVE ,
4123
+ existingPositionDirection : PositionDirection . LONG ,
4124
+ immediateOrCancel : false ,
4125
+ oraclePriceOffset : 0 ,
4126
+ maxTs : ZERO ,
4127
+ postedSlotTail : 0 ,
4128
+ } ;
4129
+ dlob . insertSignedMsgOrder ( postOnlyOrder , user0 . publicKey . toString ( ) , false ) ;
4130
+
4131
+ // PostOnly orders should always be in resting orders, never in taking orders
4132
+ const takingBids = Array . from (
4133
+ dlob . getTakingBids ( marketIndex , marketType , slot , oracle )
4134
+ ) ;
4135
+ const signedMsgTakingBids = takingBids . filter ( ( node ) => node . isSignedMsg ) ;
4136
+ expect ( signedMsgTakingBids . length ) . to . equal ( 0 ) ;
4137
+
4138
+ const restingBids = Array . from (
4139
+ dlob . getRestingLimitBids ( marketIndex , slot , marketType , oracle )
4140
+ ) ;
4141
+ const signedMsgRestingBids = restingBids . filter ( ( node ) => node . isSignedMsg ) ;
4142
+ expect ( signedMsgRestingBids . length ) . to . equal ( 1 ) ;
4143
+ expect ( signedMsgRestingBids [ 0 ] . order . orderId ) . to . equal ( 1 ) ;
4144
+ } ) ;
3862
4145
} ) ;
3863
4146
3864
4147
describe ( 'DLOB Spot Tests' , ( ) => {
0 commit comments