11import {
2- appendTransactionMessageInstructions ,
2+ COMPUTE_BUDGET_PROGRAM_ADDRESS ,
3+ ComputeBudgetInstruction ,
4+ getSetComputeUnitLimitInstruction ,
5+ identifyComputeBudgetInstruction ,
6+ } from '@solana-program/compute-budget' ;
7+ import {
8+ Commitment ,
39 CompilableTransactionMessage ,
10+ compileTransaction ,
411 FullySignedTransaction ,
12+ getBase64EncodedWireTransaction ,
13+ GetEpochInfoApi ,
14+ GetSignatureStatusesApi ,
515 pipe ,
16+ Rpc ,
17+ RpcSubscriptions ,
18+ sendAndConfirmTransactionFactory ,
19+ SendTransactionApi ,
20+ SignatureNotificationsApi ,
621 signTransactionMessageWithSigners ,
22+ SimulateTransactionApi ,
23+ SlotNotificationsApi ,
724 TransactionMessageWithBlockhashLifetime ,
825 TransactionMessageWithDurableNonceLifetime ,
9- TransactionWithLifetime ,
26+ TransactionWithBlockhashLifetime ,
1027} from '@solana/web3.js' ;
11- import { MessageInstructionPlan } from './instructionPlan' ;
28+ import {
29+ getTransactionMessageFromPlan ,
30+ MessageInstructionPlan ,
31+ } from './instructionPlan' ;
1232import {
1333 chunkParallelInstructionPlans ,
1434 createInstructionPlanExecutor ,
1535 InstructionPlanExecutor ,
1636} from './instructionPlanExecutor' ;
1737
1838export type DefaultInstructionPlanExecutorConfig = Readonly < {
39+ rpc : Rpc <
40+ GetEpochInfoApi &
41+ GetSignatureStatusesApi &
42+ SendTransactionApi &
43+ SimulateTransactionApi
44+ > ;
45+
46+ rpcSubscriptions : RpcSubscriptions <
47+ SignatureNotificationsApi & SlotNotificationsApi
48+ > ;
49+
50+ /**
51+ * The commitment to use when confirming transactions.
52+ */
53+ commitment ?: Commitment ;
54+
1955 /**
2056 * When provided, chunks the plans inside a {@link ParallelInstructionPlan}.
2157 * Each chunk is executed sequentially but each plan within a chunk is
@@ -29,7 +65,7 @@ export type DefaultInstructionPlanExecutorConfig = Readonly<{
2965 * simulate the transaction to determine the optimal compute unit limit
3066 * before updating the compute budget instruction with the computed value.
3167 */
32- simulateComputeUnitLimit ?: boolean ; // TODO
68+ simulateComputeUnitLimit ?: boolean ;
3369
3470 /**
3571 * Returns the default transaction message used to send transactions.
@@ -45,33 +81,40 @@ export type DefaultInstructionPlanExecutorConfig = Readonly<{
4581 | TransactionMessageWithDurableNonceLifetime
4682 )
4783 > ;
48-
49- /**
50- * Sends and confirms a constructed transaction.
51- */
52- sendAndConfirm : (
53- transaction : FullySignedTransaction & TransactionWithLifetime ,
54- config ?: { abortSignal ?: AbortSignal }
55- ) => Promise < void > ;
5684} > ;
5785
5886export function getDefaultInstructionPlanExecutor (
5987 config : DefaultInstructionPlanExecutorConfig
6088) : InstructionPlanExecutor {
6189 const {
90+ rpc,
91+ commitment,
6292 getDefaultMessage,
6393 parallelChunkSize : chunkSize ,
64- sendAndConfirm ,
94+ simulateComputeUnitLimit : shouldSimulateComputeUnitLimit ,
6595 } = config ;
96+ const sendAndConfirm = sendAndConfirmTransactionFactory ( config ) ;
6697
6798 return async ( plan , config ) => {
6899 const handleMessage = async ( plan : MessageInstructionPlan ) => {
69- const tx = await pipe (
70- await getDefaultMessage ( config ) ,
71- ( tx ) => appendTransactionMessageInstructions ( plan . instructions , tx ) ,
72- ( tx ) => signTransactionMessageWithSigners ( tx )
73- ) ;
74- await sendAndConfirm ( tx , config ) ;
100+ const defaultMessage = await getDefaultMessage ( config ) ;
101+ let message = getTransactionMessageFromPlan ( defaultMessage , plan ) ;
102+
103+ if ( shouldSimulateComputeUnitLimit ) {
104+ message = await setComputeUnitLimitBySimulatingTransaction (
105+ message ,
106+ rpc
107+ ) ;
108+ }
109+
110+ const tx = ( await signTransactionMessageWithSigners (
111+ message
112+ ) ) as FullySignedTransaction & TransactionWithBlockhashLifetime ;
113+ await sendAndConfirm ( tx , {
114+ ...config ,
115+ commitment : commitment ?? 'confirmed' ,
116+ skipPreflight : shouldSimulateComputeUnitLimit ,
117+ } ) ;
75118 } ;
76119
77120 const executor = pipe ( createInstructionPlanExecutor ( handleMessage ) , ( e ) =>
@@ -81,3 +124,57 @@ export function getDefaultInstructionPlanExecutor(
81124 return await executor ( plan , config ) ;
82125 } ;
83126}
127+
128+ async function setComputeUnitLimitBySimulatingTransaction <
129+ TTransactionMessage extends
130+ CompilableTransactionMessage = CompilableTransactionMessage ,
131+ > (
132+ message : TTransactionMessage ,
133+ rpc : Rpc < SimulateTransactionApi >
134+ ) : Promise < TTransactionMessage > {
135+ const instructionIndex = message . instructions . findIndex ( ( instruction ) => {
136+ return (
137+ instruction . programAddress === COMPUTE_BUDGET_PROGRAM_ADDRESS &&
138+ identifyComputeBudgetInstruction ( instruction . data as Uint8Array ) ===
139+ ComputeBudgetInstruction . SetComputeUnitLimit
140+ ) ;
141+ } ) ;
142+
143+ // Ignore if no compute unit limit instruction is found.
144+ if ( instructionIndex === - 1 ) {
145+ return message ;
146+ }
147+
148+ const limit = await getComputeUnitLimitBySimulatingTransaction ( message , rpc ) ;
149+
150+ // Ignore if the limit is not found.
151+ if ( limit === undefined ) {
152+ return message ;
153+ }
154+
155+ return Object . freeze ( {
156+ ...message ,
157+ instructions : [
158+ ...message . instructions . slice ( 0 , instructionIndex ) ,
159+ getSetComputeUnitLimitInstruction ( {
160+ // Use a 1.1x multiplier to the computed limit.
161+ units : Number ( ( limit * 110n ) / 100n ) ,
162+ } ) ,
163+ ...message . instructions . slice ( instructionIndex + 1 ) ,
164+ ] ,
165+ } as TTransactionMessage ) ;
166+ }
167+
168+ async function getComputeUnitLimitBySimulatingTransaction <
169+ TTransactionMessage extends
170+ CompilableTransactionMessage = CompilableTransactionMessage ,
171+ > (
172+ message : TTransactionMessage ,
173+ rpc : Rpc < SimulateTransactionApi >
174+ ) : Promise < bigint | undefined > {
175+ const tx = getBase64EncodedWireTransaction ( compileTransaction ( message ) ) ;
176+ const result = await rpc
177+ . simulateTransaction ( tx , { encoding : 'base64' } )
178+ . send ( ) ;
179+ return result . value . unitsConsumed ;
180+ }
0 commit comments