@solana/transaction-confirmation 2.0.0-20241006045741

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.
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2023 Solana Labs, Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,179 @@
1
+ [![npm][npm-image]][npm-url]
2
+ [![npm-downloads][npm-downloads-image]][npm-url]
3
+ [![semantic-release][semantic-release-image]][semantic-release-url]
4
+ <br />
5
+ [![code-style-prettier][code-style-prettier-image]][code-style-prettier-url]
6
+
7
+ [code-style-prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square
8
+ [code-style-prettier-url]: https://github.com/prettier/prettier
9
+ [npm-downloads-image]: https://img.shields.io/npm/dm/@solana/transaction-confirmation/rc.svg?style=flat
10
+ [npm-image]: https://img.shields.io/npm/v/@solana/transaction-confirmation/rc.svg?style=flat
11
+ [npm-url]: https://www.npmjs.com/package/@solana/transaction-confirmation/v/rc
12
+ [semantic-release-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
13
+ [semantic-release-url]: https://github.com/semantic-release/semantic-release
14
+
15
+ # @solana/transaction-confirmation
16
+
17
+ This package contains utilities for confirming transactions and for building your own transaction confirmation strategies.
18
+
19
+ ## Functions
20
+
21
+ ### `createBlockHeightExceedencePromiseFactory()`
22
+
23
+ When a transaction's lifetime is tied to a blockhash, that transaction can be landed on the network until that blockhash expires. All blockhashes have a block height after which they are considered to have expired. A block height exceedence promise throws when the network progresses past that block height.
24
+
25
+ ```ts
26
+ import { isSolanaError, SolanaError } from '@solana/errors';
27
+ import { createBlockHeightExceedencePromiseFactory } from '@solana/transaction-confirmation';
28
+
29
+ const getBlockHeightExceedencePromise = createBlockHeightExceedencePromiseFactory({
30
+ rpc,
31
+ rpcSubscriptions,
32
+ });
33
+ try {
34
+ await getBlockHeightExceedencePromise({ lastValidBlockHeight });
35
+ } catch (e) {
36
+ if (isSolanaError(e, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED)) {
37
+ console.error(
38
+ `The block height of the network has exceeded ${e.context.lastValidBlockHeight}. ` +
39
+ `It is now ${e.context.currentBlockHeight}`,
40
+ );
41
+ // Re-sign and retry the transaction.
42
+ return;
43
+ }
44
+ throw e;
45
+ }
46
+ ```
47
+
48
+ ### `createNonceInvalidationPromiseFactory()`
49
+
50
+ When a transaction's lifetime is tied to the value stored in a nonce account, that transaction can be landed on the network until the nonce is advanced to a new value. A nonce invalidation promise throws when the value stored in a nonce account is not the expected one.
51
+
52
+ ```ts
53
+ import { isSolanaError, SolanaError } from '@solana/errors';
54
+ import { createNonceInvalidationPromiseFactory } from '@solana/transaction-confirmation';
55
+
56
+ const getNonceInvalidationPromise = createNonceInvalidationPromiseFactory({
57
+ rpc,
58
+ rpcSubscriptions,
59
+ });
60
+ try {
61
+ await getNonceInvalidationPromise({
62
+ currentNonceValue,
63
+ nonceAccountAddress,
64
+ });
65
+ } catch (e) {
66
+ if (isSolanaError(e, SOLANA_ERROR__NONCE_INVALID)) {
67
+ console.error(`The nonce has advanced to ${e.context.actualNonceValue}`);
68
+ // Re-sign and retry the transaction.
69
+ return;
70
+ } else if (isSolanaError(e, SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND)) {
71
+ console.error(`No nonce account was found at ${nonceAccountAddress}`);
72
+ }
73
+ throw e;
74
+ }
75
+ ```
76
+
77
+ ### `createRecentSignatureConfirmationPromiseFactory()`
78
+
79
+ The status of recently-landed transactions is available in the network's status cache. A recent signature confirmation promise resolves when a transaction achieves the target confirmation commitment, and throws when the transaction fails with an error.
80
+
81
+ ```ts
82
+ import { createRecentSignatureConfirmationPromiseFactory } from '@solana/transaction-confirmation';
83
+
84
+ const getRecentSignatureConfirmationPromise = createRecentSignatureConfirmationPromiseFactory({
85
+ rpc,
86
+ rpcSubscriptions,
87
+ });
88
+ try {
89
+ await getRecentSignatureConfirmationPromise({
90
+ commitment,
91
+ signature,
92
+ });
93
+ console.log(`The transaction with signature \`${signature}\` has achieved a commitment level of \`${commitment}\``);
94
+ } catch (e) {
95
+ console.error(`The transaction with signature \`${signature}\` failed`, e.cause);
96
+ throw e;
97
+ }
98
+ ```
99
+
100
+ ### `getTimeoutPromise()`
101
+
102
+ When no other heuristic exists to infer that a transaction has expired, you can use this promise factory with a commitment level. It throws after 30 seconds when the commitment is `processed`, and 60 seconds otherwise. You would typically race this with another confirmation strategy.
103
+
104
+ ```ts
105
+ import { safeRace } from '@solana/promises';
106
+ import { getTimeoutPromise } from '@solana/transaction-confirmation';
107
+
108
+ try {
109
+ await safeRace([getCustomTransactionConfirmationPromise(/* ... */), getTimeoutPromise({ commitment })]);
110
+ } catch (e) {
111
+ if (e instanceof DOMException && e.name === 'TimeoutError') {
112
+ console.log('Could not confirm transaction after a timeout');
113
+ }
114
+ throw e;
115
+ }
116
+ ```
117
+
118
+ ### `waitForDurableNonceTransactionConfirmation()`
119
+
120
+ Supply your own confirmation implementations to this function to create a custom nonce transaction confirmation strategy.
121
+
122
+ ```ts
123
+ import { waitForDurableNonceTransactionConfirmation } from '@solana/transaction-confirmation';
124
+
125
+ try {
126
+ await waitForDurableNonceTransactionConfirmation({
127
+ getNonceInvalidationPromise({ abortSignal, commitment, currentNonceValue, nonceAccountAddress }) {
128
+ // Return a promise that rejects when a nonce becomes invalid.
129
+ },
130
+ getRecentSignatureConfirmationPromise({ abortSignal, commitment, signature }) {
131
+ // Return a promise that resolves when a transaction achieves confirmation
132
+ },
133
+ });
134
+ } catch (e) {
135
+ // Handle errors.
136
+ }
137
+ ```
138
+
139
+ ### `waitForRecentTransactionConfirmation()`
140
+
141
+ Supply your own confirmation implementations to this function to create a custom nonce transaction confirmation strategy.
142
+
143
+ ```ts
144
+ import { waitForRecentTransactionConfirmation } from '@solana/transaction-confirmation';
145
+
146
+ try {
147
+ await waitForRecentTransactionConfirmation({
148
+ getBlockHeightExceedencePromise({ abortSignal, commitment, lastValidBlockHeight }) {
149
+ // Return a promise that rejects when the blockhash's block height has been exceeded
150
+ },
151
+ getRecentSignatureConfirmationPromise({ abortSignal, commitment, signature }) {
152
+ // Return a promise that resolves when a transaction achieves confirmation
153
+ },
154
+ });
155
+ } catch (e) {
156
+ // Handle errors.
157
+ }
158
+ ```
159
+
160
+ ### `waitForRecentTransactionConfirmationUntilTimeout()`
161
+
162
+ Supply your own confirmation implementations to this function to create a custom nonce transaction confirmation strategy.
163
+
164
+ ```ts
165
+ import { waitForRecentTransactionConfirmationUntilTimeout } from '@solana/transaction-confirmation';
166
+
167
+ try {
168
+ await waitForRecentTransactionConfirmationUntilTimeout({
169
+ getTimeoutPromise({ abortSignal, commitment }) {
170
+ // Return a promise that rejects after your chosen timeout
171
+ },
172
+ getRecentSignatureConfirmationPromise({ abortSignal, commitment, signature }) {
173
+ // Return a promise that resolves when a transaction achieves confirmation
174
+ },
175
+ });
176
+ } catch (e) {
177
+ // Handle errors.
178
+ }
179
+ ```
@@ -0,0 +1,286 @@
1
+ 'use strict';
2
+
3
+ var errors = require('@solana/errors');
4
+ var codecsStrings = require('@solana/codecs-strings');
5
+ var promises = require('@solana/promises');
6
+ var rpcTypes = require('@solana/rpc-types');
7
+ var transactions = require('@solana/transactions');
8
+
9
+ // src/confirmation-strategy-blockheight.ts
10
+ function createBlockHeightExceedencePromiseFactory({
11
+ rpc,
12
+ rpcSubscriptions
13
+ }) {
14
+ return async function getBlockHeightExceedencePromise({
15
+ abortSignal: callerAbortSignal,
16
+ commitment,
17
+ lastValidBlockHeight
18
+ }) {
19
+ const abortController = new AbortController();
20
+ const handleAbort = () => {
21
+ abortController.abort();
22
+ };
23
+ callerAbortSignal.addEventListener("abort", handleAbort, { signal: abortController.signal });
24
+ async function getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight() {
25
+ const { absoluteSlot, blockHeight } = await rpc.getEpochInfo({ commitment }).send({ abortSignal: abortController.signal });
26
+ return {
27
+ blockHeight,
28
+ differenceBetweenSlotHeightAndBlockHeight: absoluteSlot - blockHeight
29
+ };
30
+ }
31
+ try {
32
+ const [slotNotifications, { blockHeight: initialBlockHeight, differenceBetweenSlotHeightAndBlockHeight }] = await Promise.all([
33
+ rpcSubscriptions.slotNotifications().subscribe({ abortSignal: abortController.signal }),
34
+ getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight()
35
+ ]);
36
+ let currentBlockHeight = initialBlockHeight;
37
+ if (currentBlockHeight <= lastValidBlockHeight) {
38
+ let lastKnownDifferenceBetweenSlotHeightAndBlockHeight = differenceBetweenSlotHeightAndBlockHeight;
39
+ for await (const slotNotification of slotNotifications) {
40
+ const { slot } = slotNotification;
41
+ if (slot - lastKnownDifferenceBetweenSlotHeightAndBlockHeight > lastValidBlockHeight) {
42
+ const {
43
+ blockHeight: recheckedBlockHeight,
44
+ differenceBetweenSlotHeightAndBlockHeight: currentDifferenceBetweenSlotHeightAndBlockHeight
45
+ } = await getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight();
46
+ currentBlockHeight = recheckedBlockHeight;
47
+ if (currentBlockHeight > lastValidBlockHeight) {
48
+ break;
49
+ } else {
50
+ lastKnownDifferenceBetweenSlotHeightAndBlockHeight = currentDifferenceBetweenSlotHeightAndBlockHeight;
51
+ }
52
+ }
53
+ }
54
+ }
55
+ throw new errors.SolanaError(errors.SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {
56
+ currentBlockHeight,
57
+ lastValidBlockHeight
58
+ });
59
+ } finally {
60
+ abortController.abort();
61
+ }
62
+ };
63
+ }
64
+ var NONCE_VALUE_OFFSET = 4 + // version(u32)
65
+ 4 + // state(u32)
66
+ 32;
67
+ function createNonceInvalidationPromiseFactory({
68
+ rpc,
69
+ rpcSubscriptions
70
+ }) {
71
+ return async function getNonceInvalidationPromise({
72
+ abortSignal: callerAbortSignal,
73
+ commitment,
74
+ currentNonceValue: expectedNonceValue,
75
+ nonceAccountAddress
76
+ }) {
77
+ const abortController = new AbortController();
78
+ function handleAbort() {
79
+ abortController.abort();
80
+ }
81
+ callerAbortSignal.addEventListener("abort", handleAbort, { signal: abortController.signal });
82
+ const accountNotifications = await rpcSubscriptions.accountNotifications(nonceAccountAddress, { commitment, encoding: "base64" }).subscribe({ abortSignal: abortController.signal });
83
+ const base58Decoder = codecsStrings.getBase58Decoder();
84
+ const base64Encoder = codecsStrings.getBase64Encoder();
85
+ function getNonceFromAccountData([base64EncodedBytes]) {
86
+ const data = base64Encoder.encode(base64EncodedBytes);
87
+ const nonceValueBytes = data.slice(NONCE_VALUE_OFFSET, NONCE_VALUE_OFFSET + 32);
88
+ return base58Decoder.decode(nonceValueBytes);
89
+ }
90
+ const nonceAccountDidAdvancePromise = (async () => {
91
+ for await (const accountNotification of accountNotifications) {
92
+ const nonceValue = getNonceFromAccountData(accountNotification.value.data);
93
+ if (nonceValue !== expectedNonceValue) {
94
+ throw new errors.SolanaError(errors.SOLANA_ERROR__INVALID_NONCE, {
95
+ actualNonceValue: nonceValue,
96
+ expectedNonceValue
97
+ });
98
+ }
99
+ }
100
+ })();
101
+ const nonceIsAlreadyInvalidPromise = (async () => {
102
+ const { value: nonceAccount } = await rpc.getAccountInfo(nonceAccountAddress, {
103
+ commitment,
104
+ dataSlice: { length: 32, offset: NONCE_VALUE_OFFSET },
105
+ encoding: "base58"
106
+ }).send({ abortSignal: abortController.signal });
107
+ if (!nonceAccount) {
108
+ throw new errors.SolanaError(errors.SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND, {
109
+ nonceAccountAddress
110
+ });
111
+ }
112
+ const nonceValue = (
113
+ // This works because we asked for the exact slice of data representing the nonce
114
+ // value, and furthermore asked for it in `base58` encoding.
115
+ nonceAccount.data[0]
116
+ );
117
+ if (nonceValue !== expectedNonceValue) {
118
+ throw new errors.SolanaError(errors.SOLANA_ERROR__INVALID_NONCE, {
119
+ actualNonceValue: nonceValue,
120
+ expectedNonceValue
121
+ });
122
+ } else {
123
+ await new Promise(() => {
124
+ });
125
+ }
126
+ })();
127
+ try {
128
+ return await promises.safeRace([nonceAccountDidAdvancePromise, nonceIsAlreadyInvalidPromise]);
129
+ } finally {
130
+ abortController.abort();
131
+ }
132
+ };
133
+ }
134
+ function createRecentSignatureConfirmationPromiseFactory({
135
+ rpc,
136
+ rpcSubscriptions
137
+ }) {
138
+ return async function getRecentSignatureConfirmationPromise({
139
+ abortSignal: callerAbortSignal,
140
+ commitment,
141
+ signature
142
+ }) {
143
+ const abortController = new AbortController();
144
+ function handleAbort() {
145
+ abortController.abort();
146
+ }
147
+ callerAbortSignal.addEventListener("abort", handleAbort, { signal: abortController.signal });
148
+ const signatureStatusNotifications = await rpcSubscriptions.signatureNotifications(signature, { commitment }).subscribe({ abortSignal: abortController.signal });
149
+ const signatureDidCommitPromise = (async () => {
150
+ for await (const signatureStatusNotification of signatureStatusNotifications) {
151
+ if (signatureStatusNotification.value.err) {
152
+ throw errors.getSolanaErrorFromTransactionError(signatureStatusNotification.value.err);
153
+ } else {
154
+ return;
155
+ }
156
+ }
157
+ })();
158
+ const signatureStatusLookupPromise = (async () => {
159
+ const { value: signatureStatusResults } = await rpc.getSignatureStatuses([signature]).send({ abortSignal: abortController.signal });
160
+ const signatureStatus = signatureStatusResults[0];
161
+ if (signatureStatus && signatureStatus.confirmationStatus && rpcTypes.commitmentComparator(signatureStatus.confirmationStatus, commitment) >= 0) {
162
+ return;
163
+ } else {
164
+ await new Promise(() => {
165
+ });
166
+ }
167
+ })();
168
+ try {
169
+ return await promises.safeRace([signatureDidCommitPromise, signatureStatusLookupPromise]);
170
+ } finally {
171
+ abortController.abort();
172
+ }
173
+ };
174
+ }
175
+
176
+ // src/confirmation-strategy-timeout.ts
177
+ async function getTimeoutPromise({ abortSignal: callerAbortSignal, commitment }) {
178
+ return await new Promise((_, reject) => {
179
+ const handleAbort = (e) => {
180
+ clearTimeout(timeoutId);
181
+ const abortError = new DOMException(e.target.reason, "AbortError");
182
+ reject(abortError);
183
+ };
184
+ callerAbortSignal.addEventListener("abort", handleAbort);
185
+ const timeoutMs = commitment === "processed" ? 3e4 : 6e4;
186
+ const startMs = performance.now();
187
+ const timeoutId = (
188
+ // We use `setTimeout` instead of `AbortSignal.timeout()` because we want to measure
189
+ // elapsed time instead of active time.
190
+ // See https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static
191
+ setTimeout(() => {
192
+ const elapsedMs = performance.now() - startMs;
193
+ reject(new DOMException(`Timeout elapsed after ${elapsedMs} ms`, "TimeoutError"));
194
+ }, timeoutMs)
195
+ );
196
+ });
197
+ }
198
+ async function raceStrategies(signature, config, getSpecificStrategiesForRace) {
199
+ const { abortSignal: callerAbortSignal, commitment, getRecentSignatureConfirmationPromise } = config;
200
+ callerAbortSignal?.throwIfAborted();
201
+ const abortController = new AbortController();
202
+ if (callerAbortSignal) {
203
+ const handleAbort = () => {
204
+ abortController.abort();
205
+ };
206
+ callerAbortSignal.addEventListener("abort", handleAbort, { signal: abortController.signal });
207
+ }
208
+ try {
209
+ const specificStrategies = getSpecificStrategiesForRace({
210
+ ...config,
211
+ abortSignal: abortController.signal
212
+ });
213
+ return await promises.safeRace([
214
+ getRecentSignatureConfirmationPromise({
215
+ abortSignal: abortController.signal,
216
+ commitment,
217
+ signature
218
+ }),
219
+ ...specificStrategies
220
+ ]);
221
+ } finally {
222
+ abortController.abort();
223
+ }
224
+ }
225
+
226
+ // src/waiters.ts
227
+ async function waitForDurableNonceTransactionConfirmation(config) {
228
+ await raceStrategies(
229
+ transactions.getSignatureFromTransaction(config.transaction),
230
+ config,
231
+ function getSpecificStrategiesForRace({ abortSignal, commitment, getNonceInvalidationPromise, transaction }) {
232
+ return [
233
+ getNonceInvalidationPromise({
234
+ abortSignal,
235
+ commitment,
236
+ currentNonceValue: transaction.lifetimeConstraint.nonce,
237
+ nonceAccountAddress: transaction.lifetimeConstraint.nonceAccountAddress
238
+ })
239
+ ];
240
+ }
241
+ );
242
+ }
243
+ async function waitForRecentTransactionConfirmation(config) {
244
+ await raceStrategies(
245
+ transactions.getSignatureFromTransaction(config.transaction),
246
+ config,
247
+ function getSpecificStrategiesForRace({
248
+ abortSignal,
249
+ commitment,
250
+ getBlockHeightExceedencePromise,
251
+ transaction
252
+ }) {
253
+ return [
254
+ getBlockHeightExceedencePromise({
255
+ abortSignal,
256
+ commitment,
257
+ lastValidBlockHeight: transaction.lifetimeConstraint.lastValidBlockHeight
258
+ })
259
+ ];
260
+ }
261
+ );
262
+ }
263
+ async function waitForRecentTransactionConfirmationUntilTimeout(config) {
264
+ await raceStrategies(
265
+ config.signature,
266
+ config,
267
+ function getSpecificStrategiesForRace({ abortSignal, commitment, getTimeoutPromise: getTimeoutPromise2 }) {
268
+ return [
269
+ getTimeoutPromise2({
270
+ abortSignal,
271
+ commitment
272
+ })
273
+ ];
274
+ }
275
+ );
276
+ }
277
+
278
+ exports.createBlockHeightExceedencePromiseFactory = createBlockHeightExceedencePromiseFactory;
279
+ exports.createNonceInvalidationPromiseFactory = createNonceInvalidationPromiseFactory;
280
+ exports.createRecentSignatureConfirmationPromiseFactory = createRecentSignatureConfirmationPromiseFactory;
281
+ exports.getTimeoutPromise = getTimeoutPromise;
282
+ exports.waitForDurableNonceTransactionConfirmation = waitForDurableNonceTransactionConfirmation;
283
+ exports.waitForRecentTransactionConfirmation = waitForRecentTransactionConfirmation;
284
+ exports.waitForRecentTransactionConfirmationUntilTimeout = waitForRecentTransactionConfirmationUntilTimeout;
285
+ //# sourceMappingURL=index.browser.cjs.map
286
+ //# sourceMappingURL=index.browser.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/confirmation-strategy-blockheight.ts","../src/confirmation-strategy-nonce.ts","../src/confirmation-strategy-recent-signature.ts","../src/confirmation-strategy-timeout.ts","../src/confirmation-strategy-racer.ts","../src/waiters.ts"],"names":["SolanaError","SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED","getBase58Decoder","getBase64Encoder","SOLANA_ERROR__INVALID_NONCE","SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND","safeRace","getSolanaErrorFromTransactionError","commitmentComparator","getSignatureFromTransaction","getTimeoutPromise"],"mappings":"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","file":"index.browser.cjs","sourcesContent":["import { SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, SolanaError } from '@solana/errors';\nimport type { GetEpochInfoApi, Rpc } from '@solana/rpc';\nimport type { RpcSubscriptions, SlotNotificationsApi } from '@solana/rpc-subscriptions';\nimport type { Commitment } from '@solana/rpc-types';\n\ntype GetBlockHeightExceedencePromiseFn = (config: {\n abortSignal: AbortSignal;\n commitment?: Commitment;\n lastValidBlockHeight: bigint;\n}) => Promise<void>;\n\ntype CreateBlockHeightExceedencePromiseFactoryyConfig<TCluster> = {\n rpc: Rpc<GetEpochInfoApi> & { '~cluster'?: TCluster };\n rpcSubscriptions: RpcSubscriptions<SlotNotificationsApi> & { '~cluster'?: TCluster };\n};\n\nexport function createBlockHeightExceedencePromiseFactory({\n rpc,\n rpcSubscriptions,\n}: CreateBlockHeightExceedencePromiseFactoryyConfig<'devnet'>): GetBlockHeightExceedencePromiseFn;\nexport function createBlockHeightExceedencePromiseFactory({\n rpc,\n rpcSubscriptions,\n}: CreateBlockHeightExceedencePromiseFactoryyConfig<'testnet'>): GetBlockHeightExceedencePromiseFn;\nexport function createBlockHeightExceedencePromiseFactory({\n rpc,\n rpcSubscriptions,\n}: CreateBlockHeightExceedencePromiseFactoryyConfig<'mainnet'>): GetBlockHeightExceedencePromiseFn;\nexport function createBlockHeightExceedencePromiseFactory<\n TCluster extends 'devnet' | 'mainnet' | 'testnet' | void = void,\n>({\n rpc,\n rpcSubscriptions,\n}: CreateBlockHeightExceedencePromiseFactoryyConfig<TCluster>): GetBlockHeightExceedencePromiseFn {\n return async function getBlockHeightExceedencePromise({\n abortSignal: callerAbortSignal,\n commitment,\n lastValidBlockHeight,\n }) {\n const abortController = new AbortController();\n const handleAbort = () => {\n abortController.abort();\n };\n callerAbortSignal.addEventListener('abort', handleAbort, { signal: abortController.signal });\n async function getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight() {\n const { absoluteSlot, blockHeight } = await rpc\n .getEpochInfo({ commitment })\n .send({ abortSignal: abortController.signal });\n return {\n blockHeight,\n differenceBetweenSlotHeightAndBlockHeight: absoluteSlot - blockHeight,\n };\n }\n try {\n const [slotNotifications, { blockHeight: initialBlockHeight, differenceBetweenSlotHeightAndBlockHeight }] =\n await Promise.all([\n rpcSubscriptions.slotNotifications().subscribe({ abortSignal: abortController.signal }),\n getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight(),\n ]);\n let currentBlockHeight = initialBlockHeight;\n if (currentBlockHeight <= lastValidBlockHeight) {\n let lastKnownDifferenceBetweenSlotHeightAndBlockHeight = differenceBetweenSlotHeightAndBlockHeight;\n for await (const slotNotification of slotNotifications) {\n const { slot } = slotNotification;\n if (slot - lastKnownDifferenceBetweenSlotHeightAndBlockHeight > lastValidBlockHeight) {\n // Before making a final decision, recheck the actual block height.\n const {\n blockHeight: recheckedBlockHeight,\n differenceBetweenSlotHeightAndBlockHeight: currentDifferenceBetweenSlotHeightAndBlockHeight,\n } = await getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight();\n currentBlockHeight = recheckedBlockHeight;\n if (currentBlockHeight > lastValidBlockHeight) {\n // Verified; the block height has been exceeded.\n break;\n } else {\n // The block height has not been exceeded, which implies that the\n // difference between the slot height and the block height has grown\n // (ie. some blocks have been skipped since we started). Recalibrate the\n // difference and keep waiting.\n lastKnownDifferenceBetweenSlotHeightAndBlockHeight =\n currentDifferenceBetweenSlotHeightAndBlockHeight;\n }\n }\n }\n }\n throw new SolanaError(SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {\n currentBlockHeight,\n lastValidBlockHeight,\n });\n } finally {\n abortController.abort();\n }\n };\n}\n","import type { Address } from '@solana/addresses';\nimport { getBase58Decoder, getBase64Encoder } from '@solana/codecs-strings';\nimport { SOLANA_ERROR__INVALID_NONCE, SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND, SolanaError } from '@solana/errors';\nimport { safeRace } from '@solana/promises';\nimport type { GetAccountInfoApi, Rpc } from '@solana/rpc';\nimport type { AccountNotificationsApi, RpcSubscriptions } from '@solana/rpc-subscriptions';\nimport type { Base64EncodedDataResponse, Commitment } from '@solana/rpc-types';\nimport { Nonce } from '@solana/transaction-messages';\n\ntype GetNonceInvalidationPromiseFn = (config: {\n abortSignal: AbortSignal;\n commitment: Commitment;\n currentNonceValue: Nonce;\n nonceAccountAddress: Address;\n}) => Promise<void>;\n\ntype CreateNonceInvalidationPromiseFactoryConfig<TCluster> = {\n rpc: Rpc<GetAccountInfoApi> & { '~cluster'?: TCluster };\n rpcSubscriptions: RpcSubscriptions<AccountNotificationsApi> & { '~cluster'?: TCluster };\n};\n\nconst NONCE_VALUE_OFFSET =\n 4 + // version(u32)\n 4 + // state(u32)\n 32; // nonce authority(pubkey)\n// Then comes the nonce value.\n\nexport function createNonceInvalidationPromiseFactory({\n rpc,\n rpcSubscriptions,\n}: CreateNonceInvalidationPromiseFactoryConfig<'devnet'>): GetNonceInvalidationPromiseFn;\nexport function createNonceInvalidationPromiseFactory({\n rpc,\n rpcSubscriptions,\n}: CreateNonceInvalidationPromiseFactoryConfig<'testnet'>): GetNonceInvalidationPromiseFn;\nexport function createNonceInvalidationPromiseFactory({\n rpc,\n rpcSubscriptions,\n}: CreateNonceInvalidationPromiseFactoryConfig<'mainnet'>): GetNonceInvalidationPromiseFn;\nexport function createNonceInvalidationPromiseFactory<TCluster extends 'devnet' | 'mainnet' | 'testnet' | void = void>({\n rpc,\n rpcSubscriptions,\n}: CreateNonceInvalidationPromiseFactoryConfig<TCluster>): GetNonceInvalidationPromiseFn {\n return async function getNonceInvalidationPromise({\n abortSignal: callerAbortSignal,\n commitment,\n currentNonceValue: expectedNonceValue,\n nonceAccountAddress,\n }) {\n const abortController = new AbortController();\n function handleAbort() {\n abortController.abort();\n }\n callerAbortSignal.addEventListener('abort', handleAbort, { signal: abortController.signal });\n /**\n * STEP 1: Set up a subscription for nonce account changes.\n */\n const accountNotifications = await rpcSubscriptions\n .accountNotifications(nonceAccountAddress, { commitment, encoding: 'base64' })\n .subscribe({ abortSignal: abortController.signal });\n const base58Decoder = getBase58Decoder();\n const base64Encoder = getBase64Encoder();\n function getNonceFromAccountData([base64EncodedBytes]: Base64EncodedDataResponse): Nonce {\n const data = base64Encoder.encode(base64EncodedBytes);\n const nonceValueBytes = data.slice(NONCE_VALUE_OFFSET, NONCE_VALUE_OFFSET + 32);\n return base58Decoder.decode(nonceValueBytes) as Nonce;\n }\n const nonceAccountDidAdvancePromise = (async () => {\n for await (const accountNotification of accountNotifications) {\n const nonceValue = getNonceFromAccountData(accountNotification.value.data);\n if (nonceValue !== expectedNonceValue) {\n throw new SolanaError(SOLANA_ERROR__INVALID_NONCE, {\n actualNonceValue: nonceValue,\n expectedNonceValue,\n });\n }\n }\n })();\n /**\n * STEP 2: Having subscribed for updates, make a one-shot request for the current nonce\n * value to check if it has already been advanced.\n */\n const nonceIsAlreadyInvalidPromise = (async () => {\n const { value: nonceAccount } = await rpc\n .getAccountInfo(nonceAccountAddress, {\n commitment,\n dataSlice: { length: 32, offset: NONCE_VALUE_OFFSET },\n encoding: 'base58',\n })\n .send({ abortSignal: abortController.signal });\n if (!nonceAccount) {\n throw new SolanaError(SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND, {\n nonceAccountAddress,\n });\n }\n const nonceValue =\n // This works because we asked for the exact slice of data representing the nonce\n // value, and furthermore asked for it in `base58` encoding.\n nonceAccount.data[0] as unknown as Nonce;\n if (nonceValue !== expectedNonceValue) {\n throw new SolanaError(SOLANA_ERROR__INVALID_NONCE, {\n actualNonceValue: nonceValue,\n expectedNonceValue,\n });\n } else {\n await new Promise(() => {\n /* never resolve */\n });\n }\n })();\n try {\n return await safeRace([nonceAccountDidAdvancePromise, nonceIsAlreadyInvalidPromise]);\n } finally {\n abortController.abort();\n }\n };\n}\n","import { getSolanaErrorFromTransactionError } from '@solana/errors';\nimport type { Signature } from '@solana/keys';\nimport { safeRace } from '@solana/promises';\nimport type { GetSignatureStatusesApi, Rpc } from '@solana/rpc';\nimport type { RpcSubscriptions, SignatureNotificationsApi } from '@solana/rpc-subscriptions';\nimport { type Commitment, commitmentComparator } from '@solana/rpc-types';\n\ntype GetRecentSignatureConfirmationPromiseFn = (config: {\n abortSignal: AbortSignal;\n commitment: Commitment;\n signature: Signature;\n}) => Promise<void>;\n\ntype CreateRecentSignatureConfirmationPromiseFactoryConfig<TCluster> = {\n rpc: Rpc<GetSignatureStatusesApi> & { '~cluster'?: TCluster };\n rpcSubscriptions: RpcSubscriptions<SignatureNotificationsApi> & { '~cluster'?: TCluster };\n};\n\nexport function createRecentSignatureConfirmationPromiseFactory({\n rpc,\n rpcSubscriptions,\n}: CreateRecentSignatureConfirmationPromiseFactoryConfig<'devnet'>): GetRecentSignatureConfirmationPromiseFn;\nexport function createRecentSignatureConfirmationPromiseFactory({\n rpc,\n rpcSubscriptions,\n}: CreateRecentSignatureConfirmationPromiseFactoryConfig<'testnet'>): GetRecentSignatureConfirmationPromiseFn;\nexport function createRecentSignatureConfirmationPromiseFactory({\n rpc,\n rpcSubscriptions,\n}: CreateRecentSignatureConfirmationPromiseFactoryConfig<'mainnet'>): GetRecentSignatureConfirmationPromiseFn;\nexport function createRecentSignatureConfirmationPromiseFactory<\n TCluster extends 'devnet' | 'mainnet' | 'testnet' | void = void,\n>({\n rpc,\n rpcSubscriptions,\n}: CreateRecentSignatureConfirmationPromiseFactoryConfig<TCluster>): GetRecentSignatureConfirmationPromiseFn {\n return async function getRecentSignatureConfirmationPromise({\n abortSignal: callerAbortSignal,\n commitment,\n signature,\n }) {\n const abortController = new AbortController();\n function handleAbort() {\n abortController.abort();\n }\n callerAbortSignal.addEventListener('abort', handleAbort, { signal: abortController.signal });\n /**\n * STEP 1: Set up a subscription for status changes to a signature.\n */\n const signatureStatusNotifications = await rpcSubscriptions\n .signatureNotifications(signature, { commitment })\n .subscribe({ abortSignal: abortController.signal });\n const signatureDidCommitPromise = (async () => {\n for await (const signatureStatusNotification of signatureStatusNotifications) {\n if (signatureStatusNotification.value.err) {\n throw getSolanaErrorFromTransactionError(signatureStatusNotification.value.err);\n } else {\n return;\n }\n }\n })();\n /**\n * STEP 2: Having subscribed for updates, make a one-shot request for the current status.\n * This will only yield a result if the signature is still in the status cache.\n */\n const signatureStatusLookupPromise = (async () => {\n const { value: signatureStatusResults } = await rpc\n .getSignatureStatuses([signature])\n .send({ abortSignal: abortController.signal });\n const signatureStatus = signatureStatusResults[0];\n if (\n signatureStatus &&\n signatureStatus.confirmationStatus &&\n commitmentComparator(signatureStatus.confirmationStatus, commitment) >= 0\n ) {\n return;\n } else {\n await new Promise(() => {\n /* never resolve */\n });\n }\n })();\n try {\n return await safeRace([signatureDidCommitPromise, signatureStatusLookupPromise]);\n } finally {\n abortController.abort();\n }\n };\n}\n","import type { Commitment } from '@solana/rpc-types';\n\ntype Config = Readonly<{\n abortSignal: AbortSignal;\n commitment: Commitment;\n}>;\n\nexport async function getTimeoutPromise({ abortSignal: callerAbortSignal, commitment }: Config) {\n return await new Promise((_, reject) => {\n const handleAbort = (e: AbortSignalEventMap['abort']) => {\n clearTimeout(timeoutId);\n const abortError = new DOMException((e.target as AbortSignal).reason, 'AbortError');\n reject(abortError);\n };\n callerAbortSignal.addEventListener('abort', handleAbort);\n const timeoutMs = commitment === 'processed' ? 30_000 : 60_000;\n const startMs = performance.now();\n const timeoutId =\n // We use `setTimeout` instead of `AbortSignal.timeout()` because we want to measure\n // elapsed time instead of active time.\n // See https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static\n setTimeout(() => {\n const elapsedMs = performance.now() - startMs;\n reject(new DOMException(`Timeout elapsed after ${elapsedMs} ms`, 'TimeoutError'));\n }, timeoutMs);\n });\n}\n","import type { Signature } from '@solana/keys';\nimport { safeRace } from '@solana/promises';\nimport type { Commitment } from '@solana/rpc-types';\n\nimport { createRecentSignatureConfirmationPromiseFactory } from './confirmation-strategy-recent-signature';\n\nexport interface BaseTransactionConfirmationStrategyConfig {\n abortSignal?: AbortSignal;\n commitment: Commitment;\n getRecentSignatureConfirmationPromise: ReturnType<typeof createRecentSignatureConfirmationPromiseFactory>;\n}\n\ntype WithNonNullableAbortSignal<T> = Omit<T, 'abortSignal'> & Readonly<{ abortSignal: AbortSignal }>;\n\nexport async function raceStrategies<TConfig extends BaseTransactionConfirmationStrategyConfig>(\n signature: Signature,\n config: TConfig,\n getSpecificStrategiesForRace: (config: WithNonNullableAbortSignal<TConfig>) => readonly Promise<unknown>[],\n) {\n const { abortSignal: callerAbortSignal, commitment, getRecentSignatureConfirmationPromise } = config;\n callerAbortSignal?.throwIfAborted();\n const abortController = new AbortController();\n if (callerAbortSignal) {\n const handleAbort = () => {\n abortController.abort();\n };\n callerAbortSignal.addEventListener('abort', handleAbort, { signal: abortController.signal });\n }\n try {\n const specificStrategies = getSpecificStrategiesForRace({\n ...config,\n abortSignal: abortController.signal,\n });\n return await safeRace([\n getRecentSignatureConfirmationPromise({\n abortSignal: abortController.signal,\n commitment,\n signature,\n }),\n ...specificStrategies,\n ]);\n } finally {\n abortController.abort();\n }\n}\n","import { Signature } from '@solana/keys';\nimport { getSignatureFromTransaction, Transaction } from '@solana/transactions';\nimport {\n TransactionWithBlockhashLifetime,\n TransactionWithDurableNonceLifetime,\n} from '@solana/transactions/dist/types/lifetime';\n\nimport { createBlockHeightExceedencePromiseFactory } from './confirmation-strategy-blockheight';\nimport { createNonceInvalidationPromiseFactory } from './confirmation-strategy-nonce';\nimport { BaseTransactionConfirmationStrategyConfig, raceStrategies } from './confirmation-strategy-racer';\nimport { getTimeoutPromise } from './confirmation-strategy-timeout';\n\ninterface WaitForDurableNonceTransactionConfirmationConfig extends BaseTransactionConfirmationStrategyConfig {\n getNonceInvalidationPromise: ReturnType<typeof createNonceInvalidationPromiseFactory>;\n transaction: Readonly<Transaction & TransactionWithDurableNonceLifetime>;\n}\n\ninterface WaitForRecentTransactionWithBlockhashLifetimeConfirmationConfig\n extends BaseTransactionConfirmationStrategyConfig {\n getBlockHeightExceedencePromise: ReturnType<typeof createBlockHeightExceedencePromiseFactory>;\n transaction: Readonly<Transaction & TransactionWithBlockhashLifetime>;\n}\n\ninterface WaitForRecentTransactionWithTimeBasedLifetimeConfirmationConfig\n extends BaseTransactionConfirmationStrategyConfig {\n getTimeoutPromise: typeof getTimeoutPromise;\n signature: Signature;\n}\n\nexport async function waitForDurableNonceTransactionConfirmation(\n config: WaitForDurableNonceTransactionConfirmationConfig,\n): Promise<void> {\n await raceStrategies(\n getSignatureFromTransaction(config.transaction),\n config,\n function getSpecificStrategiesForRace({ abortSignal, commitment, getNonceInvalidationPromise, transaction }) {\n return [\n getNonceInvalidationPromise({\n abortSignal,\n commitment,\n currentNonceValue: transaction.lifetimeConstraint.nonce,\n nonceAccountAddress: transaction.lifetimeConstraint.nonceAccountAddress,\n }),\n ];\n },\n );\n}\n\nexport async function waitForRecentTransactionConfirmation(\n config: WaitForRecentTransactionWithBlockhashLifetimeConfirmationConfig,\n): Promise<void> {\n await raceStrategies(\n getSignatureFromTransaction(config.transaction),\n config,\n function getSpecificStrategiesForRace({\n abortSignal,\n commitment,\n getBlockHeightExceedencePromise,\n transaction,\n }) {\n return [\n getBlockHeightExceedencePromise({\n abortSignal,\n commitment,\n lastValidBlockHeight: transaction.lifetimeConstraint.lastValidBlockHeight,\n }),\n ];\n },\n );\n}\n\n/** @deprecated */\nexport async function waitForRecentTransactionConfirmationUntilTimeout(\n config: WaitForRecentTransactionWithTimeBasedLifetimeConfirmationConfig,\n): Promise<void> {\n await raceStrategies(\n config.signature,\n config,\n function getSpecificStrategiesForRace({ abortSignal, commitment, getTimeoutPromise }) {\n return [\n getTimeoutPromise({\n abortSignal,\n commitment,\n }),\n ];\n },\n );\n}\n"]}