@@ -506,7 +506,6 @@ impl Service {
506
506
async fn extract_transfer_instructions (
507
507
& self ,
508
508
tx : & VersionedTransaction ,
509
- from : Pubkey ,
510
509
) -> Result < Vec < TransferInstructionData > , RestError > {
511
510
let instructions: Vec < ( usize , & CompiledInstruction ) > =
512
511
Self :: extract_program_instructions ( tx, & system_program:: id ( ) )
@@ -537,23 +536,40 @@ impl Service {
537
536
) )
538
537
}
539
538
} ;
540
- if transfer_instruction. from == from {
541
- result. push ( transfer_instruction) ;
542
- }
539
+ result. push ( transfer_instruction) ;
543
540
}
544
541
Ok ( result)
545
542
}
546
543
547
- // Ensures that any SOL transfer instruction from the user is only carried out when user token mint is SOL and user needs to wrap SOL.
548
- async fn check_user_transfer_instruction (
544
+ // Ensures no SOL transfers from relayer. Ensures that any SOL transfer instruction from the user is only carried out when user token mint is SOL and user needs to wrap SOL.
545
+ async fn check_transfer_instructions (
549
546
& self ,
550
547
tx : & VersionedTransaction ,
551
548
transaction_data : & entities:: BidTransactionDataSwap ,
552
549
opportunity_swap_data : & OpportunitySvmProgramSwap ,
553
550
) -> Result < ( ) , RestError > {
554
- let user_transfer_instructions = self
555
- . extract_transfer_instructions ( tx, transaction_data. accounts . user_wallet )
556
- . await ?;
551
+ let transfer_instructions: Vec < TransferInstructionData > =
552
+ self . extract_transfer_instructions ( tx) . await ?;
553
+
554
+ let relayer_transfer_instructions = transfer_instructions
555
+ . iter ( )
556
+ . filter ( |instruction| {
557
+ instruction. from == self . config . chain_config . express_relay . relayer . pubkey ( )
558
+ } )
559
+ . collect :: < Vec < _ > > ( ) ;
560
+ if !relayer_transfer_instructions. is_empty ( ) {
561
+ return Err ( RestError :: InvalidInstruction (
562
+ relayer_transfer_instructions
563
+ . first ( )
564
+ . map ( |instruction| instruction. index ) ,
565
+ InstructionError :: RelayerTransferInstructionNotAllowed ,
566
+ ) ) ;
567
+ }
568
+
569
+ let user_transfer_instructions = transfer_instructions
570
+ . iter ( )
571
+ . filter ( |instruction| instruction. from == transaction_data. accounts . user_wallet )
572
+ . collect :: < Vec < _ > > ( ) ;
557
573
if user_transfer_instructions. len ( ) > 1 {
558
574
return Err ( RestError :: InvalidInstruction (
559
575
user_transfer_instructions
@@ -877,7 +893,7 @@ impl Service {
877
893
}
878
894
}
879
895
880
- // Ensures that the necessary create ATA instructions are present in the transaction (and are correctly paid for).
896
+ // Ensures that the necessary create ATA instructions are present in the transaction (and are appropriately paid for).
881
897
async fn check_create_ata_instructions (
882
898
& self ,
883
899
tx : & VersionedTransaction ,
@@ -990,7 +1006,7 @@ impl Service {
990
1006
transaction_data : & entities:: BidTransactionDataSwap ,
991
1007
opportunity_swap_data : & OpportunitySvmProgramSwap ,
992
1008
) -> Result < ( ) , RestError > {
993
- self . check_user_transfer_instruction ( tx, transaction_data, opportunity_swap_data)
1009
+ self . check_transfer_instructions ( tx, transaction_data, opportunity_swap_data)
994
1010
. await ?;
995
1011
if transaction_data. accounts . mint_user == spl_token:: native_mint:: id ( ) {
996
1012
// User has to wrap Sol
@@ -3232,6 +3248,42 @@ mod tests {
3232
3248
) ;
3233
3249
}
3234
3250
3251
+ #[ tokio:: test]
3252
+ async fn test_verify_bid_when_relayer_transfer_instruction ( ) {
3253
+ let ( service, opportunities) = get_service ( false ) ;
3254
+ let searcher = Keypair :: new ( ) ;
3255
+ let opportunity = opportunities. user_token_specified . clone ( ) ;
3256
+ let swap_instruction = svm:: Svm :: get_swap_instruction ( GetSwapInstructionParams {
3257
+ searcher : searcher. pubkey ( ) ,
3258
+ opportunity_params : get_opportunity_params ( opportunity. clone ( ) ) ,
3259
+ bid_amount : 1 ,
3260
+ deadline : ( OffsetDateTime :: now_utc ( ) + Duration :: seconds ( 30 ) )
3261
+ . unix_timestamp ( ) ,
3262
+ fee_receiver_relayer : Pubkey :: new_unique ( ) ,
3263
+ relayer_signer : service. config . chain_config . express_relay . relayer . pubkey ( ) ,
3264
+ } )
3265
+ . unwrap ( ) ;
3266
+ let transfer_instruction = system_instruction:: transfer (
3267
+ & service. config . chain_config . express_relay . relayer . pubkey ( ) ,
3268
+ & Pubkey :: new_unique ( ) ,
3269
+ 1 ,
3270
+ ) ;
3271
+ let result = get_verify_bid_result (
3272
+ service,
3273
+ searcher,
3274
+ vec ! [ swap_instruction, transfer_instruction] ,
3275
+ opportunity,
3276
+ )
3277
+ . await ;
3278
+ assert_eq ! (
3279
+ result. unwrap_err( ) ,
3280
+ RestError :: InvalidInstruction (
3281
+ Some ( 1 ) ,
3282
+ InstructionError :: RelayerTransferInstructionNotAllowed
3283
+ )
3284
+ ) ;
3285
+ }
3286
+
3235
3287
#[ tokio:: test]
3236
3288
async fn test_verify_bid_when_no_close_account_instruction_is_allowed ( ) {
3237
3289
let ( service, opportunities) = get_service ( false ) ;
0 commit comments