1
1
use {
2
2
crate :: {
3
- client:: { ProgramClient , ProgramClientError , SendTransaction , SimulateTransaction } ,
3
+ client:: {
4
+ ProgramClient , ProgramClientError , SendTransaction , SimulateTransaction ,
5
+ SimulationResult ,
6
+ } ,
4
7
proof_generation:: transfer_with_fee_split_proof_data,
5
8
} ,
6
9
futures:: { future:: join_all, try_join} ,
@@ -522,6 +525,45 @@ where
522
525
}
523
526
}
524
527
528
+ /// Helper function to add a compute unit limit instruction to a given set
529
+ /// of instructions
530
+ async fn add_compute_unit_limit_from_simulation (
531
+ & self ,
532
+ instructions : & mut Vec < Instruction > ,
533
+ blockhash : & Hash ,
534
+ ) -> TokenResult < ( ) > {
535
+ // add a max compute unit limit instruction for the simulation
536
+ const MAX_COMPUTE_UNIT_LIMIT : u32 = 1_400_000 ;
537
+ instructions. push ( ComputeBudgetInstruction :: set_compute_unit_limit (
538
+ MAX_COMPUTE_UNIT_LIMIT ,
539
+ ) ) ;
540
+
541
+ let transaction = Transaction :: new_unsigned ( Message :: new_with_blockhash (
542
+ instructions,
543
+ Some ( & self . payer . pubkey ( ) ) ,
544
+ blockhash,
545
+ ) ) ;
546
+ let simulation_result = self
547
+ . client
548
+ . simulate_transaction ( & transaction)
549
+ . await
550
+ . map_err ( TokenError :: Client ) ?;
551
+ if let Ok ( units_consumed) = simulation_result. get_compute_units_consumed ( ) {
552
+ // Overwrite the compute unit limit instruction with the actual units consumed
553
+ let compute_unit_limit =
554
+ u32:: try_from ( units_consumed) . map_err ( |x| TokenError :: Client ( x. into ( ) ) ) ?;
555
+ instructions
556
+ . last_mut ( )
557
+ . expect ( "Compute budget instruction was added earlier" )
558
+ . data = ComputeBudgetInstruction :: set_compute_unit_limit ( compute_unit_limit) . data ;
559
+ } else {
560
+ // `get_compute_units_consumed()` fails for offline signing, so we
561
+ // catch that error and remove the instruction that was added
562
+ instructions. pop ( ) ;
563
+ }
564
+ Ok ( ( ) )
565
+ }
566
+
525
567
async fn construct_tx < S : Signers > (
526
568
& self ,
527
569
token_instructions : & [ Instruction ] ,
@@ -549,18 +591,6 @@ where
549
591
550
592
instructions. extend_from_slice ( token_instructions) ;
551
593
552
- if let Some ( compute_unit_limit) = self . compute_unit_limit {
553
- instructions. push ( ComputeBudgetInstruction :: set_compute_unit_limit (
554
- compute_unit_limit,
555
- ) ) ;
556
- }
557
-
558
- if let Some ( compute_unit_price) = self . compute_unit_price {
559
- instructions. push ( ComputeBudgetInstruction :: set_compute_unit_price (
560
- compute_unit_price,
561
- ) ) ;
562
- }
563
-
564
594
let blockhash = if let ( Some ( nonce_account) , Some ( nonce_authority) , Some ( nonce_blockhash) ) = (
565
595
self . nonce_account ,
566
596
& self . nonce_authority ,
@@ -579,6 +609,25 @@ where
579
609
. map_err ( TokenError :: Client ) ?
580
610
} ;
581
611
612
+ if let Some ( compute_unit_price) = self . compute_unit_price {
613
+ instructions. push ( ComputeBudgetInstruction :: set_compute_unit_price (
614
+ compute_unit_price,
615
+ ) ) ;
616
+ }
617
+
618
+ // The simulation to find out the compute unit usage must be run after
619
+ // all instructions have been added to the transaction, so be sure to
620
+ // keep this instruction as the last one before creating and sending the
621
+ // transaction.
622
+ if let Some ( compute_unit_limit) = self . compute_unit_limit {
623
+ instructions. push ( ComputeBudgetInstruction :: set_compute_unit_limit (
624
+ compute_unit_limit,
625
+ ) ) ;
626
+ } else {
627
+ self . add_compute_unit_limit_from_simulation ( & mut instructions, & blockhash)
628
+ . await ?;
629
+ }
630
+
582
631
let message = Message :: new_with_blockhash ( & instructions, fee_payer, & blockhash) ;
583
632
let mut transaction = Transaction :: new_unsigned ( message) ;
584
633
let signing_pubkeys = signing_keypairs. pubkeys ( ) ;
@@ -2087,12 +2136,26 @@ where
2087
2136
)
2088
2137
. await ?;
2089
2138
2090
- self . process_ixs (
2139
+ // This instruction is right at the transaction size limit, so we cannot
2140
+ // add any other instructions to it
2141
+ let blockhash = self
2142
+ . client
2143
+ . get_latest_blockhash ( )
2144
+ . await
2145
+ . map_err ( TokenError :: Client ) ?;
2146
+
2147
+ let transaction = Transaction :: new_signed_with_payer (
2091
2148
& [ instruction_type
2092
2149
. encode_verify_proof ( Some ( withdraw_proof_context_state_info) , withdraw_proof_data) ] ,
2093
- & [ ] as & [ & dyn Signer ; 0 ] ,
2094
- )
2095
- . await
2150
+ Some ( & self . payer . pubkey ( ) ) ,
2151
+ & [ self . payer . as_ref ( ) ] ,
2152
+ blockhash,
2153
+ ) ;
2154
+
2155
+ self . client
2156
+ . send_transaction ( & transaction)
2157
+ . await
2158
+ . map_err ( TokenError :: Client )
2096
2159
}
2097
2160
2098
2161
/// Transfer tokens confidentially
@@ -2571,12 +2634,24 @@ where
2571
2634
2572
2635
// This instruction is right at the transaction size limit, but in the
2573
2636
// future it might be able to support the transfer too
2574
- self . process_ixs (
2637
+ let blockhash = self
2638
+ . client
2639
+ . get_latest_blockhash ( )
2640
+ . await
2641
+ . map_err ( TokenError :: Client ) ?;
2642
+
2643
+ let transaction = Transaction :: new_signed_with_payer (
2575
2644
& [ instruction_type
2576
2645
. encode_verify_proof ( Some ( range_proof_context_state_info) , range_proof_data) ] ,
2577
- & [ ] as & [ & dyn Signer ; 0 ] ,
2578
- )
2579
- . await
2646
+ Some ( & self . payer . pubkey ( ) ) ,
2647
+ & [ self . payer . as_ref ( ) ] ,
2648
+ blockhash,
2649
+ ) ;
2650
+
2651
+ self . client
2652
+ . send_transaction ( & transaction)
2653
+ . await
2654
+ . map_err ( TokenError :: Client )
2580
2655
}
2581
2656
2582
2657
/// Create a range proof context state account with a confidential transfer
0 commit comments