/** * Is this error a wallet/user rejection of a transaction or signature request? * * Detects ethers v6's `ACTION_REJECTED` code, the EIP-1193 `4001` * ("User rejected the request") code (top-level or nested), and the common * human-readable phrasings as a fallback for providers that omit a code. * * @param error - The caught value, of unknown type. * @returns `true` when the failure was the user declining in their wallet. * * @example * ```ts * try { await vault.deposit(...); } * catch (e) { * if (isUserRejectionError(e)) return; // user cancelled — not an error * throw e; * } * ``` */ export declare function isUserRejectionError(error: unknown): boolean; /** * Is this error a routine on-chain read revert rather than a real failure? * * Reading a function a contract doesn't implement, or an address that holds no * (or incompatible) bytecode, reverts — a normal outcome when the SDK probes * heterogeneous vaults. Matches ethers' `CALL_EXCEPTION` code and the * message variants emitted by ethers and viem (`missing revert data`, * `execution reverted`, `call revert exception`, and the bare `reverted` * phrasing such as `the contract function "totalAssets" reverted`). * * Scope note: callers apply this on **read** paths only. A reverted *write* * (a tx that failed on-chain) is a genuine error and is intentionally not * demoted by this predicate's use in `write.actions`. * * @param error - The caught value, of unknown type. * @returns `true` when the failure is an expected/benign contract revert. * * @example * ```ts * try { return await vaultContract.maxDepositAmount(); } * catch (e) { * logChainError('maxDeposit', e, isExpectedRevertError(e)); * throw e; * } * ``` */ export declare function isExpectedRevertError(error: unknown): boolean; export declare function isInsufficientFundsError(error: unknown): boolean; /** * Is this error a transient RPC **transport** failure that is safe to retry, * rather than a decision the chain made? * * Why this exists: the SDK's write paths poll `eth_getTransactionReceipt` to * confirm a broadcast transaction. When the provider hiccups mid-poll, ethers * surfaces `could not coalesce error (error={ "code": -32603, … "method": * "eth_getTransactionReceipt" … })`. Historically that propagated out of * `safeWaitForTx` and the SDK reported the write as **failed** — even though * the transaction was broadcast, its hash was known, and it mined fine. Users * then retried and hit `ERC20InsufficientBalance` because the first attempt had * in fact succeeded. Classifying the failure as transport-level lets callers * re-poll instead of lying to the user. * * Matches, in order of precedence: * 1. **Veto** — anything with revert evidence ({@link hasRevertEvidence}: * `CALL_EXCEPTION` carrying revert `data`, an `execution reverted` message, * or an attached `receipt.status === 0`) returns `false`. Nodes reuse * `-32603`/`-32000` for real reverts, so the veto must come first. * 2. JSON-RPC / ethers transport codes — see `RETRYABLE_RPC_CODES`. * 3. HTTP `429` and any `5xx` carried on the error. * 4. Transport message fragments — see `RETRYABLE_RPC_PHRASES`. * * Retrying is only safe for **idempotent** work: re-reading an immutable value * (`decimals()`) or re-polling a receipt for a hash that is already on the * wire. Never use this to re-send a transaction. * * @param error - The caught value, of unknown type. * @returns `true` when the failure is a transient transport fault worth * retrying with backoff; `false` for chain-level decisions (reverts) and for * anything unrecognised — the safe default is to surface the error. * * @example * ```ts * try { * return await provider.waitForTransaction(hash, 1, 120_000); * } catch (e) { * if (!isRetryableRpcError(e)) throw e; // real revert — surface it * await sleep(250); * return await provider.waitForTransaction(hash, 1, 120_000); * } * ``` */ export declare function isRetryableRpcError(error: unknown): boolean; /** * `lpTokenAddress()` — `keccak256("lpTokenAddress()")[0..4]`. The August `evm-2` * tokenized vault's receipt-token getter, exported so the readers that invoke it * can scope {@link isEmptyViewResponse} to exactly this call instead of * duplicating the literal. */ export declare const LP_TOKEN_ADDRESS_SELECTOR = "0xf5ae497a"; /** * Is this error an **empty RPC response to an argument-free view call** — * i.e. a transport artefact wearing a revert's clothes? * * Why this is separate from {@link isRetryableRpcError}: when a provider * truncates or 500s a response to `eth_call`, ethers reports * `missing revert data (action="call", data="0x313ce567", …)` with a `null` * `data` field. That is byte-for-byte the shape of a genuine revert with no * reason string, so a general "transport" predicate cannot safely claim it — * doing so would retry every data-less `CALL_EXCEPTION` in the SDK. This * predicate narrows the claim to the one case where the ambiguity resolves: * a **deployed ERC-20's `decimals()`/`symbol()`/`name()`/`totalSupply()` cannot * legitimately revert**, because it takes no arguments and returns state fixed * at deployment. An empty response there is the provider's fault, full stop. * * A match requires all of: * 1. no revert evidence ({@link hasRevertEvidence}) — anything carrying real * revert `data`, an `execution reverted` message, or a failed receipt is out; * 2. a `missing revert data` message; * 3. an `action` of `call` or `staticCall` — a read, never a state change; * 4. **when a selector is derivable** from the error, that it is * `expectedSelector` (if given) or one of * {@link ARGUMENT_FREE_VIEW_SELECTORS}. When no selector can be recovered, * conditions 1–3 stand on their own. * * Note the cost of a false positive is bounded and small: the caller retries an * idempotent read a couple of times before surfacing the same error. The cost * of a false negative is the production flood this predicate exists to stop. * * @param error - The caught value, of unknown type. * @param expectedSelector - Optional `0x`-prefixed 4-byte selector the caller * knows it invoked (e.g. `'0x313ce567'` for `decimals()`). When supplied, the * error's own selector must match it — this stops a `decimals()` retry from * firing on an unrelated view call that happened to fail the same way. * @returns `true` when the failure is an empty provider response to a view call * that cannot revert, and is therefore safe to retry. * * @example * ```ts * try { return Number(await erc20.decimals()); } * catch (e) { * if (!isEmptyViewResponse(e, '0x313ce567')) throw e; // real problem * return Number(await erc20.decimals()); // provider blip * } * ``` */ export declare function isEmptyViewResponse(error: unknown, expectedSelector?: string): boolean; /** * Run an **idempotent** RPC read, retrying with exponential backoff while the * failure classifies as a transient transport fault * ({@link isRetryableRpcError}). * * Lives next to the classifiers it consumes so there is exactly one retry * implementation in the SDK: both the receipt-poll fallback in the vault write * paths and the cached `decimals()` reader in `core/helpers/web3.ts` call this. * * Only safe for operations that can be repeated without side effects: polling * `eth_getTransactionReceipt` for an already-broadcast hash, or re-reading an * immutable value such as `decimals()`. **Never wrap a transaction send in * this.** * * Anything that is not a transport fault (a genuine revert, a user rejection, * an insufficient-funds rejection) is rethrown on the first attempt with no * delay, so real failures still fail fast. * * @param tag - Low-cardinality log label for the retry breadcrumb. * @param operation - The idempotent async read to run. * @param context - Extra structured context for the retry breadcrumb (e.g. * `{ hash }`). Sanitized by the logger before transport. * @param isRetryable - Predicate deciding whether a caught error warrants * another attempt. Defaults to the strict transport definition * ({@link isRetryableRpcError}); pass a wider one only where the call site * can prove the extra shape is also a provider artefact — the only such case * today is the selector-scoped {@link isEmptyViewResponse} used by * `getDecimalsOrThrow`. * @returns Whatever `operation` resolves to on the first successful attempt. * @throws The last error thrown by `operation` once retries are exhausted, or * immediately when the error is not retryable. * * @example * ```ts * const receipt = await retryOnTransientRpc( * 'safeWaitForTx:transport-retry', * () => provider.waitForTransaction(hash, 1, 120_000), * { hash }, * ); * ``` */ export declare function retryOnTransientRpc(tag: string, operation: () => Promise, context?: Record, isRetryable?: (error: unknown) => boolean): Promise; /** * Log a caught chain error at the severity its category warrants, without * swallowing it. When `isBenign` is `true` the failure is recorded as a `warn` * (a Sentry breadcrumb that rides along with the next real issue, not a billed * standalone issue); otherwise it is logged at `error` (a Sentry issue). The * caller is still responsible for re-throwing — this only routes telemetry. * * Pass the benign decision explicitly (via {@link isUserRejectionError} or * {@link isExpectedRevertError}) so the call site documents *why* the demotion * is safe and each path opts into only the category that applies to it. * * @param tag - Low-cardinality call-site label (e.g. `'deposit'`), used as the * Sentry breadcrumb/issue grouping key. * @param error - The caught value, of unknown type. * @param isBenign - `true` to demote to `warn`; `false` to keep at `error`. * @param context - Optional structured context attached to the log entry. It is * sanitized by the logger before transport. */ export declare function logChainError(tag: string, error: unknown, isBenign: boolean, context?: Record): void;