@@ -271,14 +271,44 @@ impl Service {
271
271
Ok ( ( ) )
272
272
}
273
273
274
+ /// Checks if the instruction provided meets the criteria for validity.
275
+ /// Instruction must either be an approved program instruction or not contain the user wallet in its accounts.
274
276
fn validate_swap_transaction_instruction (
275
277
& self ,
276
278
program_id : & Pubkey ,
277
279
ix : & CompiledInstruction ,
278
280
user_wallet : & Pubkey ,
279
281
accounts : & [ Pubkey ] ,
282
+ ) -> Result < ( ) , InstructionError > {
283
+ if self
284
+ . check_approved_program_instruction ( program_id, ix)
285
+ . is_ok ( )
286
+ {
287
+ Ok ( ( ) )
288
+ } else {
289
+ ix. accounts . iter ( ) . try_for_each ( |i| {
290
+ if accounts
291
+ . get ( * i as usize )
292
+ . expect ( "account index out of bounds" )
293
+ == user_wallet
294
+ {
295
+ return Err ( InstructionError :: UnsupportedInvocationOfUserWallet ) ;
296
+ }
297
+ Ok ( ( ) )
298
+ } ) ?;
299
+
300
+ Ok ( ( ) )
301
+ }
302
+ }
303
+
304
+ /// Checks if the instruction is an approved program instruction for swap transactions.
305
+ fn check_approved_program_instruction (
306
+ & self ,
307
+ program_id : & Pubkey ,
308
+ ix : & CompiledInstruction ,
280
309
) -> Result < ( ) , InstructionError > {
281
310
if * program_id == system_program:: id ( ) {
311
+ // approve only system program transfer instructions
282
312
if matches ! (
283
313
bincode:: deserialize:: <SystemInstruction >( & ix. data) ,
284
314
Ok ( SystemInstruction :: Transfer { .. } )
@@ -291,8 +321,11 @@ impl Service {
291
321
let ix_parsed = TokenInstruction :: unpack ( & ix. data )
292
322
. map_err ( InstructionError :: InvalidSplTokenInstruction ) ?;
293
323
match ix_parsed {
324
+ // approve token program close account instructions
294
325
TokenInstruction :: CloseAccount { .. } => Ok ( ( ) ) ,
326
+ // approve token program sync native (for WSOL) instructions
295
327
TokenInstruction :: SyncNative { .. } => Ok ( ( ) ) ,
328
+ // other token program instructions are not approved
296
329
_ => Err ( InstructionError :: UnsupportedSplTokenInstruction ( format ! (
297
330
"{:?}" ,
298
331
ix_parsed
@@ -304,28 +337,21 @@ impl Service {
304
337
InstructionError :: InvalidAssociatedTokenAccountInstruction ( e. to_string ( ) )
305
338
} ) ?;
306
339
match ix_parsed {
340
+ // approve associated token account creation
307
341
AssociatedTokenAccountInstruction :: Create => Ok ( ( ) ) ,
342
+ // approve associated token account idempotent creation
308
343
AssociatedTokenAccountInstruction :: CreateIdempotent => Ok ( ( ) ) ,
344
+ // other associated token account instructions are not approved
309
345
_ => Err ( InstructionError :: UnsupportedAssociatedTokenAccountInstruction ( ix_parsed) ) ,
310
346
}
311
347
} else if * program_id == self . config . chain_config . express_relay . program_id
312
348
|| * program_id == spl_memo_client:: ID
313
349
|| * program_id == compute_budget:: id ( )
314
350
{
351
+ // all express relay program, memo program, and compute budget program instructions are allowed
315
352
Ok ( ( ) )
316
353
} else {
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 ( ( ) )
354
+ Err ( InstructionError :: UnapprovedProgramId ( * program_id) )
329
355
}
330
356
}
331
357
@@ -2283,7 +2309,7 @@ mod tests {
2283
2309
}
2284
2310
2285
2311
#[ tokio:: test]
2286
- async fn test_verify_bid_when_unsupported_system_program_instruction ( ) {
2312
+ async fn test_check_approved_program_when_unsupported_system_program_instruction ( ) {
2287
2313
let ( service, opportunities) = get_service ( true ) ;
2288
2314
let opportunity = opportunities. user_token_specified . clone ( ) ;
2289
2315
let bid_amount = 1 ;
@@ -2319,25 +2345,27 @@ mod tests {
2319
2345
) ,
2320
2346
] ;
2321
2347
for instruction in instructions. into_iter ( ) {
2322
- let result = get_verify_bid_result (
2323
- service. clone ( ) ,
2324
- searcher. insecure_clone ( ) ,
2325
- vec ! [ instruction, swap_instruction. clone( ) ] ,
2326
- opportunities. user_token_specified . clone ( ) ,
2327
- )
2328
- . await ;
2348
+ let program_id = instruction. program_id ;
2349
+ let transaction = Transaction :: new_with_payer (
2350
+ & [ instruction. clone ( ) , swap_instruction. clone ( ) ] ,
2351
+ Some ( & searcher. pubkey ( ) ) ,
2352
+ ) ;
2353
+ let instruction = transaction
2354
+ . message ( )
2355
+ . instructions
2356
+ . first ( )
2357
+ . expect ( "Expected at least one instruction" )
2358
+ . clone ( ) ;
2359
+ let result = service. check_approved_program_instruction ( & program_id, & instruction) ;
2329
2360
assert_eq ! (
2330
2361
result. unwrap_err( ) ,
2331
- RestError :: InvalidInstruction (
2332
- Some ( 0 ) ,
2333
- InstructionError :: UnsupportedSystemProgramInstruction
2334
- )
2362
+ InstructionError :: UnsupportedSystemProgramInstruction
2335
2363
) ;
2336
2364
}
2337
2365
}
2338
2366
2339
2367
#[ tokio:: test]
2340
- async fn test_verify_bid_when_unsupported_token_instruction ( ) {
2368
+ async fn test_check_approved_program_when_unsupported_token_instruction ( ) {
2341
2369
let ( service, opportunities) = get_service ( true ) ;
2342
2370
let opportunity = opportunities. user_token_specified . clone ( ) ;
2343
2371
let bid_amount = 1 ;
@@ -2445,25 +2473,27 @@ mod tests {
2445
2473
for instruction in instructions. into_iter ( ) {
2446
2474
let data = instruction. data . clone ( ) ;
2447
2475
let ix_parsed = TokenInstruction :: unpack ( & data) . unwrap ( ) ;
2448
- let result = get_verify_bid_result (
2449
- service. clone ( ) ,
2450
- searcher. insecure_clone ( ) ,
2451
- vec ! [ instruction, swap_instruction. clone( ) ] ,
2452
- opportunities. user_token_specified . clone ( ) ,
2453
- )
2454
- . await ;
2476
+ let program_id = instruction. program_id ;
2477
+ let transaction = Transaction :: new_with_payer (
2478
+ & [ instruction. clone ( ) , swap_instruction. clone ( ) ] ,
2479
+ Some ( & searcher. pubkey ( ) ) ,
2480
+ ) ;
2481
+ let instruction = transaction
2482
+ . message ( )
2483
+ . instructions
2484
+ . first ( )
2485
+ . expect ( "Expected at least one instruction" )
2486
+ . clone ( ) ;
2487
+ let result = service. check_approved_program_instruction ( & program_id, & instruction) ;
2455
2488
assert_eq ! (
2456
2489
result. unwrap_err( ) ,
2457
- RestError :: InvalidInstruction (
2458
- Some ( 0 ) ,
2459
- InstructionError :: UnsupportedSplTokenInstruction ( format!( "{:?}" , ix_parsed) ) ,
2460
- )
2490
+ InstructionError :: UnsupportedSplTokenInstruction ( format!( "{:?}" , ix_parsed) )
2461
2491
) ;
2462
2492
}
2463
2493
}
2464
2494
2465
2495
#[ tokio:: test]
2466
- async fn test_verify_bid_when_unsupported_associated_token_account_instruction ( ) {
2496
+ async fn test_check_approved_program_when_unsupported_associated_token_account_instruction ( ) {
2467
2497
let ( service, opportunities) = get_service ( true ) ;
2468
2498
let opportunity = opportunities. user_token_specified . clone ( ) ;
2469
2499
let bid_amount = 1 ;
@@ -2483,30 +2513,68 @@ mod tests {
2483
2513
& Pubkey :: new_unique( ) ,
2484
2514
& spl_token:: id( ) ,
2485
2515
) ] ;
2516
+
2486
2517
for instruction in instructions. into_iter ( ) {
2487
2518
let data = instruction. data . clone ( ) ;
2488
2519
let ix_parsed = AssociatedTokenAccountInstruction :: try_from_slice ( & data)
2489
2520
. map_err ( |e| {
2490
2521
InstructionError :: InvalidAssociatedTokenAccountInstruction ( e. to_string ( ) )
2491
2522
} )
2492
2523
. unwrap ( ) ;
2493
- let result = get_verify_bid_result (
2494
- service. clone ( ) ,
2495
- searcher. insecure_clone ( ) ,
2496
- vec ! [ instruction, swap_instruction. clone( ) ] ,
2497
- opportunities. user_token_specified . clone ( ) ,
2498
- )
2499
- . await ;
2524
+ let program_id = instruction. program_id ;
2525
+ let transaction = Transaction :: new_with_payer (
2526
+ & [ instruction. clone ( ) , swap_instruction. clone ( ) ] ,
2527
+ Some ( & searcher. pubkey ( ) ) ,
2528
+ ) ;
2529
+ let instruction = transaction
2530
+ . message ( )
2531
+ . instructions
2532
+ . first ( )
2533
+ . expect ( "Expected at least one instruction" )
2534
+ . clone ( ) ;
2535
+ let result = service. check_approved_program_instruction ( & program_id, & instruction) ;
2500
2536
assert_eq ! (
2501
2537
result. unwrap_err( ) ,
2502
- RestError :: InvalidInstruction (
2503
- Some ( 0 ) ,
2504
- InstructionError :: UnsupportedAssociatedTokenAccountInstruction ( ix_parsed) ,
2505
- )
2538
+ InstructionError :: UnsupportedAssociatedTokenAccountInstruction ( ix_parsed)
2506
2539
) ;
2507
2540
}
2508
2541
}
2509
2542
2543
+ #[ tokio:: test]
2544
+ async fn test_check_approved_program_when_unapproved_program_id ( ) {
2545
+ let ( service, opportunities) = get_service ( true ) ;
2546
+ let opportunity = opportunities. user_token_specified . clone ( ) ;
2547
+ let bid_amount = 1 ;
2548
+ let searcher = Keypair :: new ( ) ;
2549
+ let swap_instruction = svm:: Svm :: get_swap_instruction ( GetSwapInstructionParams {
2550
+ searcher : searcher. pubkey ( ) ,
2551
+ opportunity_params : get_opportunity_params ( opportunity. clone ( ) ) ,
2552
+ bid_amount,
2553
+ deadline : ( OffsetDateTime :: now_utc ( ) + Duration :: seconds ( 30 ) ) . unix_timestamp ( ) ,
2554
+ fee_receiver_relayer : Pubkey :: new_unique ( ) ,
2555
+ relayer_signer : service. config . chain_config . express_relay . relayer . pubkey ( ) ,
2556
+ } )
2557
+ . unwrap ( ) ;
2558
+ let program_id = Pubkey :: new_unique ( ) ;
2559
+ let instruction = Instruction :: new_with_bincode ( program_id, & "" , vec ! [ ] ) ;
2560
+
2561
+ let transaction = Transaction :: new_with_payer (
2562
+ & [ instruction. clone ( ) , swap_instruction. clone ( ) ] ,
2563
+ Some ( & searcher. pubkey ( ) ) ,
2564
+ ) ;
2565
+ let instruction = transaction
2566
+ . message ( )
2567
+ . instructions
2568
+ . first ( )
2569
+ . expect ( "Expected at least one instruction" )
2570
+ . clone ( ) ;
2571
+ let result = service. check_approved_program_instruction ( & program_id, & instruction) ;
2572
+ assert_eq ! (
2573
+ result. unwrap_err( ) ,
2574
+ InstructionError :: UnapprovedProgramId ( program_id)
2575
+ ) ;
2576
+ }
2577
+
2510
2578
#[ tokio:: test]
2511
2579
async fn test_verify_bid_when_arbitrary_program_invokes_user ( ) {
2512
2580
let ( service, opportunities) = get_service ( true ) ;
0 commit comments