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