@@ -251,7 +251,9 @@ impl Service {
251
251
fn validate_swap_transaction_instructions (
252
252
& self ,
253
253
tx : & VersionedTransaction ,
254
+ user_wallet : & Pubkey ,
254
255
) -> Result < ( ) , RestError > {
256
+ let accounts = tx. message . static_account_keys ( ) ;
255
257
tx. message
256
258
. instructions ( )
257
259
. iter ( )
@@ -260,6 +262,8 @@ impl Service {
260
262
self . validate_swap_transaction_instruction (
261
263
ix. program_id ( tx. message . static_account_keys ( ) ) ,
262
264
ix,
265
+ user_wallet,
266
+ accounts,
263
267
)
264
268
. map_err ( |e| RestError :: InvalidInstruction ( Some ( index) , e) )
265
269
} ) ?;
@@ -271,6 +275,8 @@ impl Service {
271
275
& self ,
272
276
program_id : & Pubkey ,
273
277
ix : & CompiledInstruction ,
278
+ user_wallet : & Pubkey ,
279
+ accounts : & [ Pubkey ] ,
274
280
) -> Result < ( ) , InstructionError > {
275
281
if * program_id == system_program:: id ( ) {
276
282
if matches ! (
@@ -308,7 +314,18 @@ impl Service {
308
314
{
309
315
Ok ( ( ) )
310
316
} else {
311
- Err ( InstructionError :: UnsupportedProgram ( * program_id) )
317
+ ix. accounts . iter ( ) . try_for_each ( |i| {
318
+ if accounts
319
+ . get ( * i as usize )
320
+ . expect ( "account index out of bounds" )
321
+ == user_wallet
322
+ {
323
+ return Err ( InstructionError :: UnsupportedInvocationOfUserWallet ) ;
324
+ }
325
+ Ok ( ( ) )
326
+ } ) ?;
327
+
328
+ Ok ( ( ) )
312
329
}
313
330
}
314
331
@@ -1020,13 +1037,6 @@ impl Service {
1020
1037
} )
1021
1038
. await
1022
1039
. ok_or ( RestError :: SwapOpportunityNotFound ) ?;
1023
- self . validate_swap_transaction_instructions (
1024
- bid_chain_data_create_svm. get_transaction ( ) ,
1025
- ) ?;
1026
- let quote_tokens = get_swap_quote_tokens ( & opp) ;
1027
- let opportunity_swap_data = get_opportunity_swap_data ( & opp) ;
1028
- self . check_svm_swap_bid_fields ( bid_data, opportunity_swap_data, & quote_tokens)
1029
- . await ?;
1030
1040
1031
1041
let transaction_data = self
1032
1042
. get_bid_transaction_data_swap ( bid_data. transaction . clone ( ) )
@@ -1042,6 +1052,15 @@ impl Service {
1042
1052
..
1043
1053
} = transaction_data. accounts ;
1044
1054
1055
+ self . validate_swap_transaction_instructions (
1056
+ bid_chain_data_create_svm. get_transaction ( ) ,
1057
+ & user_wallet,
1058
+ ) ?;
1059
+ let quote_tokens = get_swap_quote_tokens ( & opp) ;
1060
+ let opportunity_swap_data = get_opportunity_swap_data ( & opp) ;
1061
+ self . check_svm_swap_bid_fields ( bid_data, opportunity_swap_data, & quote_tokens)
1062
+ . await ?;
1063
+
1045
1064
Self :: check_memo_instructions ( & bid_data. transaction , & opportunity_swap_data. memo )
1046
1065
. await ?;
1047
1066
@@ -2266,6 +2285,18 @@ mod tests {
2266
2285
#[ tokio:: test]
2267
2286
async fn test_verify_bid_when_unsupported_system_program_instruction ( ) {
2268
2287
let ( service, opportunities) = get_service ( true ) ;
2288
+ let opportunity = opportunities. user_token_specified . clone ( ) ;
2289
+ let bid_amount = 1 ;
2290
+ let searcher = Keypair :: new ( ) ;
2291
+ let swap_instruction = svm:: Svm :: get_swap_instruction ( GetSwapInstructionParams {
2292
+ searcher : searcher. pubkey ( ) ,
2293
+ opportunity_params : get_opportunity_params ( opportunity. clone ( ) ) ,
2294
+ bid_amount,
2295
+ deadline : ( OffsetDateTime :: now_utc ( ) + Duration :: seconds ( 30 ) ) . unix_timestamp ( ) ,
2296
+ fee_receiver_relayer : Pubkey :: new_unique ( ) ,
2297
+ relayer_signer : service. config . chain_config . express_relay . relayer . pubkey ( ) ,
2298
+ } )
2299
+ . unwrap ( ) ;
2269
2300
let instructions = vec ! [
2270
2301
system_instruction:: advance_nonce_account( & Pubkey :: new_unique( ) , & Pubkey :: new_unique( ) ) ,
2271
2302
system_instruction:: create_account(
@@ -2288,11 +2319,10 @@ mod tests {
2288
2319
) ,
2289
2320
] ;
2290
2321
for instruction in instructions. into_iter ( ) {
2291
- let searcher = Keypair :: new ( ) ;
2292
2322
let result = get_verify_bid_result (
2293
2323
service. clone ( ) ,
2294
- searcher,
2295
- vec ! [ instruction] ,
2324
+ searcher. insecure_clone ( ) ,
2325
+ vec ! [ instruction, swap_instruction . clone ( ) ] ,
2296
2326
opportunities. user_token_specified . clone ( ) ,
2297
2327
)
2298
2328
. await ;
@@ -2309,6 +2339,18 @@ mod tests {
2309
2339
#[ tokio:: test]
2310
2340
async fn test_verify_bid_when_unsupported_token_instruction ( ) {
2311
2341
let ( service, opportunities) = get_service ( true ) ;
2342
+ let opportunity = opportunities. user_token_specified . clone ( ) ;
2343
+ let bid_amount = 1 ;
2344
+ let searcher = Keypair :: new ( ) ;
2345
+ let swap_instruction = svm:: Svm :: get_swap_instruction ( GetSwapInstructionParams {
2346
+ searcher : searcher. pubkey ( ) ,
2347
+ opportunity_params : get_opportunity_params ( opportunity. clone ( ) ) ,
2348
+ bid_amount,
2349
+ deadline : ( OffsetDateTime :: now_utc ( ) + Duration :: seconds ( 30 ) ) . unix_timestamp ( ) ,
2350
+ fee_receiver_relayer : Pubkey :: new_unique ( ) ,
2351
+ relayer_signer : service. config . chain_config . express_relay . relayer . pubkey ( ) ,
2352
+ } )
2353
+ . unwrap ( ) ;
2312
2354
let instructions = vec ! [
2313
2355
spl_token:: instruction:: initialize_account(
2314
2356
& spl_token:: id( ) ,
@@ -2403,11 +2445,10 @@ mod tests {
2403
2445
for instruction in instructions. into_iter ( ) {
2404
2446
let data = instruction. data . clone ( ) ;
2405
2447
let ix_parsed = TokenInstruction :: unpack ( & data) . unwrap ( ) ;
2406
- let searcher = Keypair :: new ( ) ;
2407
2448
let result = get_verify_bid_result (
2408
2449
service. clone ( ) ,
2409
- searcher,
2410
- vec ! [ instruction] ,
2450
+ searcher. insecure_clone ( ) ,
2451
+ vec ! [ instruction, swap_instruction . clone ( ) ] ,
2411
2452
opportunities. user_token_specified . clone ( ) ,
2412
2453
)
2413
2454
. await ;
@@ -2424,6 +2465,18 @@ mod tests {
2424
2465
#[ tokio:: test]
2425
2466
async fn test_verify_bid_when_unsupported_associated_token_account_instruction ( ) {
2426
2467
let ( service, opportunities) = get_service ( true ) ;
2468
+ let opportunity = opportunities. user_token_specified . clone ( ) ;
2469
+ let bid_amount = 1 ;
2470
+ let searcher = Keypair :: new ( ) ;
2471
+ let swap_instruction = svm:: Svm :: get_swap_instruction ( GetSwapInstructionParams {
2472
+ searcher : searcher. pubkey ( ) ,
2473
+ opportunity_params : get_opportunity_params ( opportunity. clone ( ) ) ,
2474
+ bid_amount,
2475
+ deadline : ( OffsetDateTime :: now_utc ( ) + Duration :: seconds ( 30 ) ) . unix_timestamp ( ) ,
2476
+ fee_receiver_relayer : Pubkey :: new_unique ( ) ,
2477
+ relayer_signer : service. config . chain_config . express_relay . relayer . pubkey ( ) ,
2478
+ } )
2479
+ . unwrap ( ) ;
2427
2480
let instructions = vec ! [ recover_nested(
2428
2481
& Pubkey :: new_unique( ) ,
2429
2482
& Pubkey :: new_unique( ) ,
@@ -2437,11 +2490,10 @@ mod tests {
2437
2490
InstructionError :: InvalidAssociatedTokenAccountInstruction ( e. to_string ( ) )
2438
2491
} )
2439
2492
. unwrap ( ) ;
2440
- let searcher = Keypair :: new ( ) ;
2441
2493
let result = get_verify_bid_result (
2442
2494
service. clone ( ) ,
2443
- searcher,
2444
- vec ! [ instruction] ,
2495
+ searcher. insecure_clone ( ) ,
2496
+ vec ! [ instruction, swap_instruction . clone ( ) ] ,
2445
2497
opportunities. user_token_specified . clone ( ) ,
2446
2498
)
2447
2499
. await ;
@@ -2456,29 +2508,113 @@ mod tests {
2456
2508
}
2457
2509
2458
2510
#[ tokio:: test]
2459
- async fn test_verify_bid_when_unsupported_program ( ) {
2511
+ async fn test_verify_bid_when_arbitrary_program_invokes_user ( ) {
2460
2512
let ( service, opportunities) = get_service ( true ) ;
2461
- let program_id = Pubkey :: new_unique ( ) ;
2462
- let instructions = vec ! [ Instruction :: new_with_bincode( program_id, & "" , vec![ ] ) ] ;
2513
+ let opportunity = opportunities. user_token_specified . clone ( ) ;
2514
+ let bid_amount = 1 ;
2515
+ let searcher = Keypair :: new ( ) ;
2516
+ let swap_instruction = svm:: Svm :: get_swap_instruction ( GetSwapInstructionParams {
2517
+ searcher : searcher. pubkey ( ) ,
2518
+ opportunity_params : get_opportunity_params ( opportunity. clone ( ) ) ,
2519
+ bid_amount,
2520
+ deadline : ( OffsetDateTime :: now_utc ( ) + Duration :: seconds ( 30 ) ) . unix_timestamp ( ) ,
2521
+ fee_receiver_relayer : Pubkey :: new_unique ( ) ,
2522
+ relayer_signer : service. config . chain_config . express_relay . relayer . pubkey ( ) ,
2523
+ } )
2524
+ . unwrap ( ) ;
2525
+ let SwapParams {
2526
+ user_wallet_address,
2527
+ router_account : _,
2528
+ permission_account : _,
2529
+ minimum_deadline : _,
2530
+ } = get_opportunity_swap_params ( opportunity) ;
2531
+ let instructions = vec ! [
2532
+ Instruction :: new_with_bincode(
2533
+ Pubkey :: new_unique( ) ,
2534
+ & "" ,
2535
+ vec![ AccountMeta {
2536
+ pubkey: user_wallet_address,
2537
+ is_signer: false ,
2538
+ is_writable: true ,
2539
+ } ] ,
2540
+ ) ,
2541
+ Instruction :: new_with_bincode(
2542
+ Pubkey :: new_unique( ) ,
2543
+ & "" ,
2544
+ vec![ AccountMeta {
2545
+ pubkey: user_wallet_address,
2546
+ is_signer: true ,
2547
+ is_writable: true ,
2548
+ } ] ,
2549
+ ) ,
2550
+ ] ;
2463
2551
for instruction in instructions. into_iter ( ) {
2464
- let searcher = Keypair :: new ( ) ;
2465
2552
let result = get_verify_bid_result (
2466
2553
service. clone ( ) ,
2467
- searcher,
2468
- vec ! [ instruction] ,
2554
+ searcher. insecure_clone ( ) ,
2555
+ vec ! [ instruction, swap_instruction . clone ( ) ] ,
2469
2556
opportunities. user_token_specified . clone ( ) ,
2470
2557
)
2471
2558
. await ;
2472
2559
assert_eq ! (
2473
2560
result. unwrap_err( ) ,
2474
2561
RestError :: InvalidInstruction (
2475
2562
Some ( 0 ) ,
2476
- InstructionError :: UnsupportedProgram ( program_id )
2563
+ InstructionError :: UnsupportedInvocationOfUserWallet
2477
2564
)
2478
2565
) ;
2479
2566
}
2480
2567
}
2481
2568
2569
+ #[ tokio:: test]
2570
+ async fn test_verify_bid_when_arbitrary_program_does_not_invoke_user ( ) {
2571
+ let ( service, opportunities) = get_service ( true ) ;
2572
+ let opportunity = opportunities. user_token_specified . clone ( ) ;
2573
+ let bid_amount = 1 ;
2574
+ let searcher = Keypair :: new ( ) ;
2575
+ let swap_instruction = svm:: Svm :: get_swap_instruction ( GetSwapInstructionParams {
2576
+ searcher : searcher. pubkey ( ) ,
2577
+ opportunity_params : get_opportunity_params ( opportunity. clone ( ) ) ,
2578
+ bid_amount,
2579
+ deadline : ( OffsetDateTime :: now_utc ( ) + Duration :: seconds ( 30 ) ) . unix_timestamp ( ) ,
2580
+ fee_receiver_relayer : Pubkey :: new_unique ( ) ,
2581
+ relayer_signer : service. config . chain_config . express_relay . relayer . pubkey ( ) ,
2582
+ } )
2583
+ . unwrap ( ) ;
2584
+ let instructions = vec ! [ Instruction :: new_with_bincode(
2585
+ Pubkey :: new_unique( ) ,
2586
+ & "" ,
2587
+ vec![ ] ,
2588
+ ) ] ;
2589
+ let swap_params = get_opportunity_swap_params ( opportunity) ;
2590
+
2591
+ for instruction in instructions. into_iter ( ) {
2592
+ let instructions = vec ! [ instruction, swap_instruction. clone( ) ] ;
2593
+ let mut transaction =
2594
+ Transaction :: new_with_payer ( & instructions, Some ( & searcher. pubkey ( ) ) ) ;
2595
+ transaction. partial_sign ( & [ searcher. insecure_clone ( ) ] , Hash :: default ( ) ) ;
2596
+ let result = get_verify_bid_result (
2597
+ service. clone ( ) ,
2598
+ searcher. insecure_clone ( ) ,
2599
+ instructions,
2600
+ opportunities. user_token_specified . clone ( ) ,
2601
+ )
2602
+ . await
2603
+ . unwrap ( ) ;
2604
+
2605
+ assert_eq ! (
2606
+ result. 0 ,
2607
+ BidChainDataSvm {
2608
+ transaction: transaction. into( ) ,
2609
+ permission_account: swap_params. permission_account,
2610
+ router: swap_params. router_account,
2611
+ bid_payment_instruction_type: BidPaymentInstructionType :: Swap ,
2612
+ }
2613
+ ) ;
2614
+ assert_eq ! ( result. 1 , bid_amount) ;
2615
+ }
2616
+ }
2617
+
2482
2618
#[ tokio:: test]
2483
2619
async fn test_verify_bid_when_multiple_express_relay_instructions ( ) {
2484
2620
let ( service, opportunities) = get_service ( true ) ;
0 commit comments