@solana/transaction-messages
Version:
Helpers for creating transaction messages
154 lines • 8.38 kB
TypeScript
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