import { Instruction } from '@solana/instructions'; import { ExcludeTransactionMessageDurableNonceLifetime } from './durable-nonce'; import { TransactionMessage } from './transaction-message'; import { ExcludeTransactionMessageWithinSizeLimit } from './transaction-message-size'; /** * A helper type to append instructions to a transaction message * without losing type information about the current instructions. */ type AppendTransactionMessageInstructions< TTransactionMessage extends TransactionMessage, TInstructions extends readonly Instruction[], > = TTransactionMessage extends TransactionMessage ? Omit, 'instructions'> & { readonly instructions: readonly [...TTransactionMessage['instructions'], ...TInstructions]; } : never; /** * A helper type to prepend instructions to a transaction message * without losing type information about the current instructions. */ type PrependTransactionMessageInstructions< TTransactionMessage extends TransactionMessage, TInstructions extends readonly Instruction[], > = TTransactionMessage extends TransactionMessage ? Omit< ExcludeTransactionMessageWithinSizeLimit>, 'instructions' > & { readonly instructions: readonly [...TInstructions, ...TTransactionMessage['instructions']]; } : never; /** * Given an instruction, this method will return a new transaction message with that instruction * having been added to the end of the list of existing instructions. * * @see {@link appendTransactionInstructions} if you need to append multiple instructions to a * transaction message. * * @example * ```ts * import { address } from '@solana/addresses'; * import { getUtf8Encoder } from '@solana/codecs-strings'; * import { appendTransactionMessageInstruction } from '@solana/transaction-messages'; * * const memoTransactionMessage = appendTransactionMessageInstruction( * { * data: getUtf8Encoder().encode('Hello world!'), * programAddress: address('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), * }, * transactionMessage, * ); * ``` */ export function appendTransactionMessageInstruction< TTransactionMessage extends TransactionMessage, TInstruction extends Instruction, >( instruction: TInstruction, transactionMessage: TTransactionMessage, ): AppendTransactionMessageInstructions { return appendTransactionMessageInstructions([instruction], transactionMessage); } /** * Given an array of instructions, this method will return a new transaction message with those * instructions having been added to the end of the list of existing instructions. * * @see {@link appendTransactionInstruction} if you only need to append one instruction to a * transaction message. * * @example * ```ts * import { address } from '@solana/addresses'; * import { appendTransactionMessageInstructions } from '@solana/transaction-messages'; * * const memoTransaction = appendTransactionMessageInstructions( * [ * { * data: new TextEncoder().encode('Hello world!'), * programAddress: address('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), * }, * { * data: new TextEncoder().encode('How are you?'), * programAddress: address('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), * }, * ], * tx, * ); * ``` */ export function appendTransactionMessageInstructions< TTransactionMessage extends TransactionMessage, const TInstructions extends readonly Instruction[], >( instructions: TInstructions, transactionMessage: TTransactionMessage, ): AppendTransactionMessageInstructions { return Object.freeze({ ...transactionMessage, instructions: Object.freeze([ ...(transactionMessage.instructions as TTransactionMessage['instructions']), ...instructions, ] as readonly [...TTransactionMessage['instructions'], ...TInstructions]), }) as AppendTransactionMessageInstructions; } /** * Given an instruction, this method will return a new transaction message with that instruction * having been added to the beginning of the list of existing instructions. * * @see {@link prependTransactionInstructions} if you need to prepend multiple instructions to a * transaction message. * * @example * ```ts * import { address } from '@solana/addresses'; * import { prependTransactionMessageInstruction } from '@solana/transaction-messages'; * * const memoTransaction = prependTransactionMessageInstruction( * { * data: new TextEncoder().encode('Hello world!'), * programAddress: address('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), * }, * tx, * ); * ``` */ export function prependTransactionMessageInstruction< TTransactionMessage extends TransactionMessage, TInstruction extends Instruction, >( instruction: TInstruction, transactionMessage: TTransactionMessage, ): PrependTransactionMessageInstructions { return prependTransactionMessageInstructions([instruction], transactionMessage); } /** * Given an array of instructions, this method will return a new transaction message with those * instructions having been added to the beginning of the list of existing instructions. * * @see {@link prependTransactionInstruction} if you only need to prepend one instruction to a * transaction message. * * @example * ```ts * import { address } from '@solana/addresses'; * import { prependTransactionMessageInstructions } from '@solana/transaction-messages'; * * const memoTransaction = prependTransactionMessageInstructions( * [ * { * data: new TextEncoder().encode('Hello world!'), * programAddress: address('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), * }, * { * data: new TextEncoder().encode('How are you?'), * programAddress: address('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), * }, * ], * tx, * ); * ``` */ export function prependTransactionMessageInstructions< TTransactionMessage extends TransactionMessage, const TInstructions extends readonly Instruction[], >( instructions: TInstructions, transactionMessage: TTransactionMessage, ): PrependTransactionMessageInstructions { return Object.freeze({ ...(transactionMessage as ExcludeTransactionMessageDurableNonceLifetime), instructions: Object.freeze([ ...instructions, ...(transactionMessage.instructions as TTransactionMessage['instructions']), ] as readonly [...TInstructions, ...TTransactionMessage['instructions']]), }) as unknown as PrependTransactionMessageInstructions; }