@augustdigital/sdk 9.9.0 → 9.10.1

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,88 @@
1
+ import { AnchorProvider } from '@coral-xyz/anchor';
2
+ import { type Commitment, type ConfirmOptions, type Connection, type Signer, type Transaction, type TransactionSignature, type VersionedTransaction } from '@solana/web3.js';
3
+ /** Delay between signature-status polls. */
4
+ export declare const CONFIRM_POLL_INTERVAL_MS = 2000;
5
+ /**
6
+ * Wall-clock backstop, also bounding every in-flight RPC read (web3.js sets no
7
+ * request timeout). Expiry is normally seen first: a blockhash lasts ~150
8
+ * blocks (~60s, longer when slots are skipped), and the finalized height it is checked against lags ~13s.
9
+ */
10
+ export declare const CONFIRM_TIMEOUT_MS = 120000;
11
+ /** Cap on a single RPC read, so a hung request counts as a failed read rather than eating the backstop. */
12
+ export declare const CONFIRM_READ_TIMEOUT_MS = 10000;
13
+ /** Ceiling for the doubling wait after polls whose reads failed. */
14
+ export declare const CONFIRM_MAX_BACKOFF_MS = 16000;
15
+ /**
16
+ * Wait for a sent transaction to reach `commitment`, over HTTP only.
17
+ *
18
+ * Anchor's `.rpc()` confirms by signature alone: a WebSocket
19
+ * `signatureSubscribe` raced against a fixed 30s (`confirmed`) or 60s
20
+ * (`finalized`) timer, so a transaction that landed is reported as failed
21
+ * whenever the notification is missed. This polls `getSignatureStatuses`, and
22
+ * ends on the blockhash's real expiry rather than a timer.
23
+ *
24
+ * It never reports that resubmitting is safe. An expired transaction that no
25
+ * lookup can find very likely never landed, but an RPC node with incomplete
26
+ * history cannot prove it, and a wrong "safe" duplicates a deposit or redeem.
27
+ *
28
+ * @param connection - Connection the transaction was sent through.
29
+ * @param signature - Signature returned by `sendRawTransaction`.
30
+ * @param lastValidBlockHeight - Expiry of the blockhash the signed
31
+ * transaction carries, checked against the finalized block height. Omit
32
+ * when unknown (or for a durable-nonce transaction): the wait then ends only
33
+ * on confirmation, failure or `timeoutMs`.
34
+ * @param commitment - Level the transaction must reach.
35
+ * @param pollIntervalMs - Delay between polls; must be positive. Defaults to {@link CONFIRM_POLL_INTERVAL_MS}.
36
+ * @param timeoutMs - Wall-clock backstop; must be positive. Defaults to {@link CONFIRM_TIMEOUT_MS}.
37
+ * @param readTimeoutMs - Cap on each RPC read; must be positive. Defaults to {@link CONFIRM_READ_TIMEOUT_MS}.
38
+ * @returns `signature`, once its status reaches `commitment` with no error.
39
+ * @throws {@link SendTransactionError} when the transaction failed at
40
+ * `commitment`, with logs attached for Anchor's `translateError`.
41
+ * @throws {@link AugustTransactionError} `TRANSACTION_UNCONFIRMED` when no
42
+ * outcome was established: the blockhash expired with the transaction
43
+ * nowhere to be found, or `timeoutMs` passed. `context.reason` is
44
+ * `expired_not_found` (very likely never landed — still check before
45
+ * resubmitting), `below_commitment` (seen landing without error),
46
+ * `failed_below_commitment` (seen failing, not yet final), `rpc_failing`
47
+ * (reads failed) or `not_visible`; `context` also carries the last status
48
+ * seen and read counts, and `cause` the last failed read.
49
+ * @throws {@link AugustValidationError} for a non-positive or non-finite interval or timeout.
50
+ * @remarks **RPC cost.** The first poll runs right after sending. Per poll: one `getSignatureStatuses`, plus one
51
+ * finalized `getBlockHeight` while the transaction is not visible (and, past
52
+ * expiry, one history lookup). Typically 1–3 calls at `confirmed`, ~7–10 at
53
+ * `finalized`, ~60 for an expiring transaction; the `timeoutMs` backstop
54
+ * allows ~60 polls of 1–3 calls, fewer while reads fail: the wait doubles
55
+ * after each poll with a failed read, up to {@link CONFIRM_MAX_BACKOFF_MS}. Failed reads are tolerated, so a transient RPC
56
+ * error never misreports a landed transaction.
57
+ */
58
+ export declare function confirmByBlockhash({ connection, signature, lastValidBlockHeight, commitment, pollIntervalMs, timeoutMs, readTimeoutMs, }: {
59
+ connection: Connection;
60
+ signature: TransactionSignature;
61
+ lastValidBlockHeight?: number;
62
+ commitment: Commitment;
63
+ pollIntervalMs?: number;
64
+ timeoutMs?: number;
65
+ readTimeoutMs?: number;
66
+ }): Promise<TransactionSignature>;
67
+ /**
68
+ * `AnchorProvider` whose `sendAndConfirm` — what `program.methods…rpc()` calls —
69
+ * confirms with {@link confirmByBlockhash} instead of Anchor's signature-only
70
+ * WebSocket wait. Signing and Anchor's error translation are unchanged.
71
+ */
72
+ export declare class BlockhashConfirmingProvider extends AnchorProvider {
73
+ /**
74
+ * Sign, send and confirm a transaction.
75
+ *
76
+ * @param tx - Transaction to send. Its blockhash (and fee payer, if unset)
77
+ * are set here. A `VersionedTransaction` already carries a blockhash this
78
+ * cannot learn the expiry of, so it takes Anchor's default path.
79
+ * @param signers - Extra signers besides the wallet.
80
+ * @param opts - Defaults to the provider's options.
81
+ * @returns The transaction signature.
82
+ * @throws See {@link confirmByBlockhash}; also whatever the wallet or
83
+ * `sendRawTransaction` throws (user rejection, preflight failure).
84
+ * @remarks **RPC cost.** `getLatestBlockhash` + `sendRawTransaction`, then
85
+ * the confirmation polls described on {@link confirmByBlockhash}.
86
+ */
87
+ sendAndConfirm(tx: Transaction | VersionedTransaction, signers?: Signer[], opts?: ConfirmOptions): Promise<TransactionSignature>;
88
+ }
@@ -0,0 +1,291 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BlockhashConfirmingProvider = exports.CONFIRM_MAX_BACKOFF_MS = exports.CONFIRM_READ_TIMEOUT_MS = exports.CONFIRM_TIMEOUT_MS = exports.CONFIRM_POLL_INTERVAL_MS = void 0;
4
+ exports.confirmByBlockhash = confirmByBlockhash;
5
+ const anchor_1 = require("@coral-xyz/anchor");
6
+ const web3_js_1 = require("@solana/web3.js");
7
+ const core_1 = require("../../core");
8
+ const concurrency_1 = require("../../core/helpers/concurrency");
9
+ /** Delay between signature-status polls. */
10
+ exports.CONFIRM_POLL_INTERVAL_MS = 2_000;
11
+ /**
12
+ * Wall-clock backstop, also bounding every in-flight RPC read (web3.js sets no
13
+ * request timeout). Expiry is normally seen first: a blockhash lasts ~150
14
+ * blocks (~60s, longer when slots are skipped), and the finalized height it is checked against lags ~13s.
15
+ */
16
+ exports.CONFIRM_TIMEOUT_MS = 120_000;
17
+ /** Cap on a single RPC read, so a hung request counts as a failed read rather than eating the backstop. */
18
+ exports.CONFIRM_READ_TIMEOUT_MS = 10_000;
19
+ /** Ceiling for the doubling wait after polls whose reads failed. */
20
+ exports.CONFIRM_MAX_BACKOFF_MS = 16_000;
21
+ const COMMITMENT_RANK = {
22
+ processed: 0,
23
+ recent: 0,
24
+ confirmed: 1,
25
+ single: 1,
26
+ singleGossip: 1,
27
+ finalized: 2,
28
+ max: 2,
29
+ root: 2,
30
+ };
31
+ /**
32
+ * The level a status has reached. Older RPCs omit `confirmationStatus`; the
33
+ * level is then inferred from `confirmations` as Agave's
34
+ * `TransactionStatus::satisfies_commitment` does: `null` is rooted, more than
35
+ * one confirmation is `confirmed`, anything else `processed`.
36
+ */
37
+ function levelOf(status) {
38
+ if (status.confirmationStatus)
39
+ return status.confirmationStatus;
40
+ if (status.confirmations === null)
41
+ return 'finalized';
42
+ return status.confirmations > 1 ? 'confirmed' : 'processed';
43
+ }
44
+ function reaches(status, commitment) {
45
+ return COMMITMENT_RANK[levelOf(status)] >= COMMITMENT_RANK[commitment];
46
+ }
47
+ /**
48
+ * Wait for a sent transaction to reach `commitment`, over HTTP only.
49
+ *
50
+ * Anchor's `.rpc()` confirms by signature alone: a WebSocket
51
+ * `signatureSubscribe` raced against a fixed 30s (`confirmed`) or 60s
52
+ * (`finalized`) timer, so a transaction that landed is reported as failed
53
+ * whenever the notification is missed. This polls `getSignatureStatuses`, and
54
+ * ends on the blockhash's real expiry rather than a timer.
55
+ *
56
+ * It never reports that resubmitting is safe. An expired transaction that no
57
+ * lookup can find very likely never landed, but an RPC node with incomplete
58
+ * history cannot prove it, and a wrong "safe" duplicates a deposit or redeem.
59
+ *
60
+ * @param connection - Connection the transaction was sent through.
61
+ * @param signature - Signature returned by `sendRawTransaction`.
62
+ * @param lastValidBlockHeight - Expiry of the blockhash the signed
63
+ * transaction carries, checked against the finalized block height. Omit
64
+ * when unknown (or for a durable-nonce transaction): the wait then ends only
65
+ * on confirmation, failure or `timeoutMs`.
66
+ * @param commitment - Level the transaction must reach.
67
+ * @param pollIntervalMs - Delay between polls; must be positive. Defaults to {@link CONFIRM_POLL_INTERVAL_MS}.
68
+ * @param timeoutMs - Wall-clock backstop; must be positive. Defaults to {@link CONFIRM_TIMEOUT_MS}.
69
+ * @param readTimeoutMs - Cap on each RPC read; must be positive. Defaults to {@link CONFIRM_READ_TIMEOUT_MS}.
70
+ * @returns `signature`, once its status reaches `commitment` with no error.
71
+ * @throws {@link SendTransactionError} when the transaction failed at
72
+ * `commitment`, with logs attached for Anchor's `translateError`.
73
+ * @throws {@link AugustTransactionError} `TRANSACTION_UNCONFIRMED` when no
74
+ * outcome was established: the blockhash expired with the transaction
75
+ * nowhere to be found, or `timeoutMs` passed. `context.reason` is
76
+ * `expired_not_found` (very likely never landed — still check before
77
+ * resubmitting), `below_commitment` (seen landing without error),
78
+ * `failed_below_commitment` (seen failing, not yet final), `rpc_failing`
79
+ * (reads failed) or `not_visible`; `context` also carries the last status
80
+ * seen and read counts, and `cause` the last failed read.
81
+ * @throws {@link AugustValidationError} for a non-positive or non-finite interval or timeout.
82
+ * @remarks **RPC cost.** The first poll runs right after sending. Per poll: one `getSignatureStatuses`, plus one
83
+ * finalized `getBlockHeight` while the transaction is not visible (and, past
84
+ * expiry, one history lookup). Typically 1–3 calls at `confirmed`, ~7–10 at
85
+ * `finalized`, ~60 for an expiring transaction; the `timeoutMs` backstop
86
+ * allows ~60 polls of 1–3 calls, fewer while reads fail: the wait doubles
87
+ * after each poll with a failed read, up to {@link CONFIRM_MAX_BACKOFF_MS}. Failed reads are tolerated, so a transient RPC
88
+ * error never misreports a landed transaction.
89
+ */
90
+ async function confirmByBlockhash({ connection, signature, lastValidBlockHeight, commitment, pollIntervalMs = exports.CONFIRM_POLL_INTERVAL_MS, timeoutMs = exports.CONFIRM_TIMEOUT_MS, readTimeoutMs = exports.CONFIRM_READ_TIMEOUT_MS, }) {
91
+ for (const [name, value] of [
92
+ ['pollIntervalMs', pollIntervalMs],
93
+ ['timeoutMs', timeoutMs],
94
+ ['readTimeoutMs', readTimeoutMs],
95
+ ]) {
96
+ if (!Number.isFinite(value) || value <= 0)
97
+ throw new core_1.AugustValidationError('INVALID_INPUT', `confirmByBlockhash: ${name} must be a positive finite number, got ${value}.`);
98
+ }
99
+ const deadline = Date.now() + timeoutMs;
100
+ let reads = 0;
101
+ let failedReads = 0;
102
+ let lastReadError;
103
+ let lastSeen;
104
+ /** Finalized block height at which the transaction was found expired and absent. */
105
+ let expiredAt;
106
+ /** One RPC read; `undefined` when it failed or hung (outcome unknown). */
107
+ const read = async (call) => {
108
+ reads += 1;
109
+ const remaining = deadline - Date.now();
110
+ // Records whether the RPC itself rejected, so a timeout is told apart from
111
+ // a failure by which promise lost, not by reading the clock.
112
+ let rpcFailure;
113
+ const pending = call().catch((error) => {
114
+ rpcFailure = { error };
115
+ throw error;
116
+ });
117
+ try {
118
+ return await (0, concurrency_1.withTimeout)(pending, Math.min(readTimeoutMs, remaining), 'Solana RPC read');
119
+ }
120
+ catch (timeout) {
121
+ // The timer was the backstop's, not the per-read cap: not an RPC failure.
122
+ if (!rpcFailure && remaining < readTimeoutMs)
123
+ return undefined;
124
+ failedReads += 1;
125
+ lastReadError = rpcFailure ? rpcFailure.error : timeout;
126
+ return undefined;
127
+ }
128
+ };
129
+ const statusOf = (searchTransactionHistory = false) => read(() => connection
130
+ .getSignatureStatuses([signature], { searchTransactionHistory })
131
+ .then((r) => r.value[0]));
132
+ /**
133
+ * `true` once `status` is final at `commitment`; throws if it failed there.
134
+ * A failure seen below `commitment` is not final yet, as in Anchor.
135
+ */
136
+ const settled = async (status) => {
137
+ lastSeen = status;
138
+ if (!reaches(status, commitment))
139
+ return false;
140
+ if (!status.err)
141
+ return true;
142
+ const tx = await read(() => connection.getTransaction(signature, {
143
+ commitment: 'confirmed',
144
+ maxSupportedTransactionVersion: 0,
145
+ }));
146
+ // Keep `JSON.stringify(status.err)` in the message: without logs, Anchor's
147
+ // `ProgramError.parse` recovers the program error code from its
148
+ // `"Custom":N}`.
149
+ throw new web3_js_1.SendTransactionError({
150
+ action: 'send',
151
+ signature,
152
+ transactionMessage: `Transaction failed: ${JSON.stringify(status.err)}`,
153
+ logs: tx?.meta?.logMessages ?? undefined,
154
+ });
155
+ };
156
+ /** One poll: `true` once confirmed; throws on a definitive failure. */
157
+ const pollOnce = async () => {
158
+ // `undefined` = read failed (unknown); `null` = not visible to the RPC.
159
+ const status = await statusOf();
160
+ if (status)
161
+ return settled(status);
162
+ // Seen before but gone now (dropped fork): no longer evidence it landed.
163
+ if (status === null)
164
+ lastSeen = undefined;
165
+ if (status === undefined || lastValidBlockHeight === undefined)
166
+ return false;
167
+ // Finalized, whatever `commitment` is: a `processed`/`confirmed` height can
168
+ // belong to a fork that is later dropped while the transaction still lands
169
+ // on the surviving one. Past the finalized height, every fork is expired.
170
+ const height = await read(() => connection.getBlockHeight('finalized'));
171
+ if (height === undefined || height <= lastValidBlockHeight)
172
+ return false;
173
+ // Past expiry. Last look, including history, in case it landed between polls.
174
+ const final = await statusOf(true);
175
+ if (final)
176
+ return settled(final);
177
+ if (final === null)
178
+ expiredAt = height;
179
+ return false;
180
+ };
181
+ // Poll first: a transaction often lands within a second of sending. After a
182
+ // poll with a failed read, the wait doubles (up to the ceiling) so a failing
183
+ // RPC is not hit at full cadence; a clean poll restores the normal interval.
184
+ let backoff = 0;
185
+ for (;;) {
186
+ const failedBefore = failedReads;
187
+ if (await pollOnce())
188
+ return signature;
189
+ if (expiredAt !== undefined)
190
+ break;
191
+ backoff = failedReads > failedBefore ? backoff + 1 : 0;
192
+ const interval = Math.min(pollIntervalMs * 2 ** backoff, Math.max(pollIntervalMs, exports.CONFIRM_MAX_BACKOFF_MS));
193
+ const wait = Math.min(interval, deadline - Date.now());
194
+ if (wait <= 0)
195
+ break;
196
+ await new Promise((r) => setTimeout(r, wait));
197
+ }
198
+ const seconds = timeoutMs / 1000;
199
+ const seenAt = lastSeen ? levelOf(lastSeen) : null;
200
+ const reason = expiredAt !== undefined
201
+ ? 'expired_not_found'
202
+ : !lastSeen
203
+ ? failedReads > 0
204
+ ? 'rpc_failing'
205
+ : 'not_visible'
206
+ : lastSeen.err
207
+ ? 'failed_below_commitment'
208
+ : 'below_commitment';
209
+ const message = {
210
+ expired_not_found: `Transaction ${signature} was not found and its blockhash has expired (block height exceeded), ` +
211
+ 'so it very likely never landed. This cannot be proven from the RPC; check the signature before resubmitting.',
212
+ below_commitment: `Transaction ${signature} landed without error (${seenAt}) but was not confirmed in ${seconds} seconds ` +
213
+ `at '${commitment}'. Do not resubmit; it is most likely to finalize.`,
214
+ failed_below_commitment: `Transaction ${signature} was seen failing (${seenAt}) but was not confirmed in ${seconds} seconds ` +
215
+ `at '${commitment}', so the failure is not final yet. Check the signature before resubmitting.`,
216
+ rpc_failing: undefined,
217
+ not_visible: undefined,
218
+ }[reason];
219
+ throw new core_1.AugustTransactionError(message ??
220
+ `Transaction ${signature} was not confirmed in ${seconds} seconds. ` +
221
+ 'It is unknown if it succeeded or failed; check the signature before resubmitting.', signature, {
222
+ cause: expiredAt !== undefined
223
+ ? new web3_js_1.TransactionExpiredBlockheightExceededError(signature)
224
+ : lastReadError,
225
+ context: {
226
+ signature,
227
+ commitment,
228
+ reason,
229
+ ...(expiredAt !== undefined && {
230
+ lastValidBlockHeight,
231
+ blockHeight: expiredAt,
232
+ }),
233
+ lastSeenStatus: seenAt,
234
+ lastSeenErr: lastSeen?.err ?? null,
235
+ reads,
236
+ failedReads,
237
+ },
238
+ });
239
+ }
240
+ /**
241
+ * `AnchorProvider` whose `sendAndConfirm` — what `program.methods…rpc()` calls —
242
+ * confirms with {@link confirmByBlockhash} instead of Anchor's signature-only
243
+ * WebSocket wait. Signing and Anchor's error translation are unchanged.
244
+ */
245
+ class BlockhashConfirmingProvider extends anchor_1.AnchorProvider {
246
+ /**
247
+ * Sign, send and confirm a transaction.
248
+ *
249
+ * @param tx - Transaction to send. Its blockhash (and fee payer, if unset)
250
+ * are set here. A `VersionedTransaction` already carries a blockhash this
251
+ * cannot learn the expiry of, so it takes Anchor's default path.
252
+ * @param signers - Extra signers besides the wallet.
253
+ * @param opts - Defaults to the provider's options.
254
+ * @returns The transaction signature.
255
+ * @throws See {@link confirmByBlockhash}; also whatever the wallet or
256
+ * `sendRawTransaction` throws (user rejection, preflight failure).
257
+ * @remarks **RPC cost.** `getLatestBlockhash` + `sendRawTransaction`, then
258
+ * the confirmation polls described on {@link confirmByBlockhash}.
259
+ */
260
+ async sendAndConfirm(tx, signers, opts = this.opts) {
261
+ if ('version' in tx)
262
+ return super.sendAndConfirm(tx, signers, opts);
263
+ const commitment = opts.commitment ?? this.connection.commitment ?? 'finalized';
264
+ const preflightCommitment = opts.preflightCommitment ?? commitment;
265
+ const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash(preflightCommitment);
266
+ tx.feePayer = tx.feePayer ?? this.wallet.publicKey;
267
+ tx.recentBlockhash = blockhash;
268
+ for (const signer of signers ?? [])
269
+ tx.partialSign(signer);
270
+ const signed = await this.wallet.signTransaction(tx);
271
+ const signature = await this.connection.sendRawTransaction(signed.serialize(), {
272
+ skipPreflight: opts.skipPreflight,
273
+ preflightCommitment,
274
+ maxRetries: opts.maxRetries,
275
+ minContextSlot: opts.minContextSlot,
276
+ });
277
+ return confirmByBlockhash({
278
+ connection: this.connection,
279
+ signature,
280
+ // The expiry fetched here only bounds a tx that carries this blockhash.
281
+ // A wallet may re-stamp it when signing, and a durable-nonce tx
282
+ // serializes its nonce instead; either way it could still land.
283
+ lastValidBlockHeight: !signed.nonceInfo && signed.recentBlockhash === blockhash
284
+ ? lastValidBlockHeight
285
+ : undefined,
286
+ commitment,
287
+ });
288
+ }
289
+ }
290
+ exports.BlockhashConfirmingProvider = BlockhashConfirmingProvider;
291
+ //# sourceMappingURL=confirm.js.map
@@ -25,7 +25,7 @@ export declare const Solana: {
25
25
  getProvider: ({ connection, publicKey, signTransaction, }: {
26
26
  publicKey: PublicKey;
27
27
  signTransaction: (<T extends Transaction | web3.VersionedTransaction>(transaction: T) => Promise<T>) | undefined;
28
- } & import("./types").ISolanaConnectionOptions) => AnchorProvider;
28
+ } & import("./types").ISolanaConnectionOptions) => import("./confirm").BlockhashConfirmingProvider;
29
29
  getReadOnlyProvider: ({ connection }: import("./types").ISolanaConnectionOptions) => AnchorProvider;
30
30
  deriveShareMintPda: (vaultProgramId: PublicKey | string) => web3.PublicKey;
31
31
  deriveVaultStatePda: (vaultProgramId: PublicKey | string) => web3.PublicKey;
@@ -1,6 +1,7 @@
1
1
  import { AnchorProvider, BN, Program, type web3 } from '@coral-xyz/anchor';
2
2
  import { PublicKey, type ParsedAccountData, type Transaction } from '@solana/web3.js';
3
3
  import type { ISolanaConnectionOptions, ISolanaVaultState } from './types';
4
+ import { BlockhashConfirmingProvider } from './confirm';
4
5
  declare function getExplorerLink({ signature, type, network, }: {
5
6
  signature: string;
6
7
  type: 'tx' | 'address' | 'token';
@@ -16,7 +17,7 @@ declare function getBestRpcEndpoint({ endpoint, network, connection, }: ISolanaC
16
17
  declare function getProvider({ connection, publicKey, signTransaction, }: {
17
18
  publicKey: PublicKey;
18
19
  signTransaction: (<T extends Transaction | web3.VersionedTransaction>(transaction: T) => Promise<T>) | undefined;
19
- } & ISolanaConnectionOptions): AnchorProvider;
20
+ } & ISolanaConnectionOptions): BlockhashConfirmingProvider;
20
21
  declare function getReadOnlyProvider({ connection }: ISolanaConnectionOptions): AnchorProvider;
21
22
  declare function deriveVaultStatePda(vaultProgramId: PublicKey | string): web3.PublicKey;
22
23
  declare function deriveShareMintPda(vaultProgramId: PublicKey | string): web3.PublicKey;
@@ -7,6 +7,7 @@ const anchor_1 = require("@coral-xyz/anchor");
7
7
  const web3_js_1 = require("@solana/web3.js");
8
8
  const ethers_1 = require("ethers");
9
9
  const constants_1 = require("./constants");
10
+ const confirm_1 = require("./confirm");
10
11
  const core_1 = require("../../core");
11
12
  // ============================================================================
12
13
  // SOLANA UTILITIES
@@ -96,7 +97,8 @@ function getProvider({ connection, publicKey, signTransaction, }) {
96
97
  if (!publicKey || !signTransaction) {
97
98
  throw new core_1.AugustValidationError('INVALID_INPUT', 'Wallet not connected: publicKey and signTransaction are required for write operations');
98
99
  }
99
- return new anchor_1.AnchorProvider(connection, {
100
+ // Anchor's own confirmation misreports landed txs as timed out; see confirm.ts.
101
+ return new confirm_1.BlockhashConfirmingProvider(connection, {
100
102
  publicKey,
101
103
  signTransaction,
102
104
  signAllTransactions: async (txs) => {
@@ -45,6 +45,9 @@ export declare function slippageHint(e: unknown): string;
45
45
  * or when the wallet's deposit-mint balance is below `depositAmount`.
46
46
  * @throws AugustSDKError when a candidate token account's balance cannot be
47
47
  * read, or wrapping any downstream failure (`cause` preserved).
48
+ * @throws AugustTransactionError (`TRANSACTION_UNCONFIRMED`) when the
49
+ * transaction was sent but its outcome could not be established; check
50
+ * `signature` before resubmitting.
48
51
  * @remarks **Side effects.** Submits exactly one transaction, signed by the
49
52
  * provider's wallet. When the wallet has no share account, that transaction
50
53
  * also creates one, and `publicKey` pays its rent — roughly 0.00204 SOL,
@@ -92,6 +95,9 @@ export declare function handleSolanaDeposit({ provider, connection, network, vau
92
95
  * or when the wallet holds fewer shares than `redeemShares`.
93
96
  * @throws AugustSDKError when a candidate token account's balance cannot be
94
97
  * read, or wrapping any downstream failure (`cause` preserved).
98
+ * @throws AugustTransactionError (`TRANSACTION_UNCONFIRMED`) when the
99
+ * transaction was sent but its outcome could not be established; check
100
+ * `signature` before resubmitting.
95
101
  * @remarks **Side effects.** Submits exactly one transaction, signed by the
96
102
  * provider's wallet. That transaction also creates the payout account when
97
103
  * the wallet has none, and the vault's fee-recipient account when *it* has
@@ -301,6 +301,9 @@ async function resolveUserTokenAccountPair({ connection, owner, mints, }) {
301
301
  * or when the wallet's deposit-mint balance is below `depositAmount`.
302
302
  * @throws AugustSDKError when a candidate token account's balance cannot be
303
303
  * read, or wrapping any downstream failure (`cause` preserved).
304
+ * @throws AugustTransactionError (`TRANSACTION_UNCONFIRMED`) when the
305
+ * transaction was sent but its outcome could not be established; check
306
+ * `signature` before resubmitting.
304
307
  * @remarks **Side effects.** Submits exactly one transaction, signed by the
305
308
  * provider's wallet. When the wallet has no share account, that transaction
306
309
  * also creates one, and `publicKey` pays its rent — roughly 0.00204 SOL,
@@ -547,6 +550,9 @@ async function handleSolanaDeposit({ provider, connection, network = constants_1
547
550
  * or when the wallet holds fewer shares than `redeemShares`.
548
551
  * @throws AugustSDKError when a candidate token account's balance cannot be
549
552
  * read, or wrapping any downstream failure (`cause` preserved).
553
+ * @throws AugustTransactionError (`TRANSACTION_UNCONFIRMED`) when the
554
+ * transaction was sent but its outcome could not be established; check
555
+ * `signature` before resubmitting.
550
556
  * @remarks **Side effects.** Submits exactly one transaction, signed by the
551
557
  * provider's wallet. That transaction also creates the payout account when
552
558
  * the wallet has none, and the vault's fee-recipient account when *it* has
@@ -19,6 +19,7 @@ exports.queryContract = queryContract;
19
19
  exports.buildSorobanTx = buildSorobanTx;
20
20
  const stellar_sdk_1 = require("@stellar/stellar-sdk");
21
21
  const core_1 = require("../../core");
22
+ const concurrency_1 = require("../../core/helpers/concurrency");
22
23
  const constants_1 = require("./constants");
23
24
  function createServer(rpcUrl) {
24
25
  return new stellar_sdk_1.rpc.Server(rpcUrl, {
@@ -149,17 +150,6 @@ function scrubErrorUrls(value, scrub, depth = 0, seen = new WeakSet()) {
149
150
  }
150
151
  return out;
151
152
  }
152
- /**
153
- * Race a promise against a timeout so a hung call can't block indefinitely. The
154
- * timer is always cleared, so a fast-resolving promise leaves nothing pending.
155
- */
156
- function withTimeout(promise, ms, label) {
157
- let timer;
158
- const timeout = new Promise((_resolve, reject) => {
159
- timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
160
- });
161
- return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
162
- }
163
153
  /**
164
154
  * Return a Soroban RPC server backed by a healthy endpoint for `config`. Thin
165
155
  * wrapper over {@link resolveHealthyServer} for callers (e.g. submission) that
@@ -241,7 +231,7 @@ async function probeHealthyServer(rpcUrls, cacheKey) {
241
231
  firstConstructableUrl = url;
242
232
  }
243
233
  try {
244
- const health = await withTimeout(server.getHealth(), HEALTH_PROBE_TIMEOUT_MS, 'getHealth');
234
+ const health = await (0, concurrency_1.withTimeout)(server.getHealth(), HEALTH_PROBE_TIMEOUT_MS, 'getHealth');
245
235
  if (health.status === 'healthy') {
246
236
  healthyServerCache.set(cacheKey, {
247
237
  server,
@@ -355,7 +345,7 @@ async function withEndpointFailover(config, method, operation, options = {}) {
355
345
  const rpcUrls = config.rpcUrls && config.rpcUrls.length > 0
356
346
  ? config.rpcUrls
357
347
  : [config.rpcUrl];
358
- const runOn = (server) => withTimeout(operation(server), timeoutMs, `Soroban ${method}`);
348
+ const runOn = (server) => (0, concurrency_1.withTimeout)(operation(server), timeoutMs, `Soroban ${method}`);
359
349
  const cacheKey = rpcUrls.join('|');
360
350
  // Reduce every configured endpoint URL to protocol+host in anything surfaced
361
351
  // from here (logs, the thrown message, the chained cause). SDK errors embed the
@@ -3,4 +3,4 @@
3
3
  * Generated during publish from package.json version
4
4
  * This file is gitignored and created at publish time
5
5
  */
6
- export declare const SDK_VERSION = "9.9.0";
6
+ export declare const SDK_VERSION = "9.10.1";
@@ -6,5 +6,5 @@ exports.SDK_VERSION = void 0;
6
6
  * Generated during publish from package.json version
7
7
  * This file is gitignored and created at publish time
8
8
  */
9
- exports.SDK_VERSION = '9.9.0';
9
+ exports.SDK_VERSION = '9.10.1';
10
10
  //# sourceMappingURL=version.js.map
@@ -117,6 +117,7 @@ export declare const WEBSERVER_ENDPOINTS: {
117
117
  summary: (vaultAddress: IAddress) => string;
118
118
  withdrawals: (chain: string, vaultAddress: IAddress) => string;
119
119
  debank: (vaultAddress: IAddress) => string;
120
+ octav: (vaultAddress: IAddress) => string;
120
121
  userHistory: (vaultAddress: IAddress, wallet: IAddress) => string;
121
122
  archivedVaults: string;
122
123
  };
@@ -184,6 +184,7 @@ exports.WEBSERVER_ENDPOINTS = {
184
184
  summary: (vaultAddress) => `/tokenized_vault/vault_summary/${(0, ethers_1.getAddress)(vaultAddress)}`,
185
185
  withdrawals: (chain, vaultAddress) => `/withdrawals/${chain}/${(0, ethers_1.getAddress)(vaultAddress)}`,
186
186
  debank: (vaultAddress) => `/tokenized_vault/${(0, ethers_1.getAddress)(vaultAddress)}/debank`,
187
+ octav: (vaultAddress) => `/tokenized_vault/${subaccountSegment(vaultAddress)}/octav`,
187
188
  // Mongo-backed deposit/withdraw history for archived vaults whose
188
189
  // subgraph has been (or will be) retired. See fetchArchivedVaults.
189
190
  userHistory: (vaultAddress, wallet) => `/upshift/vault_history/${(0, ethers_1.getAddress)(vaultAddress)}?wallet=${(0, ethers_1.getAddress)(wallet)}`,
@@ -15,7 +15,7 @@
15
15
  * ```
16
16
  */
17
17
  /** Stable error codes attached to SDK errors. */
18
- export type AugustErrorCode = 'AUTH_MISSING_KEY' | 'AUTH_INVALID_KEY' | 'AUTH_FORBIDDEN' | 'AUTH_UNAUTHORIZED' | 'INVALID_URL' | 'INVALID_INPUT' | 'INVALID_CHAIN' | 'INVALID_ADDRESS' | 'ACCOUNT_NOT_FUNDED' | 'NETWORK_ERROR' | 'TIMEOUT' | 'RATE_LIMITED' | 'SERVER_ERROR' | 'HISTORY_UNAVAILABLE' | 'PRICE_UNAVAILABLE' | 'UNKNOWN';
18
+ export type AugustErrorCode = 'AUTH_MISSING_KEY' | 'AUTH_INVALID_KEY' | 'AUTH_FORBIDDEN' | 'AUTH_UNAUTHORIZED' | 'INVALID_URL' | 'INVALID_INPUT' | 'INVALID_CHAIN' | 'INVALID_ADDRESS' | 'ACCOUNT_NOT_FUNDED' | 'NETWORK_ERROR' | 'TIMEOUT' | 'RATE_LIMITED' | 'SERVER_ERROR' | 'TRANSACTION_UNCONFIRMED' | 'HISTORY_UNAVAILABLE' | 'PRICE_UNAVAILABLE' | 'UNKNOWN';
19
19
  /** Options accepted by every SDK error constructor. */
20
20
  export interface AugustErrorOptions {
21
21
  /** Original error preserved for stack-trace chaining. */
@@ -145,5 +145,33 @@ export declare class AugustPriceUnavailableError extends AugustSDKError {
145
145
  */
146
146
  constructor(message: string, options?: AugustErrorOptions);
147
147
  }
148
+ /**
149
+ * A transaction was sent but its outcome could not be established
150
+ * (`TRANSACTION_UNCONFIRMED`). It may have landed: check {@link signature}
151
+ * before resubmitting, or a deposit or redeem can execute twice.
152
+ * `context.reason` says what was seen (e.g. `expired_not_found`: the blockhash
153
+ * expired and no lookup found it, so it very likely never landed).
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * try {
158
+ * await sdk.solana.vaultDeposit(programId, idl, wallet, amount, undefined, vault);
159
+ * } catch (err) {
160
+ * if (err instanceof AugustTransactionError) showPending(err.signature);
161
+ * else throw err;
162
+ * }
163
+ * ```
164
+ */
165
+ export declare class AugustTransactionError extends AugustSDKError {
166
+ /** Signature of the transaction that was sent. */
167
+ readonly signature: string;
168
+ /**
169
+ * @param message - Human-readable description, naming the signature.
170
+ * @param signature - Signature of the transaction that was sent.
171
+ * @param options - Standard SDK error options.
172
+ */
173
+ constructor(message: string, signature: string, options?: AugustErrorOptions);
174
+ toJSON(): Record<string, unknown>;
175
+ }
148
176
  /** Type guard. Works across realms (e.g. Web Worker / VM contexts). */
149
177
  export declare function isAugustSDKError(err: unknown): err is AugustSDKError;
@@ -16,7 +16,7 @@
16
16
  * ```
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.AugustPriceUnavailableError = exports.AugustHistoryUnavailableError = exports.AugustServerError = exports.AugustRateLimitError = exports.AugustValidationError = exports.AugustTimeoutError = exports.AugustNetworkError = exports.AugustAuthError = exports.AugustSDKError = void 0;
19
+ exports.AugustTransactionError = exports.AugustPriceUnavailableError = exports.AugustHistoryUnavailableError = exports.AugustServerError = exports.AugustRateLimitError = exports.AugustValidationError = exports.AugustTimeoutError = exports.AugustNetworkError = exports.AugustAuthError = exports.AugustSDKError = void 0;
20
20
  exports.isAugustSDKError = isAugustSDKError;
21
21
  /** Base class for all SDK errors. */
22
22
  class AugustSDKError extends Error {
@@ -212,6 +212,41 @@ class AugustPriceUnavailableError extends AugustSDKError {
212
212
  }
213
213
  }
214
214
  exports.AugustPriceUnavailableError = AugustPriceUnavailableError;
215
+ /**
216
+ * A transaction was sent but its outcome could not be established
217
+ * (`TRANSACTION_UNCONFIRMED`). It may have landed: check {@link signature}
218
+ * before resubmitting, or a deposit or redeem can execute twice.
219
+ * `context.reason` says what was seen (e.g. `expired_not_found`: the blockhash
220
+ * expired and no lookup found it, so it very likely never landed).
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * try {
225
+ * await sdk.solana.vaultDeposit(programId, idl, wallet, amount, undefined, vault);
226
+ * } catch (err) {
227
+ * if (err instanceof AugustTransactionError) showPending(err.signature);
228
+ * else throw err;
229
+ * }
230
+ * ```
231
+ */
232
+ class AugustTransactionError extends AugustSDKError {
233
+ /** Signature of the transaction that was sent. */
234
+ signature;
235
+ /**
236
+ * @param message - Human-readable description, naming the signature.
237
+ * @param signature - Signature of the transaction that was sent.
238
+ * @param options - Standard SDK error options.
239
+ */
240
+ constructor(message, signature, options) {
241
+ super('TRANSACTION_UNCONFIRMED', message, options);
242
+ this.name = 'AugustTransactionError';
243
+ this.signature = signature;
244
+ }
245
+ toJSON() {
246
+ return { ...super.toJSON(), signature: this.signature };
247
+ }
248
+ }
249
+ exports.AugustTransactionError = AugustTransactionError;
215
250
  /** Type guard. Works across realms (e.g. Web Worker / VM contexts). */
216
251
  function isAugustSDKError(err) {
217
252
  return (err instanceof AugustSDKError ||