@solana/transaction-confirmation 2.0.0-development.1532546

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,178 @@
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/experimental.svg?style=flat
10
+ [npm-image]: https://img.shields.io/npm/v/@solana/transaction-confirmation/experimental.svg?style=flat
11
+ [npm-url]: https://www.npmjs.com/package/@solana/transaction-confirmation/v/experimental
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 { getTimeoutPromise } from '@solana/transaction-confirmation';
106
+
107
+ try {
108
+ await Promise.race([getCustomTransactionConfirmationPromise(/* ... */), getTimeoutPromise({ commitment })]);
109
+ } catch (e) {
110
+ if (e instanceof DOMException && e.name === 'TimeoutError') {
111
+ console.log('Could not confirm transaction after a timeout');
112
+ }
113
+ throw e;
114
+ }
115
+ ```
116
+
117
+ ### `waitForDurableNonceTransactionConfirmation()`
118
+
119
+ Supply your own confirmation implementations to this function to create a custom nonce transaction confirmation strategy.
120
+
121
+ ```ts
122
+ import { waitForDurableNonceTransactionConfirmation } from '@solana/transaction-confirmation';
123
+
124
+ try {
125
+ await waitForDurableNonceTransactionConfirmation({
126
+ getNonceInvalidationPromise({ abortSignal, commitment, currentNonceValue, nonceAccountAddress }) {
127
+ // Return a promise that rejects when a nonce becomes invalid.
128
+ },
129
+ getRecentSignatureConfirmationPromise({ abortSignal, commitment, signature }) {
130
+ // Return a promise that resolves when a transaction achieves confirmation
131
+ },
132
+ });
133
+ } catch (e) {
134
+ // Handle errors.
135
+ }
136
+ ```
137
+
138
+ ### `waitForRecentTransactionConfirmation()`
139
+
140
+ Supply your own confirmation implementations to this function to create a custom nonce transaction confirmation strategy.
141
+
142
+ ```ts
143
+ import { waitForRecentTransactionConfirmation } from '@solana/transaction-confirmation';
144
+
145
+ try {
146
+ await waitForRecentTransactionConfirmation({
147
+ getBlockHeightExceedencePromise({ abortSignal, commitment, lastValidBlockHeight }) {
148
+ // Return a promise that rejects when the blockhash's block height has been exceeded
149
+ },
150
+ getRecentSignatureConfirmationPromise({ abortSignal, commitment, signature }) {
151
+ // Return a promise that resolves when a transaction achieves confirmation
152
+ },
153
+ });
154
+ } catch (e) {
155
+ // Handle errors.
156
+ }
157
+ ```
158
+
159
+ ### `waitForRecentTransactionConfirmationUntilTimeout()`
160
+
161
+ Supply your own confirmation implementations to this function to create a custom nonce transaction confirmation strategy.
162
+
163
+ ```ts
164
+ import { waitForRecentTransactionConfirmationUntilTimeout } from '@solana/transaction-confirmation';
165
+
166
+ try {
167
+ await waitForRecentTransactionConfirmationUntilTimeout({
168
+ getTimeoutPromise({ abortSignal, commitment }) {
169
+ // Return a promise that rejects after your chosen timeout
170
+ },
171
+ getRecentSignatureConfirmationPromise({ abortSignal, commitment, signature }) {
172
+ // Return a promise that resolves when a transaction achieves confirmation
173
+ },
174
+ });
175
+ } catch (e) {
176
+ // Handle errors.
177
+ }
178
+ ```
@@ -0,0 +1,283 @@
1
+ 'use strict';
2
+
3
+ var errors = require('@solana/errors');
4
+ var codecsStrings = require('@solana/codecs-strings');
5
+ var rpcTypes = require('@solana/rpc-types');
6
+ var transactions = require('@solana/transactions');
7
+
8
+ // src/confirmation-strategy-blockheight.ts
9
+ function createBlockHeightExceedencePromiseFactory({
10
+ rpc,
11
+ rpcSubscriptions
12
+ }) {
13
+ return async function getBlockHeightExceedencePromise({
14
+ abortSignal: callerAbortSignal,
15
+ commitment,
16
+ lastValidBlockHeight
17
+ }) {
18
+ const abortController = new AbortController();
19
+ const handleAbort = () => {
20
+ abortController.abort();
21
+ };
22
+ callerAbortSignal.addEventListener("abort", handleAbort, { signal: abortController.signal });
23
+ async function getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight() {
24
+ const { absoluteSlot, blockHeight } = await rpc.getEpochInfo({ commitment }).send({ abortSignal: abortController.signal });
25
+ return {
26
+ blockHeight,
27
+ differenceBetweenSlotHeightAndBlockHeight: absoluteSlot - blockHeight
28
+ };
29
+ }
30
+ try {
31
+ const [slotNotifications, { blockHeight: initialBlockHeight, differenceBetweenSlotHeightAndBlockHeight }] = await Promise.all([
32
+ rpcSubscriptions.slotNotifications().subscribe({ abortSignal: abortController.signal }),
33
+ getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight()
34
+ ]);
35
+ let currentBlockHeight = initialBlockHeight;
36
+ if (currentBlockHeight <= lastValidBlockHeight) {
37
+ let lastKnownDifferenceBetweenSlotHeightAndBlockHeight = differenceBetweenSlotHeightAndBlockHeight;
38
+ for await (const slotNotification of slotNotifications) {
39
+ const { slot } = slotNotification;
40
+ if (slot - lastKnownDifferenceBetweenSlotHeightAndBlockHeight > lastValidBlockHeight) {
41
+ const {
42
+ blockHeight: recheckedBlockHeight,
43
+ differenceBetweenSlotHeightAndBlockHeight: currentDifferenceBetweenSlotHeightAndBlockHeight
44
+ } = await getBlockHeightAndDifferenceBetweenSlotHeightAndBlockHeight();
45
+ currentBlockHeight = recheckedBlockHeight;
46
+ if (currentBlockHeight > lastValidBlockHeight) {
47
+ break;
48
+ } else {
49
+ lastKnownDifferenceBetweenSlotHeightAndBlockHeight = currentDifferenceBetweenSlotHeightAndBlockHeight;
50
+ }
51
+ }
52
+ }
53
+ }
54
+ throw new errors.SolanaError(errors.SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, {
55
+ currentBlockHeight,
56
+ lastValidBlockHeight
57
+ });
58
+ } finally {
59
+ abortController.abort();
60
+ }
61
+ };
62
+ }
63
+ var NONCE_VALUE_OFFSET = 4 + // version(u32)
64
+ 4 + // state(u32)
65
+ 32;
66
+ function createNonceInvalidationPromiseFactory(rpc, rpcSubscriptions) {
67
+ return async function getNonceInvalidationPromise({
68
+ abortSignal: callerAbortSignal,
69
+ commitment,
70
+ currentNonceValue: expectedNonceValue,
71
+ nonceAccountAddress
72
+ }) {
73
+ const abortController = new AbortController();
74
+ function handleAbort() {
75
+ abortController.abort();
76
+ }
77
+ callerAbortSignal.addEventListener("abort", handleAbort, { signal: abortController.signal });
78
+ const accountNotifications = await rpcSubscriptions.accountNotifications(nonceAccountAddress, { commitment, encoding: "base64" }).subscribe({ abortSignal: abortController.signal });
79
+ const base58Decoder = codecsStrings.getBase58Decoder();
80
+ const base64Encoder = codecsStrings.getBase64Encoder();
81
+ function getNonceFromAccountData([base64EncodedBytes]) {
82
+ const data = base64Encoder.encode(base64EncodedBytes);
83
+ const nonceValueBytes = data.slice(NONCE_VALUE_OFFSET, NONCE_VALUE_OFFSET + 32);
84
+ return base58Decoder.decode(nonceValueBytes);
85
+ }
86
+ const nonceAccountDidAdvancePromise = (async () => {
87
+ for await (const accountNotification of accountNotifications) {
88
+ const nonceValue = getNonceFromAccountData(accountNotification.value.data);
89
+ if (nonceValue !== expectedNonceValue) {
90
+ throw new errors.SolanaError(errors.SOLANA_ERROR__NONCE_INVALID, {
91
+ actualNonceValue: nonceValue,
92
+ expectedNonceValue
93
+ });
94
+ }
95
+ }
96
+ })();
97
+ const nonceIsAlreadyInvalidPromise = (async () => {
98
+ const { value: nonceAccount } = await rpc.getAccountInfo(nonceAccountAddress, {
99
+ commitment,
100
+ dataSlice: { length: 32, offset: NONCE_VALUE_OFFSET },
101
+ encoding: "base58"
102
+ }).send({ abortSignal: abortController.signal });
103
+ if (!nonceAccount) {
104
+ throw new errors.SolanaError(errors.SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND, {
105
+ nonceAccountAddress
106
+ });
107
+ }
108
+ const nonceValue = (
109
+ // This works because we asked for the exact slice of data representing the nonce
110
+ // value, and furthermore asked for it in `base58` encoding.
111
+ nonceAccount.data[0]
112
+ );
113
+ if (nonceValue !== expectedNonceValue) {
114
+ throw new errors.SolanaError(errors.SOLANA_ERROR__NONCE_INVALID, {
115
+ actualNonceValue: nonceValue,
116
+ expectedNonceValue
117
+ });
118
+ } else {
119
+ await new Promise(() => {
120
+ });
121
+ }
122
+ })();
123
+ try {
124
+ return await Promise.race([nonceAccountDidAdvancePromise, nonceIsAlreadyInvalidPromise]);
125
+ } finally {
126
+ abortController.abort();
127
+ }
128
+ };
129
+ }
130
+ function createRecentSignatureConfirmationPromiseFactory(rpc, rpcSubscriptions) {
131
+ return async function getRecentSignatureConfirmationPromise({
132
+ abortSignal: callerAbortSignal,
133
+ commitment,
134
+ signature
135
+ }) {
136
+ const abortController = new AbortController();
137
+ function handleAbort() {
138
+ abortController.abort();
139
+ }
140
+ callerAbortSignal.addEventListener("abort", handleAbort, { signal: abortController.signal });
141
+ const signatureStatusNotifications = await rpcSubscriptions.signatureNotifications(signature, { commitment }).subscribe({ abortSignal: abortController.signal });
142
+ const signatureDidCommitPromise = (async () => {
143
+ for await (const signatureStatusNotification of signatureStatusNotifications) {
144
+ if (signatureStatusNotification.value.err) {
145
+ throw new Error(`The transaction with signature \`${signature}\` failed.`, {
146
+ cause: signatureStatusNotification.value.err
147
+ });
148
+ } else {
149
+ return;
150
+ }
151
+ }
152
+ })();
153
+ const signatureStatusLookupPromise = (async () => {
154
+ const { value: signatureStatusResults } = await rpc.getSignatureStatuses([signature]).send({ abortSignal: abortController.signal });
155
+ const signatureStatus = signatureStatusResults[0];
156
+ if (signatureStatus && signatureStatus.confirmationStatus && rpcTypes.commitmentComparator(signatureStatus.confirmationStatus, commitment) >= 0) {
157
+ return;
158
+ } else {
159
+ await new Promise(() => {
160
+ });
161
+ }
162
+ })();
163
+ try {
164
+ return await Promise.race([signatureDidCommitPromise, signatureStatusLookupPromise]);
165
+ } finally {
166
+ abortController.abort();
167
+ }
168
+ };
169
+ }
170
+
171
+ // src/confirmation-strategy-timeout.ts
172
+ async function getTimeoutPromise({ abortSignal: callerAbortSignal, commitment }) {
173
+ return await new Promise((_, reject) => {
174
+ const handleAbort = (e) => {
175
+ clearTimeout(timeoutId);
176
+ const abortError = new DOMException(e.target.reason, "AbortError");
177
+ reject(abortError);
178
+ };
179
+ callerAbortSignal.addEventListener("abort", handleAbort);
180
+ const timeoutMs = commitment === "processed" ? 3e4 : 6e4;
181
+ const startMs = performance.now();
182
+ const timeoutId = (
183
+ // We use `setTimeout` instead of `AbortSignal.timeout()` because we want to measure
184
+ // elapsed time instead of active time.
185
+ // See https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static
186
+ setTimeout(() => {
187
+ const elapsedMs = performance.now() - startMs;
188
+ reject(new DOMException(`Timeout elapsed after ${elapsedMs} ms`, "TimeoutError"));
189
+ }, timeoutMs)
190
+ );
191
+ });
192
+ }
193
+
194
+ // src/confirmation-strategy-racer.ts
195
+ async function raceStrategies(signature, config, getSpecificStrategiesForRace) {
196
+ const { abortSignal: callerAbortSignal, commitment, getRecentSignatureConfirmationPromise } = config;
197
+ callerAbortSignal?.throwIfAborted();
198
+ const abortController = new AbortController();
199
+ if (callerAbortSignal) {
200
+ const handleAbort = () => {
201
+ abortController.abort();
202
+ };
203
+ callerAbortSignal.addEventListener("abort", handleAbort, { signal: abortController.signal });
204
+ }
205
+ try {
206
+ const specificStrategies = getSpecificStrategiesForRace({
207
+ ...config,
208
+ abortSignal: abortController.signal
209
+ });
210
+ return await Promise.race([
211
+ getRecentSignatureConfirmationPromise({
212
+ abortSignal: abortController.signal,
213
+ commitment,
214
+ signature
215
+ }),
216
+ ...specificStrategies
217
+ ]);
218
+ } finally {
219
+ abortController.abort();
220
+ }
221
+ }
222
+
223
+ // src/waiters.ts
224
+ async function waitForDurableNonceTransactionConfirmation(config) {
225
+ await raceStrategies(
226
+ transactions.getSignatureFromTransaction(config.transaction),
227
+ config,
228
+ function getSpecificStrategiesForRace({ abortSignal, commitment, getNonceInvalidationPromise, transaction }) {
229
+ return [
230
+ getNonceInvalidationPromise({
231
+ abortSignal,
232
+ commitment,
233
+ currentNonceValue: transaction.lifetimeConstraint.nonce,
234
+ nonceAccountAddress: transaction.instructions[0].accounts[0].address
235
+ })
236
+ ];
237
+ }
238
+ );
239
+ }
240
+ async function waitForRecentTransactionConfirmation(config) {
241
+ await raceStrategies(
242
+ transactions.getSignatureFromTransaction(config.transaction),
243
+ config,
244
+ function getSpecificStrategiesForRace({
245
+ abortSignal,
246
+ commitment,
247
+ getBlockHeightExceedencePromise,
248
+ transaction
249
+ }) {
250
+ return [
251
+ getBlockHeightExceedencePromise({
252
+ abortSignal,
253
+ commitment,
254
+ lastValidBlockHeight: transaction.lifetimeConstraint.lastValidBlockHeight
255
+ })
256
+ ];
257
+ }
258
+ );
259
+ }
260
+ async function waitForRecentTransactionConfirmationUntilTimeout(config) {
261
+ await raceStrategies(
262
+ config.signature,
263
+ config,
264
+ function getSpecificStrategiesForRace({ abortSignal, commitment, getTimeoutPromise: getTimeoutPromise2 }) {
265
+ return [
266
+ getTimeoutPromise2({
267
+ abortSignal,
268
+ commitment
269
+ })
270
+ ];
271
+ }
272
+ );
273
+ }
274
+
275
+ exports.createBlockHeightExceedencePromiseFactory = createBlockHeightExceedencePromiseFactory;
276
+ exports.createNonceInvalidationPromiseFactory = createNonceInvalidationPromiseFactory;
277
+ exports.createRecentSignatureConfirmationPromiseFactory = createRecentSignatureConfirmationPromiseFactory;
278
+ exports.getTimeoutPromise = getTimeoutPromise;
279
+ exports.waitForDurableNonceTransactionConfirmation = waitForDurableNonceTransactionConfirmation;
280
+ exports.waitForRecentTransactionConfirmation = waitForRecentTransactionConfirmation;
281
+ exports.waitForRecentTransactionConfirmationUntilTimeout = waitForRecentTransactionConfirmationUntilTimeout;
282
+ //# sourceMappingURL=out.js.map
283
+ //# 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/waiters.ts","../src/confirmation-strategy-racer.ts"],"names":["SolanaError","getTimeoutPromise"],"mappings":"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","sourcesContent":["import { SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED, SolanaError } from '@solana/errors';\nimport type { GetEpochInfoApi, SlotNotificationsApi } from '@solana/rpc-core';\nimport type { Rpc, RpcSubscriptions } from '@solana/rpc-types';\nimport { Commitment } from '@solana/rpc-types';\n\ntype GetBlockHeightExceedencePromiseFn = (config: {\n abortSignal: AbortSignal;\n commitment?: Commitment;\n lastValidBlockHeight: bigint;\n}) => Promise<void>;\n\nexport function createBlockHeightExceedencePromiseFactory({\n rpc,\n rpcSubscriptions,\n}: Readonly<{\n rpc: Rpc<GetEpochInfoApi>;\n rpcSubscriptions: RpcSubscriptions<SlotNotificationsApi>;\n}>): 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 // Verfied; 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 { Address } from '@solana/addresses';\nimport { getBase58Decoder, getBase64Encoder } from '@solana/codecs-strings';\nimport { SOLANA_ERROR__NONCE_ACCOUNT_NOT_FOUND, SOLANA_ERROR__NONCE_INVALID, SolanaError } from '@solana/errors';\nimport type { AccountNotificationsApi, GetAccountInfoApi } from '@solana/rpc-core';\nimport type { Base64EncodedDataResponse, Commitment, Rpc, RpcSubscriptions } from '@solana/rpc-types';\nimport { Nonce } from '@solana/transactions';\n\ntype GetNonceInvalidationPromiseFn = (config: {\n abortSignal: AbortSignal;\n commitment: Commitment;\n currentNonceValue: Nonce;\n nonceAccountAddress: Address;\n}) => Promise<void>;\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: Rpc<GetAccountInfoApi>,\n rpcSubscriptions: RpcSubscriptions<AccountNotificationsApi>,\n): 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__NONCE_INVALID, {\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__NONCE_INVALID, {\n actualNonceValue: nonceValue,\n expectedNonceValue,\n });\n } else {\n await new Promise(() => {\n /* never resolve */\n });\n }\n })();\n try {\n return await Promise.race([nonceAccountDidAdvancePromise, nonceIsAlreadyInvalidPromise]);\n } finally {\n abortController.abort();\n }\n };\n}\n","import { Signature } from '@solana/keys';\nimport type { GetSignatureStatusesApi, SignatureNotificationsApi } from '@solana/rpc-core';\nimport { type Commitment, commitmentComparator, type Rpc, type RpcSubscriptions } from '@solana/rpc-types';\n\ntype GetRecentSignatureConfirmationPromiseFn = (config: {\n abortSignal: AbortSignal;\n commitment: Commitment;\n signature: Signature;\n}) => Promise<void>;\n\nexport function createRecentSignatureConfirmationPromiseFactory(\n rpc: Rpc<GetSignatureStatusesApi>,\n rpcSubscriptions: RpcSubscriptions<SignatureNotificationsApi>,\n): 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 // TODO: Coded error.\n throw new Error(`The transaction with signature \\`${signature}\\` failed.`, {\n cause: signatureStatusNotification.value.err,\n });\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 Promise.race([signatureDidCommitPromise, signatureStatusLookupPromise]);\n } finally {\n abortController.abort();\n }\n };\n}\n","import { 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 { Signature } from '@solana/keys';\nimport type { Slot } from '@solana/rpc-types';\nimport {\n getSignatureFromTransaction,\n IDurableNonceTransaction,\n ITransactionWithFeePayer,\n ITransactionWithSignatures,\n} from '@solana/transactions';\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: ITransactionWithFeePayer & ITransactionWithSignatures & IDurableNonceTransaction;\n}\n\ninterface WaitForRecentTransactionWithBlockhashLifetimeConfirmationConfig\n extends BaseTransactionConfirmationStrategyConfig {\n getBlockHeightExceedencePromise: ReturnType<typeof createBlockHeightExceedencePromiseFactory>;\n transaction: ITransactionWithFeePayer &\n ITransactionWithSignatures &\n Readonly<{\n lifetimeConstraint: {\n lastValidBlockHeight: Slot;\n };\n }>;\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.instructions[0].accounts[0].address,\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","import { Signature } from '@solana/keys';\nimport { 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 Promise.race([\n getRecentSignatureConfirmationPromise({\n abortSignal: abortController.signal,\n commitment,\n signature,\n }),\n ...specificStrategies,\n ]);\n } finally {\n abortController.abort();\n }\n}\n"]}