11use {
22 crate :: {
3- client:: { ProgramClient , ProgramClientError , SendTransaction , SimulateTransaction } ,
3+ client:: {
4+ ProgramClient , ProgramClientError , SendTransaction , SimulateTransaction ,
5+ SimulationResult ,
6+ } ,
47 proof_generation:: transfer_with_fee_split_proof_data,
58 } ,
69 futures:: { future:: join_all, try_join} ,
@@ -522,6 +525,45 @@ where
522525 }
523526 }
524527
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+
525567 async fn construct_tx < S : Signers > (
526568 & self ,
527569 token_instructions : & [ Instruction ] ,
@@ -549,18 +591,6 @@ where
549591
550592 instructions. extend_from_slice ( token_instructions) ;
551593
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-
564594 let blockhash = if let ( Some ( nonce_account) , Some ( nonce_authority) , Some ( nonce_blockhash) ) = (
565595 self . nonce_account ,
566596 & self . nonce_authority ,
@@ -579,6 +609,25 @@ where
579609 . map_err ( TokenError :: Client ) ?
580610 } ;
581611
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+
582631 let message = Message :: new_with_blockhash ( & instructions, fee_payer, & blockhash) ;
583632 let mut transaction = Transaction :: new_unsigned ( message) ;
584633 let signing_pubkeys = signing_keypairs. pubkeys ( ) ;
@@ -2087,12 +2136,26 @@ where
20872136 )
20882137 . await ?;
20892138
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 (
20912148 & [ instruction_type
20922149 . 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 )
20962159 }
20972160
20982161 /// Transfer tokens confidentially
@@ -2571,12 +2634,24 @@ where
25712634
25722635 // This instruction is right at the transaction size limit, but in the
25732636 // 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 (
25752644 & [ instruction_type
25762645 . 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 )
25802655 }
25812656
25822657 /// Create a range proof context state account with a confidential transfer
0 commit comments