UNPKG

@solana/transaction-messages

Version:

Helpers for creating transaction messages

154 lines • 8.38 kB
import { Address } from '@solana/addresses'; import { IInstruction, IInstructionWithAccounts, IInstructionWithData, ReadonlyAccount, ReadonlySignerAccount, WritableAccount, WritableSignerAccount } from '@solana/instructions'; import { Brand } from '@solana/nominal-types'; import { BaseTransactionMessage } from './transaction-message'; type AdvanceNonceAccountInstruction<TNonceAccountAddress extends string = string, TNonceAuthorityAddress extends string = string> = IInstruction<'11111111111111111111111111111111'> & IInstructionWithAccounts<readonly [ WritableAccount<TNonceAccountAddress>, ReadonlyAccount<'SysvarRecentB1ockHashes11111111111111111111'>, ReadonlySignerAccount<TNonceAuthorityAddress> | WritableSignerAccount<TNonceAuthorityAddress> ]> & IInstructionWithData<AdvanceNonceAccountInstructionData>; type AdvanceNonceAccountInstructionData = Brand<Uint8Array, 'AdvanceNonceAccountInstructionData'>; type DurableNonceConfig<TNonceAccountAddress extends string = string, TNonceAuthorityAddress extends string = string, TNonceValue extends string = string> = Readonly<{ readonly nonce: Nonce<TNonceValue>; readonly nonceAccountAddress: Address<TNonceAccountAddress>; readonly nonceAuthorityAddress: Address<TNonceAuthorityAddress>; }>; /** Represents a string that is particularly known to be the base58-encoded value of a nonce. */ export type Nonce<TNonceValue extends string = string> = Brand<TNonceValue, 'Nonce'>; /** * A constraint which, when applied to a transaction message, makes that transaction message * eligible to land on the network. * * The transaction message will continue to be eligible to land until the network considers the * `nonce` to have advanced. This can happen when the nonce account in which this nonce is found is * destroyed, or the nonce value within changes. */ type NonceLifetimeConstraint<TNonceValue extends string = string> = Readonly<{ /** * A value contained in the related nonce account at the time the transaction was prepared. * * The transaction will be considered eligible to land until the nonce account ceases to exist * or contain this value. */ nonce: Nonce<TNonceValue>; }>; /** * Represents a transaction message whose lifetime is defined by the value of a nonce it includes. * * Such a transaction can only be landed on the network if the nonce is known to the network and has * not already been used to land a different transaction. */ export interface TransactionMessageWithDurableNonceLifetime<TNonceAccountAddress extends string = string, TNonceAuthorityAddress extends string = string, TNonceValue extends string = string> { readonly instructions: readonly [ AdvanceNonceAccountInstruction<TNonceAccountAddress, TNonceAuthorityAddress>, ...IInstruction[] ]; readonly lifetimeConstraint: NonceLifetimeConstraint<TNonceValue>; } /** * From time to time you might acquire a transaction message, that you expect to have a * nonce-based lifetime, from an untrusted network API or user input. Use this function to assert * that such a transaction message actually has a nonce-based lifetime. * * @example * ```ts * import { assertIsDurableNonceTransactionMessage } from '@solana/transaction-messages'; * * try { * // If this type assertion function doesn't throw, then * // Typescript will upcast `message` to `TransactionMessageWithDurableNonceLifetime`. * assertIsDurableNonceTransactionMessage(message); * // At this point, `message` is a `TransactionMessageWithDurableNonceLifetime` that can be used * // with the RPC. * const { nonce, nonceAccountAddress } = message.lifetimeConstraint; * const { data: { blockhash: actualNonce } } = await fetchNonce(nonceAccountAddress); * } catch (e) { * // `message` turned out not to have a nonce-based lifetime * } * ``` */ export declare function assertIsDurableNonceTransactionMessage(transactionMessage: BaseTransactionMessage | (BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime)): asserts transactionMessage is BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime; /** * A type guard that returns `true` if the instruction conforms to the * {@link AdvanceNonceAccountInstruction} type, and refines its type for use in your program. * * @example * ```ts * import { isAdvanceNonceAccountInstruction } from '@solana/transaction-messages'; * * if (isAdvanceNonceAccountInstruction(message.instructions[0])) { * // At this point, the first instruction in the message has been refined to a * // `AdvanceNonceAccountInstruction`. * setNonceAccountAddress(message.instructions[0].accounts[0].address); * } else { * setError('The first instruction is not an `AdvanceNonce` instruction'); * } * ``` */ export declare function isAdvanceNonceAccountInstruction(instruction: IInstruction): instruction is AdvanceNonceAccountInstruction; /** * A type guard that returns `true` if the transaction message conforms to the * {@link TransactionMessageWithDurableNonceLifetime} type, and refines its type for use in your * program. * * @example * ```ts * import { isTransactionMessageWithDurableNonceLifetime } from '@solana/transaction-messages'; * import { fetchNonce } from "@solana-program/system"; * * if (isTransactionMessageWithDurableNonceLifetime(message)) { * // At this point, `message` has been refined to a * // `TransactionMessageWithDurableNonceLifetime`. * const { nonce, nonceAccountAddress } = message.lifetimeConstraint; * const { data: { blockhash: actualNonce } } = await fetchNonce(nonceAccountAddress); * setNonceIsValid(nonce === actualNonce); * } else { * setError( * `${getSignatureFromTransaction(transaction)} does not have a nonce-based lifetime`, * ); * } * ``` */ export declare function isDurableNonceTransaction(transactionMessage: BaseTransactionMessage | (BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime)): transactionMessage is BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime; /** * Given a nonce, the account where the value of the nonce is stored, and the address of the account * authorized to consume that nonce, this method will return a new transaction having the same type * as the one supplied plus the {@link TransactionMessageWithDurableNonceLifetime} type. * * In particular, this method _prepends_ an instruction to the transaction message designed to * consume (or 'advance') the nonce in the same transaction whose lifetime is defined by it. * * @param config * * @example * ```ts * import { setTransactionMessageLifetimeUsingDurableNonce } from '@solana/transactions'; * * const NONCE_VALUE_OFFSET = * 4 + // version(u32) * 4 + // state(u32) * 32; // nonce authority(pubkey) * // Then comes the nonce value. * * const nonceAccountAddress = address('EGtMh4yvXswwHhwVhyPxGrVV2TkLTgUqGodbATEPvojZ'); * const nonceAuthorityAddress = address('4KD1Rdrd89NG7XbzW3xsX9Aqnx2EExJvExiNme6g9iAT'); * const { value: nonceAccount } = await rpc * .getAccountInfo(nonceAccountAddress, { * dataSlice: { length: 32, offset: NONCE_VALUE_OFFSET }, * encoding: 'base58', * }) * .send(); * const nonce = * // This works because we asked for the exact slice of data representing the nonce * // value, and furthermore asked for it in `base58` encoding. * nonceAccount!.data[0] as unknown as Nonce; * * const durableNonceTransactionMessage = setTransactionMessageLifetimeUsingDurableNonce( * { nonce, nonceAccountAddress, nonceAuthorityAddress }, * tx, * ); * ``` */ export declare function setTransactionMessageLifetimeUsingDurableNonce<TTransactionMessage extends BaseTransactionMessage, TNonceAccountAddress extends string = string, TNonceAuthorityAddress extends string = string, TNonceValue extends string = string>({ nonce, nonceAccountAddress, nonceAuthorityAddress, }: DurableNonceConfig<TNonceAccountAddress, TNonceAuthorityAddress, TNonceValue>, transactionMessage: TTransactionMessage | (TransactionMessageWithDurableNonceLifetime & TTransactionMessage)): TransactionMessageWithDurableNonceLifetime<TNonceAccountAddress, TNonceAuthorityAddress, TNonceValue> & TTransactionMessage; export {}; //# sourceMappingURL=durable-nonce.d.ts.map