Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
setTransactionMessageLifetimeUsingDurableNonce,
TransactionMessageWithDurableNonceLifetime,
} from '../durable-nonce';
import { BaseTransactionMessage } from '../transaction-message';
import { TransactionMessage } from '../transaction-message';

function createMockAdvanceNonceAccountInstruction<
TNonceAccountAddress extends string = string,
Expand Down Expand Up @@ -42,7 +42,7 @@ function createMockAdvanceNonceAccountInstruction<
}

describe('assertIsDurableNonceTransactionMessage()', () => {
let durableNonceTx: BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime;
let durableNonceTx: TransactionMessage & TransactionMessageWithDurableNonceLifetime;
const NONCE_CONSTRAINT = {
nonce: '123' as Nonce,
nonceAccountAddress: '123' as Address,
Expand Down Expand Up @@ -129,7 +129,7 @@ describe('assertIsDurableNonceTransactionMessage()', () => {
blockhash: '123' as Blockhash,
lastValidBlockHeight: 123n,
} as TransactionMessageWithBlockhashLifetime['lifetimeConstraint'],
} as BaseTransactionMessage);
} as unknown as TransactionMessage);
}).toThrow();
});
it('does not throw when supplied a durable nonce transaction', () => {
Expand Down Expand Up @@ -165,7 +165,7 @@ describe('assertIsDurableNonceTransactionMessage()', () => {
});

describe('setTransactionMessageLifetimeUsingDurableNonce', () => {
let baseTx: BaseTransactionMessage;
let baseTx: TransactionMessage;
const NONCE_CONSTRAINT_A = {
nonce: '123' as Nonce,
nonceAccountAddress: '123' as Address,
Expand Down Expand Up @@ -210,7 +210,7 @@ describe('setTransactionMessageLifetimeUsingDurableNonce', () => {
it('does not modify an `AdvanceNonceAccount` instruction if the existing one matches the constraint added', () => {
const instruction = createMockAdvanceNonceAccountInstruction(NONCE_CONSTRAINT_A);
instruction.accounts[2].role = AccountRole.WRITABLE_SIGNER;
const transaction: BaseTransactionMessage = {
const transaction: TransactionMessage = {
...baseTx,
instructions: [instruction, baseTx.instructions[0]],
};
Expand All @@ -222,7 +222,7 @@ describe('setTransactionMessageLifetimeUsingDurableNonce', () => {
});
describe('when the existing `AdvanceNonceAccount` instruction does not match the constraint added', () => {
it('replaces the existing instruction', () => {
const transaction: BaseTransactionMessage = {
const transaction: TransactionMessage = {
...baseTx,
instructions: [
createMockAdvanceNonceAccountInstruction(NONCE_CONSTRAINT_B),
Expand All @@ -240,7 +240,7 @@ describe('setTransactionMessageLifetimeUsingDurableNonce', () => {
});

it('freezes the replacement instruction', () => {
const transaction: BaseTransactionMessage = {
const transaction: TransactionMessage = {
...baseTx,
instructions: [
createMockAdvanceNonceAccountInstruction(NONCE_CONSTRAINT_B),
Expand All @@ -256,7 +256,7 @@ describe('setTransactionMessageLifetimeUsingDurableNonce', () => {
});
});
describe('given a durable nonce transaction', () => {
let durableNonceTxWithConstraintA: BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime;
let durableNonceTxWithConstraintA: TransactionMessage & TransactionMessageWithDurableNonceLifetime;
beforeEach(() => {
durableNonceTxWithConstraintA = {
...baseTx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { AdvanceNonceAccountInstruction } from '../durable-nonce-instruction';
import { setTransactionMessageFeePayer, TransactionMessageWithFeePayer } from '../fee-payer';
import { appendTransactionMessageInstruction } from '../instructions';
import { BaseTransactionMessage, TransactionMessage } from '../transaction-message';
import { TransactionMessage } from '../transaction-message';
import { TransactionMessageWithinSizeLimit } from '../transaction-message-size';

const mockNonceConfig = {
Expand All @@ -37,11 +37,11 @@ type V0TransactionMessage = Extract<TransactionMessage, { version: 0 }>;
{
// It narrows the transaction message type to one with a nonce-based lifetime.
{
const message = null as unknown as BaseTransactionMessage & { some: 1 };
const message = null as unknown as TransactionMessage & { some: 1 };
if (isTransactionMessageWithDurableNonceLifetime(message)) {
message satisfies BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime & { some: 1 };
message satisfies TransactionMessage & TransactionMessageWithDurableNonceLifetime & { some: 1 };
} else {
message satisfies BaseTransactionMessage & { some: 1 };
message satisfies TransactionMessage & { some: 1 };
// @ts-expect-error It does not have a nonce-based lifetime.
message satisfies TransactionMessageWithDurableNonceLifetime;
}
Expand All @@ -52,15 +52,15 @@ type V0TransactionMessage = Extract<TransactionMessage, { version: 0 }>;
{
// It narrows the transaction message type to one with a nonce-based lifetime.
{
const message = null as unknown as BaseTransactionMessage & { some: 1 };
const message = null as unknown as TransactionMessage & { some: 1 };
// @ts-expect-error Should not be durable nonce lifetime
message satisfies TransactionMessageWithDurableNonceLifetime;
// @ts-expect-error Should not have a nonce-based lifetime
message satisfies { lifetimeConstraint: { nonce: Nonce } };
// @ts-expect-error Should not start with a nonce instruction.
message.instructions[0] satisfies AdvanceNonceAccountInstruction;
assertIsTransactionMessageWithDurableNonceLifetime(message);
message satisfies BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime & { some: 1 };
message satisfies TransactionMessage & TransactionMessageWithDurableNonceLifetime & { some: 1 };
message satisfies TransactionMessageWithDurableNonceLifetime;
message satisfies { lifetimeConstraint: { nonce: Nonce } };
message.instructions[0] satisfies AdvanceNonceAccountInstruction;
Expand Down Expand Up @@ -97,7 +97,7 @@ type V0TransactionMessage = Extract<TransactionMessage, { version: 0 }>;
m => setTransactionMessageLifetimeUsingDurableNonce(mockNonceConfig, m),
);

message satisfies BaseTransactionMessage & TransactionMessageWithFeePayer;
message satisfies TransactionMessage & TransactionMessageWithFeePayer;
message satisfies TransactionMessageWithDurableNonceLifetime<'nonce', 'nonceAuthority', 'nonce'>;
message.instructions satisfies readonly [
AdvanceNonceAccountInstruction<'nonce', 'nonceAuthority'>,
Expand All @@ -116,7 +116,7 @@ type V0TransactionMessage = Extract<TransactionMessage, { version: 0 }>;
m => setTransactionMessageLifetimeUsingDurableNonce(newMockNonceConfig, m),
);

message satisfies BaseTransactionMessage & TransactionMessageWithFeePayer;
message satisfies TransactionMessage & TransactionMessageWithFeePayer;
message satisfies TransactionMessageWithDurableNonceLifetime<'newNonce', 'newNonceAuthority', 'newNonce'>;
message.instructions satisfies readonly [
AdvanceNonceAccountInstruction<'newNonce', 'newNonceAuthority'>,
Expand All @@ -126,7 +126,7 @@ type V0TransactionMessage = Extract<TransactionMessage, { version: 0 }>;

// It keeps the size limit type safety if we are only updating the durable nonce lifetime.
{
const message = null as unknown as BaseTransactionMessage &
const message = null as unknown as TransactionMessage &
TransactionMessageWithDurableNonceLifetime &
TransactionMessageWithinSizeLimit;
const newMessage = setTransactionMessageLifetimeUsingDurableNonce(mockNonceConfig, message);
Expand All @@ -135,7 +135,7 @@ type V0TransactionMessage = Extract<TransactionMessage, { version: 0 }>;

// It removes the size limit type safety if we previously has a blockhash lifetime.
{
const message = null as unknown as BaseTransactionMessage &
const message = null as unknown as TransactionMessage &
TransactionMessageWithBlockhashLifetime &
TransactionMessageWithinSizeLimit;
const newMessage = setTransactionMessageLifetimeUsingDurableNonce(mockNonceConfig, message);
Expand All @@ -145,7 +145,7 @@ type V0TransactionMessage = Extract<TransactionMessage, { version: 0 }>;

// It removes the size limit type safety if we previously had no lifetime set.
{
const message = null as unknown as BaseTransactionMessage & TransactionMessageWithinSizeLimit;
const message = null as unknown as TransactionMessage & TransactionMessageWithinSizeLimit;
const newMessage = setTransactionMessageLifetimeUsingDurableNonce(mockNonceConfig, message);
// @ts-expect-error The message may no longer be within size limit.
newMessage satisfies TransactionMessageWithinSizeLimit;
Expand Down
60 changes: 31 additions & 29 deletions packages/transaction-messages/src/durable-nonce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
isAdvanceNonceAccountInstruction,
} from './durable-nonce-instruction';
import { ExcludeTransactionMessageLifetime } from './lifetime';
import { BaseTransactionMessage } from './transaction-message';
import { TransactionMessage } from './transaction-message';
import { ExcludeTransactionMessageWithinSizeLimit } from './transaction-message-size';

type DurableNonceConfig<
Expand Down Expand Up @@ -65,7 +65,7 @@ export interface TransactionMessageWithDurableNonceLifetime<
/**
* A helper type to exclude the durable nonce lifetime constraint from a transaction message.
*/
export type ExcludeTransactionMessageDurableNonceLifetime<TTransactionMessage extends BaseTransactionMessage> =
export type ExcludeTransactionMessageDurableNonceLifetime<TTransactionMessage extends TransactionMessage> =
TTransactionMessage extends TransactionMessageWithDurableNonceLifetime
? ExcludeTransactionMessageLifetime<TTransactionMessage>
: TTransactionMessage;
Expand Down Expand Up @@ -94,8 +94,8 @@ export type ExcludeTransactionMessageDurableNonceLifetime<TTransactionMessage ex
* ```
*/
export function isTransactionMessageWithDurableNonceLifetime(
transactionMessage: BaseTransactionMessage | (BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime),
): transactionMessage is BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime {
transactionMessage: TransactionMessage | (TransactionMessage & TransactionMessageWithDurableNonceLifetime),
): transactionMessage is TransactionMessage & TransactionMessageWithDurableNonceLifetime {
return (
'lifetimeConstraint' in transactionMessage &&
typeof transactionMessage.lifetimeConstraint.nonce === 'string' &&
Expand Down Expand Up @@ -127,8 +127,8 @@ export function isTransactionMessageWithDurableNonceLifetime(
* ```
*/
export function assertIsTransactionMessageWithDurableNonceLifetime(
transactionMessage: BaseTransactionMessage | (BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime),
): asserts transactionMessage is BaseTransactionMessage & TransactionMessageWithDurableNonceLifetime {
transactionMessage: TransactionMessage | (TransactionMessage & TransactionMessageWithDurableNonceLifetime),
): asserts transactionMessage is TransactionMessage & TransactionMessageWithDurableNonceLifetime {
if (!isTransactionMessageWithDurableNonceLifetime(transactionMessage)) {
throw new SolanaError(SOLANA_ERROR__TRANSACTION__EXPECTED_NONCE_LIFETIME);
}
Expand Down Expand Up @@ -178,7 +178,7 @@ function isAdvanceNonceAccountInstructionForNonce<
* ```
*/
export function setTransactionMessageLifetimeUsingDurableNonce<
TTransactionMessage extends BaseTransactionMessage,
TTransactionMessage extends TransactionMessage,
TNonceAccountAddress extends string = string,
TNonceAuthorityAddress extends string = string,
TNonceValue extends string = string,
Expand Down Expand Up @@ -247,28 +247,30 @@ export function setTransactionMessageLifetimeUsingDurableNonce<
* representing the nonce value.
*/
type SetTransactionMessageWithDurableNonceLifetime<
TTransactionMessage extends BaseTransactionMessage,
TTransactionMessage extends TransactionMessage,
TNonceAccountAddress extends string = string,
TNonceAuthorityAddress extends string = string,
TNonceValue extends string = string,
> = Omit<
// 1. The transaction message only grows in size if it currently has a different (or no) lifetime.
TTransactionMessage extends TransactionMessageWithDurableNonceLifetime
? TTransactionMessage
: ExcludeTransactionMessageWithinSizeLimit<TTransactionMessage>,
// 2. Remove the instructions array as we are going to replace it with a new one.
'instructions'
> & {
// 3. Replace or prepend the first instruction with the advance nonce account instruction.
readonly instructions: TTransactionMessage['instructions'] extends readonly [
AdvanceNonceAccountInstruction,
...infer TTail extends readonly Instruction[],
]
? readonly [AdvanceNonceAccountInstruction<TNonceAccountAddress, TNonceAuthorityAddress>, ...TTail]
: readonly [
AdvanceNonceAccountInstruction<TNonceAccountAddress, TNonceAuthorityAddress>,
...TTransactionMessage['instructions'],
];
// 4. Set the lifetime constraint to the nonce value.
readonly lifetimeConstraint: NonceLifetimeConstraint<TNonceValue>;
};
> = TTransactionMessage extends unknown
? Omit<
// 1. The transaction message only grows in size if it currently has a different (or no) lifetime.
TTransactionMessage extends TransactionMessageWithDurableNonceLifetime
? TTransactionMessage
: ExcludeTransactionMessageWithinSizeLimit<TTransactionMessage>,
// 2. Remove the instructions array as we are going to replace it with a new one.
'instructions'
> & {
// 3. Replace or prepend the first instruction with the advance nonce account instruction.
readonly instructions: TTransactionMessage['instructions'] extends readonly [
AdvanceNonceAccountInstruction,
...infer TTail extends readonly Instruction[],
]
? readonly [AdvanceNonceAccountInstruction<TNonceAccountAddress, TNonceAuthorityAddress>, ...TTail]
: readonly [
AdvanceNonceAccountInstruction<TNonceAccountAddress, TNonceAuthorityAddress>,
...TTransactionMessage['instructions'],
];
// 4. Set the lifetime constraint to the nonce value.
readonly lifetimeConstraint: NonceLifetimeConstraint<TNonceValue>;
}
: never;
Loading