From ff4779545787f8f082fca96e44fe53afbce28f8e Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Fri, 20 Jun 2025 14:44:30 +0100 Subject: [PATCH 1/5] Refactor `newInstructionsTransformer` hook as `onTransactionMessageUpdated` --- .../src/instructionPlans/transactionPlan.ts | 44 +++- .../transactionPlannerBase.ts | 208 ++++++++---------- 2 files changed, 125 insertions(+), 127 deletions(-) diff --git a/clients/js/src/instructionPlans/transactionPlan.ts b/clients/js/src/instructionPlans/transactionPlan.ts index 36914e5..6af1248 100644 --- a/clients/js/src/instructionPlans/transactionPlan.ts +++ b/clients/js/src/instructionPlans/transactionPlan.ts @@ -25,21 +25,49 @@ export type SingleTransactionPlan< }>; export function parallelTransactionPlan( - plans: TransactionPlan[] + plans: (CompilableTransactionMessage | TransactionPlan)[] ): ParallelTransactionPlan { - return { kind: 'parallel', plans }; + return Object.freeze({ + kind: 'parallel', + plans: parseSingleTransactionPlans(plans), + }); } export function sequentialTransactionPlan( - plans: TransactionPlan[] -): SequentialTransactionPlan { - return { kind: 'sequential', divisible: true, plans }; + plans: (CompilableTransactionMessage | TransactionPlan)[] +): SequentialTransactionPlan & { divisible: true } { + return Object.freeze({ + divisible: true, + kind: 'sequential', + plans: parseSingleTransactionPlans(plans), + }); } export function nonDivisibleSequentialTransactionPlan( - plans: TransactionPlan[] -): SequentialTransactionPlan { - return { kind: 'sequential', divisible: false, plans }; + plans: (CompilableTransactionMessage | TransactionPlan)[] +): SequentialTransactionPlan & { divisible: false } { + return Object.freeze({ + divisible: false, + kind: 'sequential', + plans: parseSingleTransactionPlans(plans), + }); +} + +export function singleTransactionPlan< + TTransactionMessage extends + CompilableTransactionMessage = CompilableTransactionMessage, +>( + transactionMessage: TTransactionMessage +): SingleTransactionPlan { + return Object.freeze({ kind: 'single', message: transactionMessage }); +} + +function parseSingleTransactionPlans( + plans: (CompilableTransactionMessage | TransactionPlan)[] +): TransactionPlan[] { + return plans.map((plan) => + 'kind' in plan ? plan : singleTransactionPlan(plan) + ); } export function getAllSingleTransactionPlans( diff --git a/clients/js/src/instructionPlans/transactionPlannerBase.ts b/clients/js/src/instructionPlans/transactionPlannerBase.ts index 0b6b03e..2cbce04 100644 --- a/clients/js/src/instructionPlans/transactionPlannerBase.ts +++ b/clients/js/src/instructionPlans/transactionPlannerBase.ts @@ -27,7 +27,7 @@ export type TransactionPlannerConfig = { createTransactionMessage: (config?: { abortSignal?: AbortSignal; }) => Promise | CompilableTransactionMessage; - newInstructionsTransformer?: < + onTransactionMessageUpdated?: < TTransactionMessage extends CompilableTransactionMessage, >( transactionMessage: TTransactionMessage, @@ -38,56 +38,49 @@ export type TransactionPlannerConfig = { export function createBaseTransactionPlanner( config: TransactionPlannerConfig ): TransactionPlanner { - const createSingleTransactionPlan = async ( - instructions: IInstruction[] = [], - abortSignal?: AbortSignal - ): Promise => { - abortSignal?.throwIfAborted(); - const plan: SingleTransactionPlan = { - kind: 'single', - message: await Promise.resolve( - config.createTransactionMessage({ abortSignal }) - ), - }; - if (instructions.length > 0) { - abortSignal?.throwIfAborted(); - await addInstructionsToSingleTransactionPlan( - plan, - instructions, - abortSignal - ); - } - return plan; - }; - - const addInstructionsToSingleTransactionPlan = async ( - plan: SingleTransactionPlan, - instructions: IInstruction[], - abortSignal?: AbortSignal - ): Promise => { - let message = appendTransactionMessageInstructions( - instructions, - plan.message - ); - if (config?.newInstructionsTransformer) { - abortSignal?.throwIfAborted(); - message = await Promise.resolve( - config.newInstructionsTransformer(plan.message, { abortSignal }) - ); - } - (plan as Mutable).message = message; - }; - return async ( originalInstructionPlan, { abortSignal } = {} ): Promise => { + const createSingleTransactionPlan: CreateSingleTransactionPlanFunction = + async (instructions = []) => { + abortSignal?.throwIfAborted(); + const emptyMessage = await Promise.resolve( + config.createTransactionMessage({ abortSignal }) + ); + if (instructions.length <= 0) { + return { kind: 'single', message: emptyMessage }; + } + const plan: SingleTransactionPlan = { + kind: 'single', + message: appendTransactionMessageInstructions( + instructions, + emptyMessage + ), + }; + return await onSingleTransactionPlanUpdated(plan); + }; + + const onSingleTransactionPlanUpdated: OnSingleTransactionPlanUpdatedFunction = + async (plan) => { + abortSignal?.throwIfAborted(); + if (!config?.onTransactionMessageUpdated) { + return plan; + } + return { + kind: 'single', + message: await Promise.resolve( + config.onTransactionMessageUpdated(plan.message, { abortSignal }) + ), + }; + }; + const plan = await traverse(originalInstructionPlan, { abortSignal, parent: null, parentCandidates: [], createSingleTransactionPlan, - addInstructionsToSingleTransactionPlan, + onSingleTransactionPlanUpdated, }); if (!plan) { @@ -107,19 +100,20 @@ export function createBaseTransactionPlanner( }; } +type CreateSingleTransactionPlanFunction = ( + instructions?: IInstruction[] +) => Promise; + +type OnSingleTransactionPlanUpdatedFunction = ( + plan: SingleTransactionPlan +) => Promise; + type TraverseContext = { abortSignal?: AbortSignal; parent: InstructionPlan | null; parentCandidates: SingleTransactionPlan[]; - createSingleTransactionPlan: ( - instructions?: IInstruction[], - abortSignal?: AbortSignal - ) => Promise; - addInstructionsToSingleTransactionPlan: ( - plan: SingleTransactionPlan, - instructions: IInstruction[], - abortSignal?: AbortSignal - ) => Promise; + createSingleTransactionPlan: CreateSingleTransactionPlanFunction; + onSingleTransactionPlanUpdated: OnSingleTransactionPlanUpdatedFunction; }; async function traverse( @@ -154,23 +148,20 @@ async function traverseSequential( (context.parent.kind === 'parallel' || !instructionPlan.divisible); if (mustEntirelyFitInCandidate) { for (const parentCandidate of context.parentCandidates) { - const transactionPlan = await traverseWithSingleCandidate( - instructionPlan, - { - ...context, - candidate: { - kind: 'single', - message: { - ...parentCandidate.message, - instructions: [...parentCandidate.message.instructions], - } as CompilableTransactionMessage, - }, - } - ); + const transactionPlan = traverseWithSingleCandidate(instructionPlan, { + ...context, + candidate: { + kind: 'single', + message: { + ...parentCandidate.message, + instructions: [...parentCandidate.message.instructions], + } as CompilableTransactionMessage, + }, + }); if (transactionPlan) { (parentCandidate as Mutable).message = transactionPlan.message; - // TODO: Use hook. + await context.onSingleTransactionPlanUpdated(parentCandidate); return null; } } @@ -253,10 +244,12 @@ async function traverseSingle( const ix = instructionPlan.instruction; const candidate = selectCandidate(context.parentCandidates, [ix]); if (candidate) { - await context.addInstructionsToSingleTransactionPlan(candidate, [ix]); + (candidate.message as Mutable) = + appendTransactionMessageInstructions([ix], candidate.message); + await context.onSingleTransactionPlanUpdated(candidate); return null; } - return await context.createSingleTransactionPlan([ix], context.abortSignal); + return await context.createSingleTransactionPlan([ix]); } async function traverseIterable( @@ -271,27 +264,20 @@ async function traverseIterable( const candidateResult = selectCandidateForIterator(candidates, iterator); if (candidateResult) { const [candidate, ix] = candidateResult; - await context.addInstructionsToSingleTransactionPlan( - candidate, - [ix], - context.abortSignal - ); + (candidate.message as Mutable) = + appendTransactionMessageInstructions([ix], candidate.message); + await context.onSingleTransactionPlanUpdated(candidate); } else { - const newPlan = await context.createSingleTransactionPlan( - [], - context.abortSignal - ); + const newPlan = await context.createSingleTransactionPlan([]); const ix = iterator.next(newPlan.message); if (!ix) { throw new Error( 'Could not fit `InterableInstructionPlan` into a transaction' ); } - await context.addInstructionsToSingleTransactionPlan( - newPlan, - [ix], - context.abortSignal - ); + (newPlan.message as Mutable) = + appendTransactionMessageInstructions([ix], newPlan.message); + await context.onSingleTransactionPlanUpdated(newPlan); transactionPlans.push(newPlan); // Adding the new plan to the candidates is important for cases @@ -388,40 +374,24 @@ function isValidTransactionPlan(transactionPlan: TransactionPlan): boolean { type TraverseWithSingleCandidateContext = { abortSignal?: AbortSignal; candidate: SingleTransactionPlan | null; - createSingleTransactionPlan: ( - instructions?: IInstruction[], - abortSignal?: AbortSignal - ) => Promise; - addInstructionsToSingleTransactionPlan: ( - plan: SingleTransactionPlan, - instructions: IInstruction[], - abortSignal?: AbortSignal - ) => Promise; + createSingleTransactionPlan: CreateSingleTransactionPlanFunction; + onSingleTransactionPlanUpdated: OnSingleTransactionPlanUpdatedFunction; }; -async function traverseWithSingleCandidate( +function traverseWithSingleCandidate( instructionPlan: InstructionPlan, context: TraverseWithSingleCandidateContext -): Promise { +): SingleTransactionPlan | null { context.abortSignal?.throwIfAborted(); switch (instructionPlan.kind) { case 'sequential': - return await traverseSequentialWithSingleCandidate( - instructionPlan, - context - ); + return traverseSequentialWithSingleCandidate(instructionPlan, context); case 'parallel': - return await traverseParallelWithSingleCandidate( - instructionPlan, - context - ); + return traverseParallelWithSingleCandidate(instructionPlan, context); case 'single': - return await traverseSingleWithSingleCandidate(instructionPlan, context); + return traverseSingleWithSingleCandidate(instructionPlan, context); case 'iterable': - return await traverseIterableWithSingleCandidate( - instructionPlan, - context - ); + return traverseIterableWithSingleCandidate(instructionPlan, context); default: instructionPlan satisfies never; throw new Error( @@ -430,15 +400,15 @@ async function traverseWithSingleCandidate( } } -async function traverseSequentialWithSingleCandidate( +function traverseSequentialWithSingleCandidate( instructionPlan: SequentialInstructionPlan, context: TraverseWithSingleCandidateContext -): Promise { +): SingleTransactionPlan | null { if (context.candidate === null) { return null; } for (const plan of instructionPlan.plans) { - const candidate = await traverseWithSingleCandidate(plan, { + const candidate = traverseWithSingleCandidate(plan, { ...context, candidate: context.candidate, }); @@ -449,15 +419,15 @@ async function traverseSequentialWithSingleCandidate( return context.candidate; } -async function traverseParallelWithSingleCandidate( +function traverseParallelWithSingleCandidate( instructionPlan: ParallelInstructionPlan, context: TraverseWithSingleCandidateContext -): Promise { +): SingleTransactionPlan | null { if (context.candidate === null) { return null; } for (const plan of instructionPlan.plans) { - const candidate = await traverseWithSingleCandidate(plan, { + const candidate = traverseWithSingleCandidate(plan, { ...context, candidate: context.candidate, }); @@ -468,10 +438,10 @@ async function traverseParallelWithSingleCandidate( return context.candidate; } -async function traverseSingleWithSingleCandidate( +function traverseSingleWithSingleCandidate( instructionPlan: SingleInstructionPlan, context: TraverseWithSingleCandidateContext -): Promise { +): SingleTransactionPlan | null { if (context.candidate === null) { return null; } @@ -479,14 +449,15 @@ async function traverseSingleWithSingleCandidate( if (!isValidCandidate(context.candidate, [ix])) { return null; } - await context.addInstructionsToSingleTransactionPlan(context.candidate, [ix]); + (context.candidate.message as Mutable) = + appendTransactionMessageInstructions([ix], context.candidate.message); return context.candidate; } -async function traverseIterableWithSingleCandidate( +function traverseIterableWithSingleCandidate( instructionPlan: IterableInstructionPlan, context: TraverseWithSingleCandidateContext -): Promise { +): SingleTransactionPlan | null { if (context.candidate === null) { return null; } @@ -496,9 +467,8 @@ async function traverseIterableWithSingleCandidate( if (!ix || !isValidCandidate(context.candidate, [ix])) { return null; } - await context.addInstructionsToSingleTransactionPlan(context.candidate, [ - ix, - ]); + (context.candidate.message as Mutable) = + appendTransactionMessageInstructions([ix], context.candidate.message); } return context.candidate; } From 690d9d47b1d81ea140a7a6060752f6ec519d3baa Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Fri, 20 Jun 2025 14:50:51 +0100 Subject: [PATCH 2/5] Remove unnecessary `TraverseWithSingleCandidateContext` --- .../transactionPlannerBase.ts | 80 +++++++------------ 1 file changed, 30 insertions(+), 50 deletions(-) diff --git a/clients/js/src/instructionPlans/transactionPlannerBase.ts b/clients/js/src/instructionPlans/transactionPlannerBase.ts index 2cbce04..1e58283 100644 --- a/clients/js/src/instructionPlans/transactionPlannerBase.ts +++ b/clients/js/src/instructionPlans/transactionPlannerBase.ts @@ -149,14 +149,8 @@ async function traverseSequential( if (mustEntirelyFitInCandidate) { for (const parentCandidate of context.parentCandidates) { const transactionPlan = traverseWithSingleCandidate(instructionPlan, { - ...context, - candidate: { - kind: 'single', - message: { - ...parentCandidate.message, - instructions: [...parentCandidate.message.instructions], - } as CompilableTransactionMessage, - }, + kind: 'single', + message: { ...parentCandidate.message }, }); if (transactionPlan) { (parentCandidate as Mutable).message = @@ -371,27 +365,19 @@ function isValidTransactionPlan(transactionPlan: TransactionPlan): boolean { return transactionPlan.plans.every(isValidTransactionPlan); } -type TraverseWithSingleCandidateContext = { - abortSignal?: AbortSignal; - candidate: SingleTransactionPlan | null; - createSingleTransactionPlan: CreateSingleTransactionPlanFunction; - onSingleTransactionPlanUpdated: OnSingleTransactionPlanUpdatedFunction; -}; - function traverseWithSingleCandidate( instructionPlan: InstructionPlan, - context: TraverseWithSingleCandidateContext + candidate: SingleTransactionPlan | null ): SingleTransactionPlan | null { - context.abortSignal?.throwIfAborted(); switch (instructionPlan.kind) { case 'sequential': - return traverseSequentialWithSingleCandidate(instructionPlan, context); + return traverseSequentialWithSingleCandidate(instructionPlan, candidate); case 'parallel': - return traverseParallelWithSingleCandidate(instructionPlan, context); + return traverseParallelWithSingleCandidate(instructionPlan, candidate); case 'single': - return traverseSingleWithSingleCandidate(instructionPlan, context); + return traverseSingleWithSingleCandidate(instructionPlan, candidate); case 'iterable': - return traverseIterableWithSingleCandidate(instructionPlan, context); + return traverseIterableWithSingleCandidate(instructionPlan, candidate); default: instructionPlan satisfies never; throw new Error( @@ -402,73 +388,67 @@ function traverseWithSingleCandidate( function traverseSequentialWithSingleCandidate( instructionPlan: SequentialInstructionPlan, - context: TraverseWithSingleCandidateContext + candidate: SingleTransactionPlan | null ): SingleTransactionPlan | null { - if (context.candidate === null) { + if (candidate === null) { return null; } for (const plan of instructionPlan.plans) { - const candidate = traverseWithSingleCandidate(plan, { - ...context, - candidate: context.candidate, - }); - if (candidate === null) { + const updatedCandidate = traverseWithSingleCandidate(plan, candidate); + if (updatedCandidate === null) { return null; } } - return context.candidate; + return candidate; } function traverseParallelWithSingleCandidate( instructionPlan: ParallelInstructionPlan, - context: TraverseWithSingleCandidateContext + candidate: SingleTransactionPlan | null ): SingleTransactionPlan | null { - if (context.candidate === null) { + if (candidate === null) { return null; } for (const plan of instructionPlan.plans) { - const candidate = traverseWithSingleCandidate(plan, { - ...context, - candidate: context.candidate, - }); - if (candidate === null) { + const updatedCandidate = traverseWithSingleCandidate(plan, candidate); + if (updatedCandidate === null) { return null; } } - return context.candidate; + return candidate; } function traverseSingleWithSingleCandidate( instructionPlan: SingleInstructionPlan, - context: TraverseWithSingleCandidateContext + candidate: SingleTransactionPlan | null ): SingleTransactionPlan | null { - if (context.candidate === null) { + if (candidate === null) { return null; } const ix = instructionPlan.instruction; - if (!isValidCandidate(context.candidate, [ix])) { + if (!isValidCandidate(candidate, [ix])) { return null; } - (context.candidate.message as Mutable) = - appendTransactionMessageInstructions([ix], context.candidate.message); - return context.candidate; + (candidate.message as Mutable) = + appendTransactionMessageInstructions([ix], candidate.message); + return candidate; } function traverseIterableWithSingleCandidate( instructionPlan: IterableInstructionPlan, - context: TraverseWithSingleCandidateContext + candidate: SingleTransactionPlan | null ): SingleTransactionPlan | null { - if (context.candidate === null) { + if (candidate === null) { return null; } const iterator = instructionPlan.getIterator(); while (iterator.hasNext()) { - const ix = iterator.next(context.candidate.message); - if (!ix || !isValidCandidate(context.candidate, [ix])) { + const ix = iterator.next(candidate.message); + if (!ix || !isValidCandidate(candidate, [ix])) { return null; } - (context.candidate.message as Mutable) = - appendTransactionMessageInstructions([ix], context.candidate.message); + (candidate.message as Mutable) = + appendTransactionMessageInstructions([ix], candidate.message); } - return context.candidate; + return candidate; } From 3b5d5d09aa4a9a100493b415e0bedfeadb4de2ae Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Fri, 20 Jun 2025 14:58:19 +0100 Subject: [PATCH 3/5] Make `traverseWithSingleCandidate` use immutable candidates --- .../transactionPlannerBase.ts | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/clients/js/src/instructionPlans/transactionPlannerBase.ts b/clients/js/src/instructionPlans/transactionPlannerBase.ts index 1e58283..9102e5c 100644 --- a/clients/js/src/instructionPlans/transactionPlannerBase.ts +++ b/clients/js/src/instructionPlans/transactionPlannerBase.ts @@ -18,6 +18,7 @@ import { } from './transactionHelpers'; import { getAllSingleTransactionPlans, + singleTransactionPlan, SingleTransactionPlan, TransactionPlan, } from './transactionPlan'; @@ -367,7 +368,7 @@ function isValidTransactionPlan(transactionPlan: TransactionPlan): boolean { function traverseWithSingleCandidate( instructionPlan: InstructionPlan, - candidate: SingleTransactionPlan | null + candidate: SingleTransactionPlan ): SingleTransactionPlan | null { switch (instructionPlan.kind) { case 'sequential': @@ -388,67 +389,61 @@ function traverseWithSingleCandidate( function traverseSequentialWithSingleCandidate( instructionPlan: SequentialInstructionPlan, - candidate: SingleTransactionPlan | null + candidate: SingleTransactionPlan ): SingleTransactionPlan | null { - if (candidate === null) { - return null; - } + let newCandidate: SingleTransactionPlan = candidate; for (const plan of instructionPlan.plans) { - const updatedCandidate = traverseWithSingleCandidate(plan, candidate); - if (updatedCandidate === null) { + const result = traverseWithSingleCandidate(plan, newCandidate); + if (result === null) { return null; } + newCandidate = result; } - return candidate; + return newCandidate; } function traverseParallelWithSingleCandidate( instructionPlan: ParallelInstructionPlan, - candidate: SingleTransactionPlan | null + candidate: SingleTransactionPlan ): SingleTransactionPlan | null { - if (candidate === null) { - return null; - } + let newCandidate: SingleTransactionPlan = candidate; for (const plan of instructionPlan.plans) { - const updatedCandidate = traverseWithSingleCandidate(plan, candidate); - if (updatedCandidate === null) { + const result = traverseWithSingleCandidate(plan, newCandidate); + if (result === null) { return null; } + newCandidate = result; } - return candidate; + return newCandidate; } function traverseSingleWithSingleCandidate( instructionPlan: SingleInstructionPlan, - candidate: SingleTransactionPlan | null + candidate: SingleTransactionPlan ): SingleTransactionPlan | null { - if (candidate === null) { - return null; - } const ix = instructionPlan.instruction; if (!isValidCandidate(candidate, [ix])) { return null; } - (candidate.message as Mutable) = - appendTransactionMessageInstructions([ix], candidate.message); - return candidate; + return singleTransactionPlan( + appendTransactionMessageInstructions([ix], candidate.message) + ); } function traverseIterableWithSingleCandidate( instructionPlan: IterableInstructionPlan, - candidate: SingleTransactionPlan | null + candidate: SingleTransactionPlan ): SingleTransactionPlan | null { - if (candidate === null) { - return null; - } const iterator = instructionPlan.getIterator(); + let newCandidate: SingleTransactionPlan = candidate; while (iterator.hasNext()) { const ix = iterator.next(candidate.message); if (!ix || !isValidCandidate(candidate, [ix])) { return null; } - (candidate.message as Mutable) = - appendTransactionMessageInstructions([ix], candidate.message); + newCandidate = singleTransactionPlan( + appendTransactionMessageInstructions([ix], newCandidate.message) + ); } - return candidate; + return newCandidate; } From 6797ab7eaf479d61be4171a2a94f9a3b4ed5476e Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Fri, 20 Jun 2025 15:35:16 +0100 Subject: [PATCH 4/5] Refactor `fitEntirePlanInsideCandidate` function --- .../transactionPlannerBase.ts | 100 ++++++------------ 1 file changed, 33 insertions(+), 67 deletions(-) diff --git a/clients/js/src/instructionPlans/transactionPlannerBase.ts b/clients/js/src/instructionPlans/transactionPlannerBase.ts index 9102e5c..40a35f0 100644 --- a/clients/js/src/instructionPlans/transactionPlannerBase.ts +++ b/clients/js/src/instructionPlans/transactionPlannerBase.ts @@ -149,7 +149,7 @@ async function traverseSequential( (context.parent.kind === 'parallel' || !instructionPlan.divisible); if (mustEntirelyFitInCandidate) { for (const parentCandidate of context.parentCandidates) { - const transactionPlan = traverseWithSingleCandidate(instructionPlan, { + const transactionPlan = fitEntirePlanInsideCandidate(instructionPlan, { kind: 'single', message: { ...parentCandidate.message }, }); @@ -366,19 +366,46 @@ function isValidTransactionPlan(transactionPlan: TransactionPlan): boolean { return transactionPlan.plans.every(isValidTransactionPlan); } -function traverseWithSingleCandidate( +function fitEntirePlanInsideCandidate( instructionPlan: InstructionPlan, candidate: SingleTransactionPlan ): SingleTransactionPlan | null { + let newCandidate: SingleTransactionPlan = candidate; + switch (instructionPlan.kind) { case 'sequential': - return traverseSequentialWithSingleCandidate(instructionPlan, candidate); case 'parallel': - return traverseParallelWithSingleCandidate(instructionPlan, candidate); + for (const plan of instructionPlan.plans) { + const result = fitEntirePlanInsideCandidate(plan, newCandidate); + if (result === null) { + return null; + } + newCandidate = result; + } + return newCandidate; case 'single': - return traverseSingleWithSingleCandidate(instructionPlan, candidate); + if (!isValidCandidate(candidate, [instructionPlan.instruction])) { + return null; + } + return singleTransactionPlan( + appendTransactionMessageInstructions( + [instructionPlan.instruction], + candidate.message + ) + ); case 'iterable': - return traverseIterableWithSingleCandidate(instructionPlan, candidate); + // eslint-disable-next-line no-case-declarations + const iterator = instructionPlan.getIterator(); + while (iterator.hasNext()) { + const ix = iterator.next(candidate.message); + if (!ix || !isValidCandidate(candidate, [ix])) { + return null; + } + newCandidate = singleTransactionPlan( + appendTransactionMessageInstructions([ix], newCandidate.message) + ); + } + return newCandidate; default: instructionPlan satisfies never; throw new Error( @@ -386,64 +413,3 @@ function traverseWithSingleCandidate( ); } } - -function traverseSequentialWithSingleCandidate( - instructionPlan: SequentialInstructionPlan, - candidate: SingleTransactionPlan -): SingleTransactionPlan | null { - let newCandidate: SingleTransactionPlan = candidate; - for (const plan of instructionPlan.plans) { - const result = traverseWithSingleCandidate(plan, newCandidate); - if (result === null) { - return null; - } - newCandidate = result; - } - return newCandidate; -} - -function traverseParallelWithSingleCandidate( - instructionPlan: ParallelInstructionPlan, - candidate: SingleTransactionPlan -): SingleTransactionPlan | null { - let newCandidate: SingleTransactionPlan = candidate; - for (const plan of instructionPlan.plans) { - const result = traverseWithSingleCandidate(plan, newCandidate); - if (result === null) { - return null; - } - newCandidate = result; - } - return newCandidate; -} - -function traverseSingleWithSingleCandidate( - instructionPlan: SingleInstructionPlan, - candidate: SingleTransactionPlan -): SingleTransactionPlan | null { - const ix = instructionPlan.instruction; - if (!isValidCandidate(candidate, [ix])) { - return null; - } - return singleTransactionPlan( - appendTransactionMessageInstructions([ix], candidate.message) - ); -} - -function traverseIterableWithSingleCandidate( - instructionPlan: IterableInstructionPlan, - candidate: SingleTransactionPlan -): SingleTransactionPlan | null { - const iterator = instructionPlan.getIterator(); - let newCandidate: SingleTransactionPlan = candidate; - while (iterator.hasNext()) { - const ix = iterator.next(candidate.message); - if (!ix || !isValidCandidate(candidate, [ix])) { - return null; - } - newCandidate = singleTransactionPlan( - appendTransactionMessageInstructions([ix], newCandidate.message) - ); - } - return newCandidate; -} From 8ecc42d1c8dfd4c0d4c441d35cc8fba239051f4b Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Fri, 20 Jun 2025 15:38:03 +0100 Subject: [PATCH 5/5] Update transactionPlannerBase.ts --- .../src/instructionPlans/transactionPlannerBase.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clients/js/src/instructionPlans/transactionPlannerBase.ts b/clients/js/src/instructionPlans/transactionPlannerBase.ts index 40a35f0..3f65e93 100644 --- a/clients/js/src/instructionPlans/transactionPlannerBase.ts +++ b/clients/js/src/instructionPlans/transactionPlannerBase.ts @@ -144,15 +144,15 @@ async function traverseSequential( context: TraverseContext ): Promise { let candidate: SingleTransactionPlan | null = null; - const mustEntirelyFitInCandidate = + const mustEntirelyFitInParentCandidate = context.parent && (context.parent.kind === 'parallel' || !instructionPlan.divisible); - if (mustEntirelyFitInCandidate) { + if (mustEntirelyFitInParentCandidate) { for (const parentCandidate of context.parentCandidates) { - const transactionPlan = fitEntirePlanInsideCandidate(instructionPlan, { - kind: 'single', - message: { ...parentCandidate.message }, - }); + const transactionPlan = fitEntirePlanInsideCandidate( + instructionPlan, + parentCandidate + ); if (transactionPlan) { (parentCandidate as Mutable).message = transactionPlan.message;