diff --git a/.changeset/thin-maps-guess.md b/.changeset/thin-maps-guess.md new file mode 100644 index 000000000..23f3f428c --- /dev/null +++ b/.changeset/thin-maps-guess.md @@ -0,0 +1,59 @@ +--- +'@solana/instruction-plans': major +--- + +Reshape `SingleTransactionPlanResult` from a single object type with a `status` discriminated union into three distinct types: `SuccessfulSingleTransactionPlanResult`, `FailedSingleTransactionPlanResult`, and `CanceledSingleTransactionPlanResult`. This flattens the result structure so that `status` is now a string literal (`'successful'`, `'failed'`, or `'canceled'`) and properties like `context`, `error`, and `signature` live at the top level of each variant. + +Other changes include: + +- Rename the `message` property to `plannedMessage` on all single transaction plan result types. This makes it clearer that this original planned message from the `TransactionPlan`, not the final message that was sent to the network. +- Move the `context` object from inside the `status` field to the top level of each result variant. All variants now carry a `context` — not just successful ones. +- Expand `TransactionPlanResultContext` to optionally include `message`, `signature`, and `transaction` properties. +- Remove the now-unused `TransactionPlanResultStatus` type. +- `failedSingleTransactionPlanResult` and `canceledSingleTransactionPlanResult` now accept an optional `context` parameter. + +**BREAKING CHANGES** + +**Accessing the status kind.** Replace `result.status.kind` with `result.status`. + +```diff +- if (result.status.kind === 'successful') { /* ... */ } ++ if (result.status === 'successful') { /* ... */ } +``` + +**Accessing the signature.** The signature has moved from `result.status.signature` to `result.context.signature`. + +```diff +- const sig = result.status.signature; ++ const sig = result.context.signature; +``` + +**Accessing the transaction.** The transaction has moved from `result.status.transaction` to `result.context.transaction`. + +```diff +- const tx = result.status.transaction; ++ const tx = result.context.transaction; +``` + +**Accessing the error.** The error has moved from `result.status.error` to `result.error`. + +```diff +- const err = result.status.error; ++ const err = result.error; +``` + +**Accessing the context.** The context has moved from `result.status.context` to `result.context`. + +```diff +- const ctx = result.status.context; ++ const ctx = result.context; +``` + +**Accessing the message.** The `message` property has been renamed to `plannedMessage`. + +```diff +- const msg = result.message; ++ const msg = result.plannedMessage; +``` + +**`TransactionPlanResultStatus` removed.** Code that references this type must be updated to use the individual result variant types (`SuccessfulSingleTransactionPlanResult`, `FailedSingleTransactionPlanResult`, `CanceledSingleTransactionPlanResult`) or the `SingleTransactionPlanResult` union directly. diff --git a/packages/instruction-plans/src/__tests__/transaction-plan-result-test.ts b/packages/instruction-plans/src/__tests__/transaction-plan-result-test.ts index 9ad968abc..62d266883 100644 --- a/packages/instruction-plans/src/__tests__/transaction-plan-result-test.ts +++ b/packages/instruction-plans/src/__tests__/transaction-plan-result-test.ts @@ -47,9 +47,10 @@ describe('successfulSingleTransactionPlanResult', () => { const transactionA = createTransaction('A'); const result = successfulSingleTransactionPlanResult(messageA, transactionA); expect(result).toEqual({ + context: { signature: 'A', transaction: transactionA }, kind: 'single', - message: messageA, - status: { context: {}, kind: 'successful', signature: 'A', transaction: transactionA }, + plannedMessage: messageA, + status: 'successful', }); }); it('accepts an optional context object', () => { @@ -58,9 +59,10 @@ describe('successfulSingleTransactionPlanResult', () => { const context = { foo: 'bar' }; const result = successfulSingleTransactionPlanResult(messageA, transactionA, context); expect(result).toEqual({ + context: { ...context, signature: 'A', transaction: transactionA }, kind: 'single', - message: messageA, - status: { context, kind: 'successful', signature: 'A', transaction: transactionA }, + plannedMessage: messageA, + status: 'successful', }); }); it('freezes created SingleTransactionPlanResult objects', () => { @@ -83,9 +85,10 @@ describe('successfulSingleTransactionPlanResultFromSignature', () => { const signature = 'A' as Signature; const result = successfulSingleTransactionPlanResultFromSignature(messageA, signature); expect(result).toEqual({ + context: { signature: 'A' }, kind: 'single', - message: messageA, - status: { context: {}, kind: 'successful', signature: 'A' }, + plannedMessage: messageA, + status: 'successful', }); }); it('accepts an optional context object', () => { @@ -94,9 +97,10 @@ describe('successfulSingleTransactionPlanResultFromSignature', () => { const context = { foo: 'bar' }; const result = successfulSingleTransactionPlanResultFromSignature(messageA, signature, context); expect(result).toEqual({ + context: { ...context, signature: 'A' }, kind: 'single', - message: messageA, - status: { context, kind: 'successful', signature: 'A' }, + plannedMessage: messageA, + status: 'successful', }); }); it('freezes created SingleTransactionPlanResult objects', () => { @@ -119,9 +123,11 @@ describe('failedSingleTransactionPlanResult', () => { const error = new SolanaError(SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE); const result = failedSingleTransactionPlanResult(messageA, error); expect(result).toEqual({ + context: {}, + error, kind: 'single', - message: messageA, - status: { error, kind: 'failed' }, + plannedMessage: messageA, + status: 'failed', }); }); it('freezes created SingleTransactionPlanResult objects', () => { @@ -143,9 +149,10 @@ describe('canceledSingleTransactionPlanResult', () => { const messageA = createMessage('A'); const result = canceledSingleTransactionPlanResult(messageA); expect(result).toEqual({ + context: {}, kind: 'single', - message: messageA, - status: { kind: 'canceled' }, + plannedMessage: messageA, + status: 'canceled', }); }); it('freezes created SingleTransactionPlanResult objects', () => { @@ -917,7 +924,7 @@ describe('findTransactionPlanResult', () => { const found = findTransactionPlanResult( result, // eslint-disable-next-line jest/no-conditional-in-test - r => r.kind === 'single' && r.status.kind === 'failed', + r => r.kind === 'single' && r.status === 'failed', ); expect(found).toBe(failedResult); }); @@ -932,7 +939,7 @@ describe('findTransactionPlanResult', () => { const found = findTransactionPlanResult( result, // eslint-disable-next-line jest/no-conditional-in-test - r => r.kind === 'single' && r.status.kind === 'successful', + r => r.kind === 'single' && r.status === 'successful', ); expect(found).toBe(successfulResult); }); @@ -958,7 +965,7 @@ describe('everyTransactionPlanResult', () => { it('matches successful single transaction plans', () => { const plan = successfulSingleTransactionPlanResult(createMessage('A'), createTransaction('A')); // eslint-disable-next-line jest/no-conditional-in-test - const result = everyTransactionPlanResult(plan, p => p.kind === 'single' && p.status.kind === 'successful'); + const result = everyTransactionPlanResult(plan, p => p.kind === 'single' && p.status === 'successful'); expect(result).toBe(true); }); it('matches failed single transaction plans', () => { @@ -967,13 +974,13 @@ describe('everyTransactionPlanResult', () => { new SolanaError(SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE), ); // eslint-disable-next-line jest/no-conditional-in-test - const result = everyTransactionPlanResult(plan, p => p.kind === 'single' && p.status.kind === 'failed'); + const result = everyTransactionPlanResult(plan, p => p.kind === 'single' && p.status === 'failed'); expect(result).toBe(true); }); it('matches canceled single transaction plans', () => { const plan = canceledSingleTransactionPlanResult(createMessage('A')); // eslint-disable-next-line jest/no-conditional-in-test - const result = everyTransactionPlanResult(plan, p => p.kind === 'single' && p.status.kind === 'canceled'); + const result = everyTransactionPlanResult(plan, p => p.kind === 'single' && p.status === 'canceled'); expect(result).toBe(true); }); it('matches sequential transaction plans', () => { @@ -1006,7 +1013,7 @@ describe('everyTransactionPlanResult', () => { const result = everyTransactionPlanResult(plan, p => { // eslint-disable-next-line jest/no-conditional-in-test if (p.kind !== 'single') return true; - const message = p.message as ReturnType; + const message = p.plannedMessage as ReturnType; return ['A', 'B', 'C'].includes(message.id); }); expect(result).toBe(true); @@ -1023,7 +1030,7 @@ describe('everyTransactionPlanResult', () => { nonDivisibleSequentialTransactionPlanResult([resultA, resultC]), ]); // eslint-disable-next-line jest/no-conditional-in-test - const result = everyTransactionPlanResult(plan, p => p.kind !== 'single' || p.status.kind === 'successful'); + const result = everyTransactionPlanResult(plan, p => p.kind !== 'single' || p.status === 'successful'); expect(result).toBe(false); }); it('fails fast before evaluating children', () => { @@ -1057,7 +1064,7 @@ describe('transformTransactionPlanResult', () => { const plan = successfulSingleTransactionPlanResult(createMessage('A'), createTransaction('A')); const transformedPlan = transformTransactionPlanResult(plan, p => // eslint-disable-next-line jest/no-conditional-in-test - p.kind === 'single' ? { ...p, message: { ...p.message, id: 'New A' } } : p, + p.kind === 'single' ? { ...p, plannedMessage: { ...p.plannedMessage, id: 'New A' } } : p, ); expect(transformedPlan).toStrictEqual( successfulSingleTransactionPlanResult(createMessage('New A'), createTransaction('A')), @@ -1068,7 +1075,7 @@ describe('transformTransactionPlanResult', () => { const plan = failedSingleTransactionPlanResult(createMessage('A'), error); const transformedPlan = transformTransactionPlanResult(plan, p => // eslint-disable-next-line jest/no-conditional-in-test - p.kind === 'single' ? { ...p, message: { ...p.message, id: 'New A' } } : p, + p.kind === 'single' ? { ...p, plannedMessage: { ...p.plannedMessage, id: 'New A' } } : p, ); expect(transformedPlan).toStrictEqual(failedSingleTransactionPlanResult(createMessage('New A'), error)); }); @@ -1076,7 +1083,7 @@ describe('transformTransactionPlanResult', () => { const plan = canceledSingleTransactionPlanResult(createMessage('A')); const transformedPlan = transformTransactionPlanResult(plan, p => // eslint-disable-next-line jest/no-conditional-in-test - p.kind === 'single' ? { ...p, message: { ...p.message, id: 'New A' } } : p, + p.kind === 'single' ? { ...p, plannedMessage: { ...p.plannedMessage, id: 'New A' } } : p, ); expect(transformedPlan).toStrictEqual(canceledSingleTransactionPlanResult(createMessage('New A'))); }); @@ -1146,15 +1153,15 @@ describe('transformTransactionPlanResult', () => { transformTransactionPlanResult(plan, p => { // eslint-disable-next-line jest/no-conditional-in-test if (p.kind === 'single') { - const message = p.message as ReturnType; - return { ...p, message: { ...message, id: `New ${message.id}` } }; + const plannedMessage = p.plannedMessage as ReturnType; + return { ...p, plannedMessage: { ...plannedMessage, id: `New ${plannedMessage.id}` } }; } // eslint-disable-next-line jest/no-conditional-in-test if (p.kind === 'sequential') { - const seenMessages = p.plans + const seenPlannedMessages = p.plans .filter(subPlan => subPlan.kind === 'single') - .map(subPlan => subPlan.message as ReturnType); - seenTransactionMessageIds.push(...seenMessages.map(message => message.id)); + .map(subPlan => subPlan.plannedMessage as ReturnType); + seenTransactionMessageIds.push(...seenPlannedMessages.map(m => m.id)); } return p; }); diff --git a/packages/instruction-plans/src/transaction-plan-executor.ts b/packages/instruction-plans/src/transaction-plan-executor.ts index 23cd082e6..94357bf79 100644 --- a/packages/instruction-plans/src/transaction-plan-executor.ts +++ b/packages/instruction-plans/src/transaction-plan-executor.ts @@ -34,7 +34,7 @@ import { * This function traverses the transaction plan tree, executing each transaction * message and collecting results that mirror the structure of the original plan. * - * @typeParam TContext - The type of the context object that may be passed along with successful results. + * @typeParam TContext - The type of the context object that may be passed along with results. * @param transactionPlan - The transaction plan to execute. * @param config - Optional configuration object that can include an `AbortSignal` to cancel execution. * @return A promise that resolves to the execution results. @@ -219,7 +219,7 @@ async function traverseSingle( function findErrorFromTransactionPlanResult(result: TransactionPlanResult): Error | undefined { if (result.kind === 'single') { - return result.status.kind === 'failed' ? result.status.error : undefined; + return result.status === 'failed' ? result.error : undefined; } for (const plan of result.plans) { const error = findErrorFromTransactionPlanResult(plan); diff --git a/packages/instruction-plans/src/transaction-plan-result.ts b/packages/instruction-plans/src/transaction-plan-result.ts index 8c62f215e..83eb1ef2f 100644 --- a/packages/instruction-plans/src/transaction-plan-result.ts +++ b/packages/instruction-plans/src/transaction-plan-result.ts @@ -23,14 +23,13 @@ import { getSignatureFromTransaction, Transaction } from '@solana/transactions'; * were executed sequentially. It also retains the divisibility property from the * original plan. * - * @template TContext - The type of the context object that may be passed along with successful results + * @template TContext - The type of the context object that may be passed along with results * @template TTransactionMessage - The type of the transaction message * @template TSingle - The type of single transaction plan results in this tree * * @see {@link SingleTransactionPlanResult} * @see {@link ParallelTransactionPlanResult} * @see {@link SequentialTransactionPlanResult} - * @see {@link TransactionPlanResultStatus} */ export type TransactionPlanResult< TContext extends TransactionPlanResultContext = TransactionPlanResultContext, @@ -57,7 +56,7 @@ export type TransactionPlanResult< * plan result tree (which may contain parallel/sequential structures) where all * leaf nodes are successful. * - * @template TContext - The type of the context object that may be passed along with successful results + * @template TContext - The type of the context object that may be passed along with results * @template TTransactionMessage - The type of the transaction message * * @see {@link isSuccessfulTransactionPlanResult} @@ -74,8 +73,25 @@ export type SuccessfulTransactionPlanResult< SuccessfulSingleTransactionPlanResult >; -/** A context object that may be passed along with successful results. */ -export type TransactionPlanResultContext = Record; +/** + * The context object associated with a {@link SingleTransactionPlanResult}. + * + * This type defines the shape of the context that can be attached to transaction + * plan results. It always allows arbitrary additional properties and optionally + * includes the transaction message, the transaction signature, and the full + * transaction object. + * + * @see {@link SingleTransactionPlanResult} + * @see {@link SuccessfulSingleTransactionPlanResult} + * @see {@link FailedSingleTransactionPlanResult} + * @see {@link CanceledSingleTransactionPlanResult} + */ +export type TransactionPlanResultContext = { + [key: number | string | symbol]: unknown; + message?: TransactionMessage & TransactionMessageWithFeePayer; + signature?: Signature; + transaction?: Transaction; +}; /** * A result for a sequential transaction plan. @@ -87,7 +103,7 @@ export type TransactionPlanResultContext = Record = Readonly<{ +> = + | CanceledSingleTransactionPlanResult + | FailedSingleTransactionPlanResult + | SuccessfulSingleTransactionPlanResult; + +/** + * A {@link SingleTransactionPlanResult} with a 'successful' status. + * + * This type represents a single transaction that was successfully executed. + * It includes the original planned message, a context object containing at + * least the transaction {@link Signature}, and optionally the full + * {@link Transaction} object. + * + * You may use the {@link successfulSingleTransactionPlanResult} or + * {@link successfulSingleTransactionPlanResultFromSignature} helpers to + * create objects of this type. + * + * @template TContext - The type of the context object that may be passed along with the result. + * @template TTransactionMessage - The type of the transaction message. + * + * @example + * Creating a successful result from a transaction. + * ```ts + * const result = successfulSingleTransactionPlanResult( + * transactionMessage, + * transaction, + * ); + * result satisfies SuccessfulSingleTransactionPlanResult; + * result.context.signature; // The transaction signature. + * ``` + * + * @see {@link successfulSingleTransactionPlanResult} + * @see {@link successfulSingleTransactionPlanResultFromSignature} + * @see {@link isSuccessfulSingleTransactionPlanResult} + * @see {@link assertIsSuccessfulSingleTransactionPlanResult} + */ +export type SuccessfulSingleTransactionPlanResult< + TContext extends TransactionPlanResultContext = TransactionPlanResultContext, + TTransactionMessage extends TransactionMessage & TransactionMessageWithFeePayer = TransactionMessage & + TransactionMessageWithFeePayer, +> = { + context: Readonly; kind: 'single'; - message: TTransactionMessage; - status: TransactionPlanResultStatus; -}>; + plannedMessage: TTransactionMessage; + status: 'successful'; +}; + +/** + * A {@link SingleTransactionPlanResult} with a 'failed' status. + * + * This type represents a single transaction that failed during execution. + * It includes the original planned message, a context object, and the + * {@link Error} that caused the failure. + * + * You may use the {@link failedSingleTransactionPlanResult} helper to + * create objects of this type. + * + * @template TContext - The type of the context object that may be passed along with the result. + * @template TTransactionMessage - The type of the transaction message. + * + * @example + * Creating a failed result from a transaction message and error. + * ```ts + * const result = failedSingleTransactionPlanResult( + * transactionMessage, + * new Error('Transaction simulation failed'), + * ); + * result satisfies FailedSingleTransactionPlanResult; + * result.error; // The error that caused the failure. + * ``` + * + * @see {@link failedSingleTransactionPlanResult} + * @see {@link isFailedSingleTransactionPlanResult} + * @see {@link assertIsFailedSingleTransactionPlanResult} + */ +export type FailedSingleTransactionPlanResult< + TContext extends TransactionPlanResultContext = TransactionPlanResultContext, + TTransactionMessage extends TransactionMessage & TransactionMessageWithFeePayer = TransactionMessage & + TransactionMessageWithFeePayer, +> = { + context: Readonly; + error: Error; + kind: 'single'; + plannedMessage: TTransactionMessage; + status: 'failed'; +}; /** - * The status of a single transaction plan execution. + * A {@link SingleTransactionPlanResult} with a 'canceled' status. * - * This represents the outcome of executing a single transaction message and can be one of: - * - `successful` - The transaction was successfully executed. Contains the transaction - * and an optional context object. - * - `failed` - The transaction execution failed. Contains the error that caused the failure. - * - `canceled` - The transaction execution was canceled. + * This type represents a single transaction whose execution was canceled + * before it could complete. It includes the original planned message and + * a context object. + * + * You may use the {@link canceledSingleTransactionPlanResult} helper to + * create objects of this type. + * + * @template TContext - The type of the context object that may be passed along with the result. + * @template TTransactionMessage - The type of the transaction message. + * + * @example + * Creating a canceled result from a transaction message. + * ```ts + * const result = canceledSingleTransactionPlanResult(transactionMessage); + * result satisfies CanceledSingleTransactionPlanResult; + * ``` * - * @template TContext - The type of the context object that may be passed along with successful results + * @see {@link canceledSingleTransactionPlanResult} + * @see {@link isCanceledSingleTransactionPlanResult} + * @see {@link assertIsCanceledSingleTransactionPlanResult} */ -export type TransactionPlanResultStatus = - | Readonly<{ context: TContext; kind: 'successful'; signature: Signature; transaction?: Transaction }> - | Readonly<{ error: Error; kind: 'failed' }> - | Readonly<{ kind: 'canceled' }>; +export type CanceledSingleTransactionPlanResult< + TContext extends TransactionPlanResultContext = TransactionPlanResultContext, + TTransactionMessage extends TransactionMessage & TransactionMessageWithFeePayer = TransactionMessage & + TransactionMessageWithFeePayer, +> = { + context: Readonly; + kind: 'single'; + plannedMessage: TTransactionMessage; + status: 'canceled'; +}; /** * Creates a divisible {@link SequentialTransactionPlanResult} from an array of nested results. @@ -240,7 +356,7 @@ export type TransactionPlanResultStatus( - transactionMessage: TTransactionMessage, + plannedMessage: TTransactionMessage, transaction: Transaction, context?: TContext, ): SuccessfulSingleTransactionPlanResult { + const signature = getSignatureFromTransaction(transaction); return Object.freeze({ + context: Object.freeze({ ...((context ?? {}) as TContext), signature, transaction }), kind: 'single', - message: transactionMessage, - status: Object.freeze({ - context: context ?? ({} as TContext), - kind: 'successful', - signature: getSignatureFromTransaction(transaction), - transaction, - }), + plannedMessage, + status: 'successful', }); } @@ -367,7 +480,7 @@ export function successfulSingleTransactionPlanResult< * * @template TContext - The type of the context object * @template TTransactionMessage - The type of the transaction message - * @param transactionMessage - The original transaction message + * @param plannedMessage - The original transaction message * @param signature - The signature of the successfully executed transaction * @param context - Optional context object to be included with the result * @@ -387,14 +500,15 @@ export function successfulSingleTransactionPlanResultFromSignature< TTransactionMessage extends TransactionMessage & TransactionMessageWithFeePayer = TransactionMessage & TransactionMessageWithFeePayer, >( - transactionMessage: TTransactionMessage, + plannedMessage: TTransactionMessage, signature: Signature, context?: TContext, ): SuccessfulSingleTransactionPlanResult { return Object.freeze({ + context: Object.freeze({ ...((context ?? {}) as TContext), signature }), kind: 'single', - message: transactionMessage, - status: Object.freeze({ context: context ?? ({} as TContext), kind: 'successful', signature }), + plannedMessage, + status: 'successful', }); } @@ -405,9 +519,9 @@ export function successfulSingleTransactionPlanResultFromSignature< * the transaction execution failed. It includes the original transaction message * and the error that caused the failure. * - * @template TContext - The type of the context object (not used in failed results) + * @template TContext - The type of the context object * @template TTransactionMessage - The type of the transaction message - * @param transactionMessage - The original transaction message + * @param plannedMessage - The original transaction message * @param error - The error that caused the transaction to fail * * @example @@ -429,13 +543,16 @@ export function failedSingleTransactionPlanResult< TTransactionMessage extends TransactionMessage & TransactionMessageWithFeePayer = TransactionMessage & TransactionMessageWithFeePayer, >( - transactionMessage: TTransactionMessage, + plannedMessage: TTransactionMessage, error: Error, + context?: TContext, ): FailedSingleTransactionPlanResult { return Object.freeze({ + context: Object.freeze({ ...((context ?? {}) as TContext) }), + error, kind: 'single', - message: transactionMessage, - status: Object.freeze({ error, kind: 'failed' }), + plannedMessage, + status: 'failed', }); } @@ -445,9 +562,9 @@ export function failedSingleTransactionPlanResult< * This function creates a single result with a 'canceled' status, indicating that * the transaction execution was canceled. It includes the original transaction message. * - * @template TContext - The type of the context object (not used in canceled results) + * @template TContext - The type of the context object * @template TTransactionMessage - The type of the transaction message - * @param transactionMessage - The original transaction message + * @param plannedMessage - The original transaction message * * @example * ```ts @@ -461,11 +578,15 @@ export function canceledSingleTransactionPlanResult< TContext extends TransactionPlanResultContext = TransactionPlanResultContext, TTransactionMessage extends TransactionMessage & TransactionMessageWithFeePayer = TransactionMessage & TransactionMessageWithFeePayer, ->(transactionMessage: TTransactionMessage): CanceledSingleTransactionPlanResult { +>( + plannedMessage: TTransactionMessage, + context?: TContext, +): CanceledSingleTransactionPlanResult { return Object.freeze({ + context: Object.freeze({ ...((context ?? {}) as TContext) }), kind: 'single', - message: transactionMessage, - status: Object.freeze({ kind: 'canceled' }), + plannedMessage, + status: 'canceled', }); } @@ -480,7 +601,7 @@ export function canceledSingleTransactionPlanResult< * const result: TransactionPlanResult = successfulSingleTransactionPlanResult(message, transaction); * * if (isSingleTransactionPlanResult(result)) { - * console.log(result.status.kind); // TypeScript knows this is a SingleTransactionPlanResult. + * console.log(result.status); // TypeScript knows this is a SingleTransactionPlanResult. * } * ``` * @@ -511,7 +632,7 @@ export function isSingleTransactionPlanResult< * const result: TransactionPlanResult = successfulSingleTransactionPlanResult(message, transaction); * * assertIsSingleTransactionPlanResult(result); - * console.log(result.status.kind); // TypeScript knows this is a SingleTransactionPlanResult. + * console.log(result.status); // TypeScript knows this is a SingleTransactionPlanResult. * ``` * * @see {@link SingleTransactionPlanResult} @@ -546,7 +667,7 @@ export function assertIsSingleTransactionPlanResult< * const result: TransactionPlanResult = successfulSingleTransactionPlanResult(message, transaction); * * if (isSuccessfulSingleTransactionPlanResult(result)) { - * console.log(result.status.signature); // TypeScript knows this is a successful result. + * console.log(result.context.signature); // TypeScript knows this is a successful result. * } * ``` * @@ -560,7 +681,7 @@ export function isSuccessfulSingleTransactionPlanResult< >( plan: TransactionPlanResult, ): plan is SuccessfulSingleTransactionPlanResult { - return plan.kind === 'single' && plan.status.kind === 'successful'; + return plan.kind === 'single' && plan.status === 'successful'; } /** @@ -575,7 +696,7 @@ export function isSuccessfulSingleTransactionPlanResult< * const result: TransactionPlanResult = successfulSingleTransactionPlanResult(message, transaction); * * assertIsSuccessfulSingleTransactionPlanResult(result); - * console.log(result.status.signature); // TypeScript knows this is a successful result. + * console.log(result.context.signature); // TypeScript knows this is a successful result. * ``` * * @see {@link SuccessfulSingleTransactionPlanResult} @@ -590,7 +711,7 @@ export function assertIsSuccessfulSingleTransactionPlanResult< ): asserts plan is SuccessfulSingleTransactionPlanResult { if (!isSuccessfulSingleTransactionPlanResult(plan)) { throw new SolanaError(SOLANA_ERROR__INSTRUCTION_PLANS__UNEXPECTED_TRANSACTION_PLAN_RESULT, { - actualKind: plan.kind === 'single' ? `${plan.status.kind} single` : plan.kind, + actualKind: plan.kind === 'single' ? `${plan.status} single` : plan.kind, expectedKind: 'successful single', transactionPlanResult: plan, }); @@ -608,7 +729,7 @@ export function assertIsSuccessfulSingleTransactionPlanResult< * const result: TransactionPlanResult = failedSingleTransactionPlanResult(message, error); * * if (isFailedSingleTransactionPlanResult(result)) { - * console.log(result.status.error); // TypeScript knows this is a failed result. + * console.log(result.error); // TypeScript knows this is a failed result. * } * ``` * @@ -622,7 +743,7 @@ export function isFailedSingleTransactionPlanResult< >( plan: TransactionPlanResult, ): plan is FailedSingleTransactionPlanResult { - return plan.kind === 'single' && plan.status.kind === 'failed'; + return plan.kind === 'single' && plan.status === 'failed'; } /** @@ -637,7 +758,7 @@ export function isFailedSingleTransactionPlanResult< * const result: TransactionPlanResult = failedSingleTransactionPlanResult(message, error); * * assertIsFailedSingleTransactionPlanResult(result); - * console.log(result.status.error); // TypeScript knows this is a failed result. + * console.log(result.error); // TypeScript knows this is a failed result. * ``` * * @see {@link FailedSingleTransactionPlanResult} @@ -652,7 +773,7 @@ export function assertIsFailedSingleTransactionPlanResult< ): asserts plan is FailedSingleTransactionPlanResult { if (!isFailedSingleTransactionPlanResult(plan)) { throw new SolanaError(SOLANA_ERROR__INSTRUCTION_PLANS__UNEXPECTED_TRANSACTION_PLAN_RESULT, { - actualKind: plan.kind === 'single' ? `${plan.status.kind} single` : plan.kind, + actualKind: plan.kind === 'single' ? `${plan.status} single` : plan.kind, expectedKind: 'failed single', transactionPlanResult: plan, }); @@ -684,7 +805,7 @@ export function isCanceledSingleTransactionPlanResult< >( plan: TransactionPlanResult, ): plan is CanceledSingleTransactionPlanResult { - return plan.kind === 'single' && plan.status.kind === 'canceled'; + return plan.kind === 'single' && plan.status === 'canceled'; } /** @@ -714,7 +835,7 @@ export function assertIsCanceledSingleTransactionPlanResult< ): asserts plan is CanceledSingleTransactionPlanResult { if (!isCanceledSingleTransactionPlanResult(plan)) { throw new SolanaError(SOLANA_ERROR__INSTRUCTION_PLANS__UNEXPECTED_TRANSACTION_PLAN_RESULT, { - actualKind: plan.kind === 'single' ? `${plan.status.kind} single` : plan.kind, + actualKind: plan.kind === 'single' ? `${plan.status} single` : plan.kind, expectedKind: 'canceled single', transactionPlanResult: plan, }); @@ -1036,7 +1157,7 @@ export function assertIsSuccessfulTransactionPlanResult< * returning the first result that satisfies the predicate. It checks the root result * first, then recursively searches through nested results. * - * @template TContext - The type of the context object that may be passed along with successful results + * @template TContext - The type of the context object that may be passed along with results * @template TTransactionMessage - The type of the transaction message * @template TSingle - The type of single transaction plan results in this tree * @param transactionPlanResult - The transaction plan result tree to search. @@ -1053,7 +1174,7 @@ export function assertIsSuccessfulTransactionPlanResult< * * const failed = findTransactionPlanResult( * result, - * (r) => r.kind === 'single' && r.status.kind === 'failed', + * (r) => r.kind === 'single' && r.status === 'failed', * ); * // Returns the failed single transaction plan result for messageB. * ``` @@ -1097,7 +1218,7 @@ export function findTransactionPlanResult< * and returns the first single transaction result with a 'failed' status. If no failed * result is found, it throws a {@link SolanaError}. * - * @template TContext - The type of the context object that may be passed along with successful results + * @template TContext - The type of the context object that may be passed along with results * @template TTransactionMessage - The type of the transaction message * @param transactionPlanResult - The transaction plan result tree to search. * @return The first failed single transaction plan result. @@ -1129,10 +1250,7 @@ export function getFirstFailedSingleTransactionPlanResult< >( transactionPlanResult: TransactionPlanResult, ): FailedSingleTransactionPlanResult { - const result = findTransactionPlanResult( - transactionPlanResult, - r => r.kind === 'single' && r.status.kind === 'failed', - ); + const result = findTransactionPlanResult(transactionPlanResult, r => r.kind === 'single' && r.status === 'failed'); if (!result) { // Here we want the `transactionPlanResult` to be available in the error context @@ -1161,7 +1279,7 @@ export function getFirstFailedSingleTransactionPlanResult< * returning `true` only if the predicate returns `true` for every result in the tree * (including the root result and all nested results). * - * @template TContext - The type of the context object that may be passed along with successful results + * @template TContext - The type of the context object that may be passed along with results * @template TTransactionMessage - The type of the transaction message * @template TSingle - The type of single transaction plan results in this tree * @param transactionPlanResult - The transaction plan result tree to check. @@ -1178,7 +1296,7 @@ export function getFirstFailedSingleTransactionPlanResult< * * const allSuccessful = everyTransactionPlanResult( * result, - * (r) => r.kind !== 'single' || r.status.kind === 'successful', + * (r) => r.kind !== 'single' || r.status === 'successful', * ); * // Returns true because all single results are successful. * ``` @@ -1190,7 +1308,7 @@ export function getFirstFailedSingleTransactionPlanResult< * * const noCanceled = everyTransactionPlanResult( * result, - * (r) => r.kind !== 'single' || r.status.kind !== 'canceled', + * (r) => r.kind !== 'single' || r.status !== 'canceled', * ); * ``` * @@ -1243,8 +1361,8 @@ export function everyTransactionPlanResult< * ]); * * const transformed = transformTransactionPlanResult(result, (r) => { - * if (r.kind === 'single' && r.status.kind === 'canceled') { - * return failedSingleTransactionPlanResult(r.message, new Error('Execution canceled')); + * if (r.kind === 'single' && r.status === 'canceled') { + * return failedSingleTransactionPlanResult(r.plannedMessage, new Error('Execution canceled')); * } * return r; * }); @@ -1279,7 +1397,7 @@ export function transformTransactionPlanResult( * all the single results they contain. It's useful when you need to access all the individual * transaction results, regardless of their organization in the result tree (parallel or sequential). * - * @template TContext - The type of the context object that may be passed along with successful results + * @template TContext - The type of the context object that may be passed along with results * @template TTransactionMessage - The type of the transaction message * @template TSingle - The type of single transaction plan results in this tree * @param result - The transaction plan result to extract single results from @@ -1318,33 +1436,6 @@ export function flattenTransactionPlanResult< return result.plans.flatMap(flattenTransactionPlanResult); } -/** - * A {@link SingleTransactionPlanResult} with 'successful' status. - */ -export type SuccessfulSingleTransactionPlanResult< - TContext extends TransactionPlanResultContext = TransactionPlanResultContext, - TTransactionMessage extends TransactionMessage & TransactionMessageWithFeePayer = TransactionMessage & - TransactionMessageWithFeePayer, -> = SingleTransactionPlanResult & { status: { kind: 'successful' } }; - -/** - * A {@link SingleTransactionPlanResult} with 'failed' status. - */ -export type FailedSingleTransactionPlanResult< - TContext extends TransactionPlanResultContext = TransactionPlanResultContext, - TTransactionMessage extends TransactionMessage & TransactionMessageWithFeePayer = TransactionMessage & - TransactionMessageWithFeePayer, -> = SingleTransactionPlanResult & { status: { kind: 'failed' } }; - -/** - * A {@link SingleTransactionPlanResult} with 'canceled' status. - */ -export type CanceledSingleTransactionPlanResult< - TContext extends TransactionPlanResultContext = TransactionPlanResultContext, - TTransactionMessage extends TransactionMessage & TransactionMessageWithFeePayer = TransactionMessage & - TransactionMessageWithFeePayer, -> = SingleTransactionPlanResult & { status: { kind: 'canceled' } }; - /** * A summary of a {@link TransactionPlanResult}, categorizing transactions by their execution status. * - `successful`: Indicates whether all transactions were successful (i.e., no failed or canceled transactions). @@ -1366,7 +1457,7 @@ export type TransactionPlanResultSummary< /** * Summarize a {@link TransactionPlanResult} into a {@link TransactionPlanResultSummary}. * - * @template TContext - The type of the context object that may be passed along with successful results + * @template TContext - The type of the context object that may be passed along with results * @template TTransactionMessage - The type of the transaction message * @param result The transaction plan result to summarize * @returns A summary of the transaction plan result @@ -1386,17 +1477,17 @@ export function summarizeTransactionPlanResult< const flattenedResults = flattenTransactionPlanResult(result); for (const singleResult of flattenedResults) { - switch (singleResult.status.kind) { + switch (singleResult.status) { case 'successful': { - successfulTransactions.push(singleResult as Out['successfulTransactions'][number]); + successfulTransactions.push(singleResult); break; } case 'failed': { - failedTransactions.push(singleResult as Out['failedTransactions'][number]); + failedTransactions.push(singleResult); break; } case 'canceled': { - canceledTransactions.push(singleResult as Out['canceledTransactions'][number]); + canceledTransactions.push(singleResult); break; } }