Skip to content

Commit b33d18d

Browse files
committed
Update the signer API to return Transaction & TransactionWithLifetime
1 parent f5dc0b9 commit b33d18d

File tree

10 files changed

+58
-47
lines changed

10 files changed

+58
-47
lines changed

examples/signers/src/example.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { createLogger } from '@solana/example-utils/createLogger.js';
1111
import {
1212
address,
1313
appendTransactionMessageInstruction,
14+
assertIsTransactionWithinSizeLimit,
1415
Blockhash,
1516
compileTransaction,
1617
createKeyPairSignerFromBytes,
@@ -92,6 +93,7 @@ async function signTransaction(
9293
transactionMessage: TransactionMessage & TransactionMessageWithFeePayer & TransactionMessageWithLifetime,
9394
) {
9495
const transaction = compileTransaction(transactionMessage);
96+
assertIsTransactionWithinSizeLimit(transaction);
9597
const [signatureDictionary] = await signer.signTransactions([transaction]);
9698
const signature = signatureDictionary[signer.address];
9799
log.info(

examples/transfer-lamports/src/example.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
address,
1414
appendTransactionMessageInstruction,
1515
assertIsSendableTransaction,
16+
assertIsTransactionWithBlockhashLifetime,
1617
createKeyPairSignerFromBytes,
1718
createSolanaRpc,
1819
createSolanaRpcSubscriptions,
@@ -184,6 +185,7 @@ log.warn(
184185
);
185186
try {
186187
assertIsSendableTransaction(signedTransaction);
188+
assertIsTransactionWithBlockhashLifetime(signedTransaction);
187189
await sendAndConfirmTransaction(signedTransaction, { commitment: 'confirmed' });
188190
log.info('[success] Transfer confirmed');
189191
await pressAnyKeyPrompt('Press any key to quit');

packages/react/src/useWalletAccountTransactionSigner.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import { getAbortablePromise } from '@solana/promises';
55
import { TransactionModifyingSigner } from '@solana/signers';
66
import { getCompiledTransactionMessageDecoder } from '@solana/transaction-messages';
77
import {
8+
assertIsTransactionWithinSizeLimit,
89
getTransactionCodec,
910
getTransactionLifetimeConstraintFromCompiledTransactionMessage,
11+
Transaction,
12+
TransactionWithinSizeLimit,
1013
TransactionWithLifetime,
1114
} from '@solana/transactions';
1215
import { UiWalletAccount } from '@wallet-standard/ui';
@@ -74,7 +77,9 @@ export function useWalletAccountTransactionSigner<TWalletAccount extends UiWalle
7477
throw new SolanaError(SOLANA_ERROR__SIGNER__WALLET_MULTISIGN_UNIMPLEMENTED);
7578
}
7679
if (transactions.length === 0) {
77-
return transactions;
80+
return transactions as readonly (Transaction &
81+
TransactionWithinSizeLimit &
82+
TransactionWithLifetime)[];
7883
}
7984
const [transaction] = transactions;
8085
const wireTransactionBytes = transactionCodec.encode(transaction);
@@ -87,6 +92,8 @@ export function useWalletAccountTransactionSigner<TWalletAccount extends UiWalle
8792
signedTransaction,
8893
) as (typeof transactions)[number];
8994

95+
assertIsTransactionWithinSizeLimit(decodedSignedTransaction);
96+
9097
const existingLifetime =
9198
'lifetimeConstraint' in transaction
9299
? (transaction as TransactionWithLifetime).lifetimeConstraint

packages/signers/src/__tests__/keypair-signer-test.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ import '@solana/test-matchers/toBeFrozenObject';
33
import { address, getAddressFromPublicKey } from '@solana/addresses';
44
import { SOLANA_ERROR__SIGNER__EXPECTED_KEY_PAIR_SIGNER, SolanaError } from '@solana/errors';
55
import { generateKeyPair, SignatureBytes, signBytes } from '@solana/keys';
6-
import { partiallySignTransaction, Transaction, TransactionWithLifetime } from '@solana/transactions';
6+
import {
7+
partiallySignTransaction,
8+
Transaction,
9+
TransactionWithinSizeLimit,
10+
TransactionWithLifetime,
11+
} from '@solana/transactions';
712

813
import {
914
assertIsKeyPairSigner,
@@ -145,8 +150,8 @@ describe('createSignerFromKeyPair', () => {
145150

146151
// And given we have a couple of mock transactions to sign.
147152
const mockTransactions = [
148-
{} as Transaction & TransactionWithLifetime,
149-
{} as Transaction & TransactionWithLifetime,
153+
{} as Transaction & TransactionWithinSizeLimit & TransactionWithLifetime,
154+
{} as Transaction & TransactionWithinSizeLimit & TransactionWithLifetime,
150155
];
151156

152157
// And given we mock the next two calls of the partiallySignTransaction function.

packages/signers/src/__tests__/noop-signer-test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import '@solana/test-matchers/toBeFrozenObject';
22

33
import { address } from '@solana/addresses';
4-
import { Transaction, TransactionWithLifetime } from '@solana/transactions';
4+
import { Transaction, TransactionWithinSizeLimit, TransactionWithLifetime } from '@solana/transactions';
55

66
import { createNoopSigner, NoopSigner } from '../noop-signer';
77
import { createSignableMessage } from '../signable-message';
@@ -53,8 +53,8 @@ describe('createNoopSigner', () => {
5353

5454
// And given we have a couple of mock transactions to sign.
5555
const mockTransactions = [
56-
{} as Transaction & TransactionWithLifetime,
57-
{} as Transaction & TransactionWithLifetime,
56+
{} as Transaction & TransactionWithinSizeLimit & TransactionWithLifetime,
57+
{} as Transaction & TransactionWithinSizeLimit & TransactionWithLifetime,
5858
];
5959

6060
// When we sign both transactions using that signer.
@@ -75,8 +75,8 @@ describe('createNoopSigner', () => {
7575

7676
// And given we have a couple of mock transactions to sign.
7777
const mockTransactions = [
78-
{} as Transaction & TransactionWithLifetime,
79-
{} as Transaction & TransactionWithLifetime,
78+
{} as Transaction & TransactionWithinSizeLimit & TransactionWithLifetime,
79+
{} as Transaction & TransactionWithinSizeLimit & TransactionWithLifetime,
8080
];
8181

8282
// When we sign both transactions using that signer.

packages/signers/src/__typetests__/sign-transaction-typetest.ts

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import {
1111
import {
1212
FullySignedTransaction,
1313
Transaction,
14-
TransactionWithBlockhashLifetime,
15-
TransactionWithDurableNonceLifetime,
1614
TransactionWithinSizeLimit,
1715
TransactionWithLifetime,
1816
} from '@solana/transactions';
@@ -26,29 +24,29 @@ import {
2624
import { TransactionMessageWithSingleSendingSigner } from '../transaction-with-single-sending-signer';
2725

2826
{
29-
// [partiallySignTransactionMessageWithSigners]: returns a transaction with a blockhash lifetime
27+
// [partiallySignTransactionMessageWithSigners]: returns a transaction with a lifetime when the input message has a blockhash lifetime
3028
const transactionMessage = null as unknown as BaseTransactionMessage &
3129
TransactionMessageWithBlockhashLifetime &
3230
TransactionMessageWithFeePayer &
3331
TransactionMessageWithSigners;
3432
partiallySignTransactionMessageWithSigners(transactionMessage) satisfies Promise<
35-
Readonly<Transaction & TransactionWithBlockhashLifetime>
33+
Readonly<Transaction & TransactionWithLifetime>
3634
>;
3735
}
3836

3937
{
40-
// [partiallySignTransactionMessageWithSigners]: returns a transaction with a durable nonce lifetime
38+
// [partiallySignTransactionMessageWithSigners]: returns a transaction with a lifetime when the input message has a durable nonce lifetime
4139
const transactionMessage = null as unknown as BaseTransactionMessage &
4240
TransactionMessageWithDurableNonceLifetime &
4341
TransactionMessageWithFeePayer &
4442
TransactionMessageWithSigners;
4543
partiallySignTransactionMessageWithSigners(transactionMessage) satisfies Promise<
46-
Readonly<Transaction & TransactionWithDurableNonceLifetime>
44+
Readonly<Transaction & TransactionWithLifetime>
4745
>;
4846
}
4947

5048
{
51-
// [partiallySignTransactionMessageWithSigners]: returns a transaction with an unknown lifetime
49+
// [partiallySignTransactionMessageWithSigners]: returns a transaction with an unknown lifetime when the input message has an unknown lifetime
5250
const transactionMessage = null as unknown as BaseTransactionMessage &
5351
TransactionMessageWithFeePayer &
5452
TransactionMessageWithLifetime &
@@ -59,12 +57,11 @@ import { TransactionMessageWithSingleSendingSigner } from '../transaction-with-s
5957
}
6058

6159
{
62-
// [partiallySignTransactionMessageWithSigners]: returns a transaction with no lifetime constraint
60+
// [partiallySignTransactionMessageWithSigners]: returns a transaction with a lifetime when the input message has no lifetime
6361
const transactionMessage = null as unknown as BaseTransactionMessage &
6462
TransactionMessageWithFeePayer &
6563
TransactionMessageWithSigners;
6664
partiallySignTransactionMessageWithSigners(transactionMessage) satisfies Promise<Readonly<Transaction>>;
67-
// @ts-expect-error Expects no lifetime constraint
6865
partiallySignTransactionMessageWithSigners(transactionMessage) satisfies Promise<
6966
Readonly<Transaction & TransactionWithLifetime>
7067
>;
@@ -83,29 +80,29 @@ import { TransactionMessageWithSingleSendingSigner } from '../transaction-with-s
8380
}
8481

8582
{
86-
// [signTransactionMessageWithSigners]: returns a fully signed transaction with a blockhash lifetime
83+
// [signTransactionMessageWithSigners]: returns a fully signed transaction with a lifetime when the input message has a blockhash lifetime
8784
const transactionMessage = null as unknown as BaseTransactionMessage &
8885
TransactionMessageWithBlockhashLifetime &
8986
TransactionMessageWithFeePayer &
9087
TransactionMessageWithSigners;
9188
signTransactionMessageWithSigners(transactionMessage) satisfies Promise<
92-
Readonly<FullySignedTransaction & Transaction & TransactionWithBlockhashLifetime>
89+
Readonly<FullySignedTransaction & Transaction & TransactionWithLifetime>
9390
>;
9491
}
9592

9693
{
97-
// [signTransactionMessageWithSigners]: returns a fully signed transaction with a durable nonce lifetime
94+
// [signTransactionMessageWithSigners]: returns a fully signed transaction with a lifetime when the input message has a durable nonce lifetime
9895
const transactionMessage = null as unknown as BaseTransactionMessage &
9996
TransactionMessageWithDurableNonceLifetime &
10097
TransactionMessageWithFeePayer &
10198
TransactionMessageWithSigners;
10299
signTransactionMessageWithSigners(transactionMessage) satisfies Promise<
103-
Readonly<FullySignedTransaction & Transaction & TransactionWithDurableNonceLifetime>
100+
Readonly<FullySignedTransaction & Transaction & TransactionWithLifetime>
104101
>;
105102
}
106103

107104
{
108-
// [signTransactionMessageWithSigners]: returns a fully signed transaction with an unknown lifetime
105+
// [signTransactionMessageWithSigners]: returns a fully signed transaction with an unknown lifetime when the input message has an unknown lifetime
109106
const transactionMessage = null as unknown as BaseTransactionMessage &
110107
TransactionMessageWithFeePayer &
111108
TransactionMessageWithLifetime &
@@ -116,12 +113,11 @@ import { TransactionMessageWithSingleSendingSigner } from '../transaction-with-s
116113
}
117114

118115
{
119-
// [signTransactionMessageWithSigners]: returns a transaction with no lifetime constraint
116+
// [signTransactionMessageWithSigners]: returns a transaction with a lifetime when the input message has no lifetime
120117
const transactionMessage = null as unknown as BaseTransactionMessage &
121118
TransactionMessageWithFeePayer &
122119
TransactionMessageWithSigners;
123120
signTransactionMessageWithSigners(transactionMessage) satisfies Promise<Readonly<Transaction>>;
124-
// @ts-expect-error Expects no lifetime constraint
125121
signTransactionMessageWithSigners(transactionMessage) satisfies Promise<
126122
Readonly<Transaction & TransactionWithLifetime>
127123
>;

packages/signers/src/sign-transaction.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { BaseTransactionMessage, TransactionMessageWithFeePayer } from '@solana/
44
import {
55
assertIsFullySignedTransaction,
66
compileTransaction,
7-
FullySignedTransaction,
7+
SendableTransaction,
88
Transaction,
9-
TransactionFromTransactionMessage,
9+
TransactionWithinSizeLimit,
1010
TransactionWithLifetime,
1111
} from '@solana/transactions';
1212

@@ -69,7 +69,7 @@ export async function partiallySignTransactionMessageWithSigners<
6969
>(
7070
transactionMessage: TTransactionMessage,
7171
config?: TransactionPartialSignerConfig,
72-
): Promise<TransactionFromTransactionMessage<TTransactionMessage>> {
72+
): Promise<Transaction & TransactionWithinSizeLimit & TransactionWithLifetime> {
7373
const { partialSigners, modifyingSigners } = categorizeTransactionSigners(
7474
deduplicateSigners(getSignersFromTransactionMessage(transactionMessage).filter(isTransactionSigner)),
7575
{ identifySendingSigner: false },
@@ -114,7 +114,7 @@ export async function signTransactionMessageWithSigners<
114114
>(
115115
transactionMessage: TTransactionMessage,
116116
config?: TransactionPartialSignerConfig,
117-
): Promise<FullySignedTransaction & TransactionFromTransactionMessage<TTransactionMessage>> {
117+
): Promise<SendableTransaction & Transaction & TransactionWithLifetime> {
118118
const signedTransaction = await partiallySignTransactionMessageWithSigners(transactionMessage, config);
119119
assertIsFullySignedTransaction(signedTransaction);
120120
return signedTransaction;
@@ -283,21 +283,19 @@ async function signModifyingAndPartialTransactionSigners<
283283
modifyingSigners: readonly TransactionModifyingSigner[] = [],
284284
partialSigners: readonly TransactionPartialSigner[] = [],
285285
config?: TransactionModifyingSignerConfig,
286-
): Promise<TransactionFromTransactionMessage<TTransactionMessage>> {
287-
type ReturnType = TransactionFromTransactionMessage<TTransactionMessage>;
288-
286+
): Promise<Transaction & TransactionWithinSizeLimit & TransactionWithLifetime> {
289287
// serialize the transaction
290288
const transaction = compileTransaction(transactionMessage);
291289

292290
// Handle modifying signers sequentially.
293-
const modifiedTransaction = await modifyingSigners.reduce(
291+
const modifiedTransaction = (await modifyingSigners.reduce(
294292
async (transaction, modifyingSigner) => {
295293
config?.abortSignal?.throwIfAborted();
296294
const [tx] = await modifyingSigner.modifyAndSignTransactions([await transaction], config);
297295
return Object.freeze(tx);
298296
},
299297
Promise.resolve(transaction) as Promise<Readonly<Transaction & TransactionWithLifetime>>,
300-
);
298+
)) as Transaction & TransactionWithinSizeLimit & TransactionWithLifetime;
301299

302300
// Handle partial signers in parallel.
303301
config?.abortSignal?.throwIfAborted();
@@ -315,5 +313,5 @@ async function signModifyingAndPartialTransactionSigners<
315313
return { ...signatures, ...signatureDictionary };
316314
}, modifiedTransaction.signatures ?? {}),
317315
),
318-
}) as ReturnType;
316+
});
319317
}

packages/signers/src/transaction-modifying-signer.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Address } from '@solana/addresses';
22
import { SOLANA_ERROR__SIGNER__EXPECTED_TRANSACTION_MODIFYING_SIGNER, SolanaError } from '@solana/errors';
3-
import { Transaction } from '@solana/transactions';
3+
import { Transaction, TransactionWithinSizeLimit, TransactionWithLifetime } from '@solana/transactions';
44

55
import { BaseTransactionSignerConfig } from './types';
66

@@ -21,17 +21,18 @@ export type TransactionModifyingSignerConfig = BaseTransactionSignerConfig;
2121
* {@link SignatureDictionary}, its
2222
* {@link TransactionModifyingSigner#modifyAndSignTransactions | modifyAndSignTransactions} function
2323
* returns an updated {@link Transaction} with a potentially modified set of instructions and
24-
* signature dictionary.
24+
* signature dictionary. The returned transaction must be within the transaction size limit,
25+
* and include a `lifetimeConstraint`.
2526
*
2627
* @typeParam TAddress - Supply a string literal to define a signer having a particular address.
2728
*
2829
* @example
2930
* ```ts
3031
* const signer: TransactionModifyingSigner<'1234..5678'> = {
3132
* address: address('1234..5678'),
32-
* modifyAndSignTransactions: async <T extends Transaction>(
33-
* transactions: T[]
34-
* ): Promise<T[]> => {
33+
* modifyAndSignTransactions: async (
34+
* transactions: Transaction[]
35+
* ): Promise<(Transaction & TransactionWithinSizeLimit & TransactionWithLifetime)[]> => {
3536
* // My custom signing logic.
3637
* },
3738
* };
@@ -55,10 +56,10 @@ export type TransactionModifyingSignerConfig = BaseTransactionSignerConfig;
5556
*/
5657
export type TransactionModifyingSigner<TAddress extends string = string> = Readonly<{
5758
address: Address<TAddress>;
58-
modifyAndSignTransactions<T extends Transaction>(
59-
transactions: readonly T[],
59+
modifyAndSignTransactions(
60+
transactions: readonly (Transaction | (Transaction & TransactionWithLifetime))[],
6061
config?: TransactionModifyingSignerConfig,
61-
): Promise<readonly T[]>;
62+
): Promise<readonly (Transaction & TransactionWithinSizeLimit & TransactionWithLifetime)[]>;
6263
}>;
6364

6465
/**

packages/signers/src/transaction-partial-signer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Address } from '@solana/addresses';
22
import { SOLANA_ERROR__SIGNER__EXPECTED_TRANSACTION_PARTIAL_SIGNER, SolanaError } from '@solana/errors';
3-
import { Transaction, TransactionWithLifetime } from '@solana/transactions';
3+
import { Transaction, TransactionWithinSizeLimit, TransactionWithLifetime } from '@solana/transactions';
44

55
import { BaseTransactionSignerConfig, SignatureDictionary } from './types';
66

@@ -49,7 +49,7 @@ export type TransactionPartialSignerConfig = BaseTransactionSignerConfig;
4949
export type TransactionPartialSigner<TAddress extends string = string> = Readonly<{
5050
address: Address<TAddress>;
5151
signTransactions(
52-
transactions: readonly (Transaction & TransactionWithLifetime)[],
52+
transactions: readonly (Transaction & TransactionWithinSizeLimit & TransactionWithLifetime)[],
5353
config?: TransactionPartialSignerConfig,
5454
): Promise<readonly SignatureDictionary[]>;
5555
}>;

packages/signers/src/transaction-sending-signer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Address } from '@solana/addresses';
22
import { SOLANA_ERROR__SIGNER__EXPECTED_TRANSACTION_SENDING_SIGNER, SolanaError } from '@solana/errors';
33
import { SignatureBytes } from '@solana/keys';
4-
import { Transaction } from '@solana/transactions';
4+
import { Transaction, TransactionWithLifetime } from '@solana/transactions';
55

66
import { BaseTransactionSignerConfig } from './types';
77

@@ -61,7 +61,7 @@ export type TransactionSendingSignerConfig = BaseTransactionSignerConfig;
6161
export type TransactionSendingSigner<TAddress extends string = string> = Readonly<{
6262
address: Address<TAddress>;
6363
signAndSendTransactions(
64-
transactions: readonly Transaction[],
64+
transactions: readonly (Transaction | (Transaction & TransactionWithLifetime))[],
6565
config?: TransactionSendingSignerConfig,
6666
): Promise<readonly SignatureBytes[]>;
6767
}>;

0 commit comments

Comments
 (0)