@solana/react 6.3.1 → 6.3.2-canary-20260313143218

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,101 @@
1
+ import { address } from '@solana/addresses';
2
+ import { SOLANA_ERROR__SIGNER__WALLET_MULTISIGN_UNIMPLEMENTED, SolanaError } from '@solana/errors';
3
+ import { SignatureBytes } from '@solana/keys';
4
+ import { getAbortablePromise } from '@solana/promises';
5
+ import { TransactionSendingSigner } from '@solana/signers';
6
+ import { getTransactionEncoder } from '@solana/transactions';
7
+ import { UiWalletAccount } from '@wallet-standard/ui';
8
+ import { useMemo, useRef } from 'react';
9
+
10
+ import { OnlySolanaChains } from './chain';
11
+ import { useSignAndSendTransaction } from './useSignAndSendTransaction';
12
+
13
+ /**
14
+ * Use this to get a {@link TransactionSendingSigner} capable of signing a serialized transaction
15
+ * with the private key of a {@link UiWalletAccount} and sending it to the network for processing.
16
+ *
17
+ * @param chain The identifier of the chain the transaction is destined for. Wallets may use this to
18
+ * simulate the transaction for the user.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * import { useWalletAccountTransactionSendingSigner } from '@solana/react';
23
+ * import {
24
+ * appendTransactionMessageInstruction,
25
+ * createSolanaRpc,
26
+ * getBase58Decoder,
27
+ * pipe,
28
+ * setTransactionMessageFeePayerSigner,
29
+ * setTransactionMessageLifetimeUsingBlockhash,
30
+ * signAndSendTransactionMessageWithSigners,
31
+ * } from '@solana/kit';
32
+ *
33
+ * function RecordMemoButton({ account, rpc, text }) {
34
+ * const signer = useWalletAccountTransactionSendingSigner(account, 'solana:devnet');
35
+ * return (
36
+ * <button
37
+ * onClick={async () => {
38
+ * try {
39
+ * const { value: latestBlockhash } = await createSolanaRpc('https://api.devnet.solana.com')
40
+ * .getLatestBlockhash()
41
+ * .send();
42
+ * const message = pipe(
43
+ * createTransactionMessage({ version: 'legacy' }),
44
+ * m => setTransactionMessageFeePayerSigner(signer, m),
45
+ * m => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
46
+ * m => appendTransactionMessageInstruction(getAddMemoInstruction({ memo: text }), m),
47
+ * );
48
+ * const signatureBytes = await signAndSendTransactionMessageWithSigners(message);
49
+ * const base58Signature = getBase58Decoder().decode(signature);
50
+ * window.alert(`View transaction: https://explorer.solana.com/tx/${base58Signature}?cluster=devnet`);
51
+ * } catch (e) {
52
+ * console.error('Failed to record memo', e);
53
+ * }
54
+ * }}
55
+ * >
56
+ * Record Memo
57
+ * </button>
58
+ * );
59
+ * }
60
+ * ```
61
+ */
62
+ export function useWalletAccountTransactionSendingSigner<TWalletAccount extends UiWalletAccount>(
63
+ uiWalletAccount: TWalletAccount,
64
+ chain: OnlySolanaChains<TWalletAccount['chains']>,
65
+ ): TransactionSendingSigner<TWalletAccount['address']>;
66
+ export function useWalletAccountTransactionSendingSigner<TWalletAccount extends UiWalletAccount>(
67
+ uiWalletAccount: TWalletAccount,
68
+ chain: `solana:${string}`,
69
+ ): TransactionSendingSigner<TWalletAccount['address']>;
70
+ export function useWalletAccountTransactionSendingSigner<TWalletAccount extends UiWalletAccount>(
71
+ uiWalletAccount: TWalletAccount,
72
+ chain: `solana:${string}`,
73
+ ): TransactionSendingSigner<TWalletAccount['address']> {
74
+ const encoderRef = useRef<ReturnType<typeof getTransactionEncoder> | null>(null);
75
+ const signAndSendTransaction = useSignAndSendTransaction(uiWalletAccount, chain);
76
+ return useMemo(
77
+ () => ({
78
+ address: address(uiWalletAccount.address),
79
+ async signAndSendTransactions(transactions, config = {}) {
80
+ const { abortSignal, ...options } = config;
81
+ abortSignal?.throwIfAborted();
82
+ const transactionEncoder = (encoderRef.current ||= getTransactionEncoder());
83
+ if (transactions.length > 1) {
84
+ throw new SolanaError(SOLANA_ERROR__SIGNER__WALLET_MULTISIGN_UNIMPLEMENTED);
85
+ }
86
+ if (transactions.length === 0) {
87
+ return [];
88
+ }
89
+ const [transaction] = transactions;
90
+ const wireTransactionBytes = transactionEncoder.encode(transaction);
91
+ const inputWithOptions = {
92
+ ...options,
93
+ transaction: wireTransactionBytes as Uint8Array,
94
+ };
95
+ const { signature } = await getAbortablePromise(signAndSendTransaction(inputWithOptions), abortSignal);
96
+ return Object.freeze([signature as SignatureBytes]);
97
+ },
98
+ }),
99
+ [signAndSendTransaction, uiWalletAccount.address],
100
+ );
101
+ }
@@ -0,0 +1,146 @@
1
+ import { address } from '@solana/addresses';
2
+ import { bytesEqual } from '@solana/codecs-core';
3
+ import { SOLANA_ERROR__SIGNER__WALLET_MULTISIGN_UNIMPLEMENTED, SolanaError } from '@solana/errors';
4
+ import { getAbortablePromise } from '@solana/promises';
5
+ import { TransactionModifyingSigner } from '@solana/signers';
6
+ import { getCompiledTransactionMessageDecoder } from '@solana/transaction-messages';
7
+ import {
8
+ assertIsTransactionWithinSizeLimit,
9
+ getTransactionCodec,
10
+ getTransactionLifetimeConstraintFromCompiledTransactionMessage,
11
+ Transaction,
12
+ TransactionWithinSizeLimit,
13
+ TransactionWithLifetime,
14
+ } from '@solana/transactions';
15
+ import { UiWalletAccount } from '@wallet-standard/ui';
16
+ import { useMemo, useRef } from 'react';
17
+
18
+ import { OnlySolanaChains } from './chain';
19
+ import { useSignTransaction } from './useSignTransaction';
20
+
21
+ /**
22
+ * Use this to get a {@link TransactionSigner} capable of signing serialized transactions with the
23
+ * private key of a {@link UiWalletAccount}
24
+ *
25
+ * @returns A {@link TransactionModifyingSigner}. This is a conservative assumption based on the
26
+ * fact that your application can not control whether or not the wallet will modify the transaction
27
+ * before signing it (eg. to add guard instructions, or a priority fee budget). Otherwise this
28
+ * method could more specifically return a {@link TransactionSigner} or a
29
+ * {@link TransactionPartialSigner}.
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * import { useWalletAccountTransactionSigner } from '@solana/react';
34
+ *
35
+ * function SignTransactionButton({ account, transaction }) {
36
+ * const transactionSigner = useWalletAccountTransactionSigner(account, 'solana:devnet');
37
+ * return (
38
+ * <button
39
+ * onClick={async () => {
40
+ * try {
41
+ * const [{ signatures }] = await transactionSigner.modifyAndSignTransactions([transaction]);
42
+ * const signatureBytes = signatures[transactionSigner.address];
43
+ * window.alert(`Signature bytes: ${signatureBytes.toString()}`);
44
+ * } catch (e) {
45
+ * console.error('Failed to sign transaction', e);
46
+ * }
47
+ * }}
48
+ * >
49
+ * Sign Transaction
50
+ * </button>
51
+ * );
52
+ * }
53
+ * ```
54
+ */
55
+ export function useWalletAccountTransactionSigner<TWalletAccount extends UiWalletAccount>(
56
+ uiWalletAccount: TWalletAccount,
57
+ chain: OnlySolanaChains<TWalletAccount['chains']>,
58
+ ): TransactionModifyingSigner<TWalletAccount['address']>;
59
+ export function useWalletAccountTransactionSigner<TWalletAccount extends UiWalletAccount>(
60
+ uiWalletAccount: TWalletAccount,
61
+ chain: `solana:${string}`,
62
+ ): TransactionModifyingSigner<TWalletAccount['address']>;
63
+ export function useWalletAccountTransactionSigner<TWalletAccount extends UiWalletAccount>(
64
+ uiWalletAccount: TWalletAccount,
65
+ chain: `solana:${string}`,
66
+ ): TransactionModifyingSigner<TWalletAccount['address']> {
67
+ const encoderRef = useRef<ReturnType<typeof getTransactionCodec> | null>(null);
68
+ const signTransaction = useSignTransaction(uiWalletAccount, chain);
69
+ return useMemo(
70
+ () => ({
71
+ address: address(uiWalletAccount.address),
72
+ async modifyAndSignTransactions(transactions, config = {}) {
73
+ const { abortSignal, ...options } = config;
74
+ abortSignal?.throwIfAborted();
75
+ const transactionCodec = (encoderRef.current ||= getTransactionCodec());
76
+ if (transactions.length > 1) {
77
+ throw new SolanaError(SOLANA_ERROR__SIGNER__WALLET_MULTISIGN_UNIMPLEMENTED);
78
+ }
79
+ if (transactions.length === 0) {
80
+ return transactions as readonly (Transaction &
81
+ TransactionWithinSizeLimit &
82
+ TransactionWithLifetime)[];
83
+ }
84
+ const [transaction] = transactions;
85
+ const wireTransactionBytes = transactionCodec.encode(transaction);
86
+ const inputWithOptions = {
87
+ ...options,
88
+ transaction: wireTransactionBytes as Uint8Array,
89
+ };
90
+ const { signedTransaction } = await getAbortablePromise(signTransaction(inputWithOptions), abortSignal);
91
+ const decodedSignedTransaction = transactionCodec.decode(
92
+ signedTransaction,
93
+ ) as (typeof transactions)[number];
94
+
95
+ assertIsTransactionWithinSizeLimit(decodedSignedTransaction);
96
+
97
+ const existingLifetime =
98
+ 'lifetimeConstraint' in transaction
99
+ ? (transaction as TransactionWithLifetime).lifetimeConstraint
100
+ : undefined;
101
+
102
+ if (existingLifetime) {
103
+ if (bytesEqual(decodedSignedTransaction.messageBytes, transaction.messageBytes)) {
104
+ // If the transaction has identical bytes, the lifetime won't have changed
105
+ return Object.freeze([
106
+ {
107
+ ...decodedSignedTransaction,
108
+ lifetimeConstraint: existingLifetime,
109
+ },
110
+ ]);
111
+ }
112
+
113
+ // If the transaction has changed, check the lifetime constraint field
114
+ const compiledTransactionMessage = getCompiledTransactionMessageDecoder().decode(
115
+ decodedSignedTransaction.messageBytes,
116
+ );
117
+ const currentToken =
118
+ 'blockhash' in existingLifetime ? existingLifetime.blockhash : existingLifetime.nonce;
119
+
120
+ if (compiledTransactionMessage.lifetimeToken === currentToken) {
121
+ return Object.freeze([
122
+ {
123
+ ...decodedSignedTransaction,
124
+ lifetimeConstraint: existingLifetime,
125
+ },
126
+ ]);
127
+ }
128
+ }
129
+
130
+ // If we get here then there is no existing lifetime, or the lifetime has changed. We need to attach a new lifetime
131
+ const compiledTransactionMessage = getCompiledTransactionMessageDecoder().decode(
132
+ decodedSignedTransaction.messageBytes,
133
+ );
134
+ const lifetimeConstraint =
135
+ await getTransactionLifetimeConstraintFromCompiledTransactionMessage(compiledTransactionMessage);
136
+ return Object.freeze([
137
+ {
138
+ ...decodedSignedTransaction,
139
+ lifetimeConstraint,
140
+ },
141
+ ]);
142
+ },
143
+ }),
144
+ [uiWalletAccount.address, signTransaction],
145
+ );
146
+ }