@augustdigital/sdk 8.6.0 → 8.6.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.
@@ -123,15 +123,11 @@ function trackMethodCall(methodName, metrics, args) {
123
123
  // Counters: SUM(invocation) → total calls; SUM(error)/SUM(invocation) → error rate.
124
124
  sdk.setMeasurement('sdk.method.invocation', 1, 'none');
125
125
  sdk.setMeasurement('sdk.method.error', metrics.success ? 0 : 1, 'none');
126
- // Update tags for filtering
126
+ // Only sticky-tag the last method name — useful for attributing errors that fire
127
+ // outside an active SDK span. sdk.category and sdk.chain are already recorded as
128
+ // span attributes on each call, so mutating the global scope with them here would
129
+ // just contaminate subsequent unrelated events with stale values.
127
130
  sdk.setTag('sdk.last_method', methodName);
128
- if (metrics.category) {
129
- sdk.setTag('sdk.category', metrics.category);
130
- }
131
- if (metrics.chainId !== undefined) {
132
- sdk.setTag('sdk.chain', (0, chain_name_1.chainIdToTagValue)(metrics.chainId));
133
- sdk.setTag('sdk.last_chainId', metrics.chainId.toString());
134
- }
135
131
  }
136
132
  catch {
137
133
  // Silently fail - analytics should never break SDK
@@ -223,9 +219,9 @@ function trackSyncMethodCall(methodName, metrics, args) {
223
219
  ...args,
224
220
  },
225
221
  });
226
- // Counters sync calls attach to whatever span is active on the scope.
227
- sdk.setMeasurement('sdk.method.invocation', 1, 'none');
228
- sdk.setMeasurement('sdk.method.error', metrics.success ? 0 : 1, 'none');
222
+ // Measurements omitted: sync methods have no span of their own, so setMeasurement
223
+ // would attach to whatever parent transaction happens to be active on the scope,
224
+ // overwriting its counters. The breadcrumb above already captures success/failure.
229
225
  sdk.setTag('sdk.last_sync_method', methodName);
230
226
  if (metrics.category) {
231
227
  sdk.setTag('sdk.category', metrics.category);
@@ -6,7 +6,7 @@ import type { IAnalyticsConfig } from './types';
6
6
  * only refresh user identity and the cached API-key hash.
7
7
  *
8
8
  * @param config - Analytics configuration. Pass `tracesSampleRate` to
9
- * override the Phase 1/2 default of `1.0`.
9
+ * override the default of `0.1`.
10
10
  * @param environment - Current environment (DEV or PROD).
11
11
  * @param walletAddress - Optional wallet address for user identification.
12
12
  * @param apiKey - Optional API key (hashed for identification).
@@ -138,7 +138,7 @@ function createSentrySink() {
138
138
  * only refresh user identity and the cached API-key hash.
139
139
  *
140
140
  * @param config - Analytics configuration. Pass `tracesSampleRate` to
141
- * override the Phase 1/2 default of `1.0`.
141
+ * override the default of `0.1`.
142
142
  * @param environment - Current environment (DEV or PROD).
143
143
  * @param walletAddress - Optional wallet address for user identification.
144
144
  * @param apiKey - Optional API key (hashed for identification).
@@ -214,17 +214,36 @@ function initializeSentry(config, environment, walletAddress, apiKey, appName) {
214
214
  const rawSampleRate = typeof config.tracesSampleRate === 'number' &&
215
215
  Number.isFinite(config.tracesSampleRate)
216
216
  ? config.tracesSampleRate
217
- : 1.0;
217
+ : 0.1; // Phase 2 default — verified partners can opt into 1.0 explicitly
218
218
  const tracesSampleRate = Math.max(0, Math.min(1, rawSampleRate));
219
219
  sdk.init({
220
220
  dsn: constants_1.SENTRY_DSN,
221
- // TODO(post-verified-partner): ramp default down to ~0.1.
222
221
  tracesSampleRate,
223
222
  enableTracing: true,
224
223
  environment: environment.toLowerCase(),
225
224
  release: `august-sdk@${getSDKVersion()}`,
226
225
  sendDefaultPii: true,
227
- integrations: [sdk.captureConsoleIntegration({ levels: ['error'] })],
226
+ // SDK errors route through Logger.setSentrySink captureException directly,
227
+ // so captureConsoleIntegration is redundant here. Omitting it also prevents
228
+ // consumer app console.error calls from being billed as SDK error events (the
229
+ // global sdk:'august-digital' tag would otherwise cause them to pass beforeSend).
230
+ integrations: [],
231
+ // Drop classes of event that are never SDK signal, as a backstop to the
232
+ // call-site demotions (see `logChainError` / `isUserRejectionError`).
233
+ // Scope is deliberately narrow: only categories that are noise in EVERY
234
+ // code path. Generic contract reverts (`CALL_EXCEPTION` / "missing revert
235
+ // data") are intentionally NOT listed — a reverted *write* tx is a real
236
+ // error we keep at `error` level, and only read-path reverts are demoted
237
+ // (surgically, at the call site).
238
+ ignoreErrors: [
239
+ // Wallet user-rejections are normal product behaviour, not an SDK fault.
240
+ /user rejected/i,
241
+ /user denied/i,
242
+ 'ACTION_REJECTED',
243
+ // Local dev/test node leaking into a prod DSN: :8545 is the canonical
244
+ // Anvil/Hardhat port and is never a production RPC endpoint.
245
+ /ECONNREFUSED .*:8545/,
246
+ ],
228
247
  beforeSend(event) {
229
248
  // Only send events tagged as SDK-related
230
249
  if (event.tags?.sdk !== 'august-digital') {
@@ -233,7 +252,14 @@ function initializeSentry(config, environment, walletAddress, apiKey, appName) {
233
252
  return event;
234
253
  },
235
254
  beforeSendTransaction(transaction) {
236
- // Always include transactions for performance tracking
255
+ // Sub-sample the high-frequency network bookkeeping category (switchNetwork,
256
+ // updateWallet, clearWallet, setSigner) to 10% of whatever tracesSampleRate
257
+ // already passed — these fire on every wallet/chain update and are low signal
258
+ // for the partner-usage dashboard relative to read/write calls.
259
+ if (transaction.tags?.['sdk.category'] === 'network' &&
260
+ Math.random() > 0.1) {
261
+ return null;
262
+ }
237
263
  return transaction;
238
264
  },
239
265
  });
@@ -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 = "8.6.0";
6
+ export declare const SDK_VERSION = "8.6.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 = '8.6.0';
9
+ exports.SDK_VERSION = '8.6.1';
10
10
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Is this error a wallet/user rejection of a transaction or signature request?
3
+ *
4
+ * Detects ethers v6's `ACTION_REJECTED` code, the EIP-1193 `4001`
5
+ * ("User rejected the request") code (top-level or nested), and the common
6
+ * human-readable phrasings as a fallback for providers that omit a code.
7
+ *
8
+ * @param error - The caught value, of unknown type.
9
+ * @returns `true` when the failure was the user declining in their wallet.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * try { await vault.deposit(...); }
14
+ * catch (e) {
15
+ * if (isUserRejectionError(e)) return; // user cancelled — not an error
16
+ * throw e;
17
+ * }
18
+ * ```
19
+ */
20
+ export declare function isUserRejectionError(error: unknown): boolean;
21
+ /**
22
+ * Is this error a routine on-chain read revert rather than a real failure?
23
+ *
24
+ * Reading a function a contract doesn't implement, or an address that holds no
25
+ * (or incompatible) bytecode, reverts — a normal outcome when the SDK probes
26
+ * heterogeneous vaults. Matches ethers' `CALL_EXCEPTION` code and the
27
+ * message variants emitted by ethers and viem (`missing revert data`,
28
+ * `execution reverted`, `call revert exception`, and the bare `reverted`
29
+ * phrasing such as `the contract function "totalAssets" reverted`).
30
+ *
31
+ * Scope note: callers apply this on **read** paths only. A reverted *write*
32
+ * (a tx that failed on-chain) is a genuine error and is intentionally not
33
+ * demoted by this predicate's use in `write.actions`.
34
+ *
35
+ * @param error - The caught value, of unknown type.
36
+ * @returns `true` when the failure is an expected/benign contract revert.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * try { return await vaultContract.maxDepositAmount(); }
41
+ * catch (e) {
42
+ * logChainError('maxDeposit', e, isExpectedRevertError(e));
43
+ * throw e;
44
+ * }
45
+ * ```
46
+ */
47
+ export declare function isExpectedRevertError(error: unknown): boolean;
48
+ /**
49
+ * Log a caught chain error at the severity its category warrants, without
50
+ * swallowing it. When `isBenign` is `true` the failure is recorded as a `warn`
51
+ * (a Sentry breadcrumb that rides along with the next real issue, not a billed
52
+ * standalone issue); otherwise it is logged at `error` (a Sentry issue). The
53
+ * caller is still responsible for re-throwing — this only routes telemetry.
54
+ *
55
+ * Pass the benign decision explicitly (via {@link isUserRejectionError} or
56
+ * {@link isExpectedRevertError}) so the call site documents *why* the demotion
57
+ * is safe and each path opts into only the category that applies to it.
58
+ *
59
+ * @param tag - Low-cardinality call-site label (e.g. `'deposit'`), used as the
60
+ * Sentry breadcrumb/issue grouping key.
61
+ * @param error - The caught value, of unknown type.
62
+ * @param isBenign - `true` to demote to `warn`; `false` to keep at `error`.
63
+ * @param context - Optional structured context attached to the log entry. It is
64
+ * sanitized by the logger before transport.
65
+ */
66
+ export declare function logChainError(tag: string, error: unknown, isBenign: boolean, context?: Record<string, unknown>): void;
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isUserRejectionError = isUserRejectionError;
4
+ exports.isExpectedRevertError = isExpectedRevertError;
5
+ exports.logChainError = logChainError;
6
+ const logger_1 = require("../logger");
7
+ /**
8
+ * Classification of caught chain/RPC/wallet errors so the SDK can decide
9
+ * whether a failure is worth a standalone Sentry issue or is routine, expected
10
+ * noise that should ride along as a breadcrumb instead.
11
+ *
12
+ * Why this exists: most of the SDK's read/write paths `catch` and re-throw, and
13
+ * historically logged every caught error at `error` level — which the SDK's
14
+ * Sentry sink turns into a billed issue (see the severity policy on
15
+ * `SDKSentrySink` in `core/logger`). Two large, low-signal categories dominate
16
+ * that volume:
17
+ *
18
+ * 1. **User-rejected transactions** — the user clicked "reject" in their wallet.
19
+ * This is normal product behaviour, not an SDK fault, yet it fires on every
20
+ * cancelled deposit/redeem/approve.
21
+ * 2. **Expected on-chain read reverts** — reading a function a vault doesn't
22
+ * implement, or an address with no/incompatible bytecode, reverts. This is a
23
+ * routine outcome of probing heterogeneous vaults, not a defect.
24
+ *
25
+ * These predicates let call sites demote exactly those cases to `warn` while
26
+ * leaving genuine failures at `error`. They are intentionally dependency-free
27
+ * (no `ethers`/Sentry imports) so they stay cheap and safe in both browser and
28
+ * Node, and classify purely by inspecting the error's `code`/`message` shape.
29
+ *
30
+ * @module
31
+ */
32
+ /**
33
+ * Best-effort message extraction from an unknown thrown value. Reads
34
+ * `error.message` for `Error` and error-like objects, passes strings through,
35
+ * and falls back to `String(error)` for everything else. Never throws.
36
+ *
37
+ * @param error - The caught value, of unknown type.
38
+ * @returns The error's message text (never `undefined`).
39
+ */
40
+ function errorText(error) {
41
+ if (typeof error === 'string')
42
+ return error;
43
+ if (error instanceof Error)
44
+ return error.message;
45
+ if (error && typeof error === 'object') {
46
+ const maybe = error.message;
47
+ if (typeof maybe === 'string')
48
+ return maybe;
49
+ }
50
+ return String(error);
51
+ }
52
+ /**
53
+ * Collect the `code` fields an error-like value may carry. Wallet/provider
54
+ * errors stash their machine-readable code in different places depending on the
55
+ * stack: ethers sets a top-level string `code` (e.g. `'ACTION_REJECTED'`),
56
+ * EIP-1193 providers use a numeric `code` (e.g. `4001`), and some wrap the
57
+ * original under `error`/`info.error`/`cause`. We scan the common locations so
58
+ * callers needn't know which library produced the error.
59
+ *
60
+ * @param error - The caught value, of unknown type.
61
+ * @returns Every string/number `code` found (possibly empty).
62
+ */
63
+ function errorCodes(error) {
64
+ const codes = [];
65
+ if (error && typeof error === 'object') {
66
+ const e = error;
67
+ const candidates = [
68
+ e.code,
69
+ e.error?.code,
70
+ e.info?.error?.code,
71
+ e.cause?.code,
72
+ ];
73
+ for (const c of candidates) {
74
+ if (typeof c === 'string' || typeof c === 'number')
75
+ codes.push(c);
76
+ }
77
+ }
78
+ return codes;
79
+ }
80
+ /**
81
+ * Is this error a wallet/user rejection of a transaction or signature request?
82
+ *
83
+ * Detects ethers v6's `ACTION_REJECTED` code, the EIP-1193 `4001`
84
+ * ("User rejected the request") code (top-level or nested), and the common
85
+ * human-readable phrasings as a fallback for providers that omit a code.
86
+ *
87
+ * @param error - The caught value, of unknown type.
88
+ * @returns `true` when the failure was the user declining in their wallet.
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * try { await vault.deposit(...); }
93
+ * catch (e) {
94
+ * if (isUserRejectionError(e)) return; // user cancelled — not an error
95
+ * throw e;
96
+ * }
97
+ * ```
98
+ */
99
+ function isUserRejectionError(error) {
100
+ const codes = errorCodes(error);
101
+ if (codes.includes('ACTION_REJECTED') || codes.includes(4001))
102
+ return true;
103
+ const msg = errorText(error).toLowerCase();
104
+ return (msg.includes('user rejected') ||
105
+ msg.includes('user denied') ||
106
+ msg.includes('rejected the request') ||
107
+ msg.includes('request rejected'));
108
+ }
109
+ /**
110
+ * Is this error a routine on-chain read revert rather than a real failure?
111
+ *
112
+ * Reading a function a contract doesn't implement, or an address that holds no
113
+ * (or incompatible) bytecode, reverts — a normal outcome when the SDK probes
114
+ * heterogeneous vaults. Matches ethers' `CALL_EXCEPTION` code and the
115
+ * message variants emitted by ethers and viem (`missing revert data`,
116
+ * `execution reverted`, `call revert exception`, and the bare `reverted`
117
+ * phrasing such as `the contract function "totalAssets" reverted`).
118
+ *
119
+ * Scope note: callers apply this on **read** paths only. A reverted *write*
120
+ * (a tx that failed on-chain) is a genuine error and is intentionally not
121
+ * demoted by this predicate's use in `write.actions`.
122
+ *
123
+ * @param error - The caught value, of unknown type.
124
+ * @returns `true` when the failure is an expected/benign contract revert.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * try { return await vaultContract.maxDepositAmount(); }
129
+ * catch (e) {
130
+ * logChainError('maxDeposit', e, isExpectedRevertError(e));
131
+ * throw e;
132
+ * }
133
+ * ```
134
+ */
135
+ function isExpectedRevertError(error) {
136
+ const codes = errorCodes(error);
137
+ if (codes.includes('CALL_EXCEPTION'))
138
+ return true;
139
+ const msg = errorText(error).toLowerCase();
140
+ return (msg.includes('call_exception') ||
141
+ msg.includes('missing revert data') ||
142
+ msg.includes('execution reverted') ||
143
+ msg.includes('call revert exception') ||
144
+ msg.includes('reverted'));
145
+ }
146
+ /**
147
+ * Log a caught chain error at the severity its category warrants, without
148
+ * swallowing it. When `isBenign` is `true` the failure is recorded as a `warn`
149
+ * (a Sentry breadcrumb that rides along with the next real issue, not a billed
150
+ * standalone issue); otherwise it is logged at `error` (a Sentry issue). The
151
+ * caller is still responsible for re-throwing — this only routes telemetry.
152
+ *
153
+ * Pass the benign decision explicitly (via {@link isUserRejectionError} or
154
+ * {@link isExpectedRevertError}) so the call site documents *why* the demotion
155
+ * is safe and each path opts into only the category that applies to it.
156
+ *
157
+ * @param tag - Low-cardinality call-site label (e.g. `'deposit'`), used as the
158
+ * Sentry breadcrumb/issue grouping key.
159
+ * @param error - The caught value, of unknown type.
160
+ * @param isBenign - `true` to demote to `warn`; `false` to keep at `error`.
161
+ * @param context - Optional structured context attached to the log entry. It is
162
+ * sanitized by the logger before transport.
163
+ */
164
+ function logChainError(tag, error, isBenign, context) {
165
+ if (isBenign) {
166
+ // Demote to a breadcrumb: the SDK's Sentry sink forwards `warn` as a
167
+ // breadcrumb, so this no longer creates its own billed issue while still
168
+ // preserving the trail if a genuine error follows.
169
+ logger_1.Logger.log.warn(tag, { message: errorText(error), ...(context ?? {}) });
170
+ return;
171
+ }
172
+ logger_1.Logger.log.error(tag, error, context);
173
+ }
174
+ //# sourceMappingURL=chain-error.js.map
@@ -12,6 +12,7 @@ export * from './constants/vaults';
12
12
  export * from './constants/swap-router';
13
13
  export * from './helpers/web3';
14
14
  export * from './helpers/vaults';
15
+ export * from './helpers/chain-error';
15
16
  export * from './helpers/core';
16
17
  export * from './helpers/adapters';
17
18
  export * from './helpers/signer';
package/lib/core/index.js CHANGED
@@ -28,6 +28,7 @@ __exportStar(require("./constants/vaults"), exports);
28
28
  __exportStar(require("./constants/swap-router"), exports);
29
29
  __exportStar(require("./helpers/web3"), exports);
30
30
  __exportStar(require("./helpers/vaults"), exports);
31
+ __exportStar(require("./helpers/chain-error"), exports);
31
32
  __exportStar(require("./helpers/core"), exports);
32
33
  __exportStar(require("./helpers/adapters"), exports);
33
34
  __exportStar(require("./helpers/signer"), exports);
@@ -40,7 +40,7 @@ async function fetchInstantRedemptionFee(signer, options) {
40
40
  }
41
41
  }
42
42
  catch (e) {
43
- core_1.Logger.log.error('failed to fetch instant redeem fee', e);
43
+ (0, core_1.logChainError)('failed to fetch instant redeem fee', e, (0, core_1.isExpectedRevertError)(e));
44
44
  throw new Error(`Failed to fetch instant redeem fee for ${target}: ${e instanceof Error ? e.message : 'Unknown error'}`);
45
45
  }
46
46
  }
@@ -83,7 +83,7 @@ async function vaultAllowance(signer, options) {
83
83
  return normalized;
84
84
  }
85
85
  catch (e) {
86
- core_1.Logger.log.error('allowance', e);
86
+ (0, core_1.logChainError)('allowance', e, (0, core_1.isExpectedRevertError)(e));
87
87
  throw new Error(`Failed to fetch allowance: ${e instanceof Error ? e.message : 'Unknown error'}`);
88
88
  }
89
89
  }
@@ -109,7 +109,7 @@ async function sendersWhitelistAddress(signer, options) {
109
109
  return whitelistAddress;
110
110
  }
111
111
  catch (e) {
112
- core_1.Logger.log.error('sendersWhitelistAddress', e);
112
+ (0, core_1.logChainError)('sendersWhitelistAddress', e, (0, core_1.isExpectedRevertError)(e));
113
113
  throw new Error(`Failed to fetch senders whitelist address: ${e instanceof Error ? e.message : 'Unknown error'}`);
114
114
  }
115
115
  }
@@ -138,7 +138,7 @@ async function isWhitelisted(signer, options) {
138
138
  return result;
139
139
  }
140
140
  catch (e) {
141
- core_1.Logger.log.error('isWhitelisted', e);
141
+ (0, core_1.logChainError)('isWhitelisted', e, (0, core_1.isExpectedRevertError)(e));
142
142
  throw new Error(`Failed to check whitelist status: ${e instanceof Error ? e.message : 'Unknown error'}`);
143
143
  }
144
144
  }
@@ -183,7 +183,7 @@ async function getDeposited(signer, options) {
183
183
  return normalized;
184
184
  }
185
185
  catch (e) {
186
- core_1.Logger.log.error('getDeposited', e);
186
+ (0, core_1.logChainError)('getDeposited', e, (0, core_1.isExpectedRevertError)(e));
187
187
  throw new Error(`Failed to fetch deposited amount: ${e instanceof Error ? e.message : 'Unknown error'}`);
188
188
  }
189
189
  }
@@ -232,7 +232,7 @@ async function getRemainingAllocations(signer, options) {
232
232
  return normalized;
233
233
  }
234
234
  catch (e) {
235
- core_1.Logger.log.error('getRemainingAllocations', e);
235
+ (0, core_1.logChainError)('getRemainingAllocations', e, (0, core_1.isExpectedRevertError)(e));
236
236
  throw new Error(`Failed to fetch remaining allocations: ${e instanceof Error ? e.message : 'Unknown error'}`);
237
237
  }
238
238
  }
@@ -263,7 +263,11 @@ async function previewRwaRedemption(signer, options) {
263
263
  return normalized;
264
264
  }
265
265
  catch (e) {
266
- core_1.Logger.log.error('previewRwaRedemption', e, { target, asset, amount });
266
+ (0, core_1.logChainError)('previewRwaRedemption', e, (0, core_1.isExpectedRevertError)(e), {
267
+ target,
268
+ asset,
269
+ amount,
270
+ });
267
271
  throw new Error(`Preview RWA redemption failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
268
272
  }
269
273
  }
@@ -354,7 +358,10 @@ async function previewDeposit(signer, options) {
354
358
  if (e instanceof core_1.AugustValidationError || e instanceof core_1.AugustSDKError) {
355
359
  throw e;
356
360
  }
357
- core_1.Logger.log.error('previewDeposit', e, { vault, amount: amount.toString() });
361
+ (0, core_1.logChainError)('previewDeposit', e, (0, core_1.isExpectedRevertError)(e), {
362
+ vault,
363
+ amount: amount.toString(),
364
+ });
358
365
  throw new core_1.AugustSDKError('UNKNOWN', `previewDeposit failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { vault, amount: amount.toString() } });
359
366
  }
360
367
  }
@@ -410,7 +417,7 @@ async function previewRedeem(signer, options) {
410
417
  if (e instanceof core_1.AugustValidationError || e instanceof core_1.AugustSDKError) {
411
418
  throw e;
412
419
  }
413
- core_1.Logger.log.error('previewRedeem', e, {
420
+ (0, core_1.logChainError)('previewRedeem', e, (0, core_1.isExpectedRevertError)(e), {
414
421
  vault,
415
422
  shares: shares.toString(),
416
423
  });
@@ -480,7 +487,11 @@ async function allowance(signer, options) {
480
487
  if (e instanceof core_1.AugustValidationError || e instanceof core_1.AugustSDKError) {
481
488
  throw e;
482
489
  }
483
- core_1.Logger.log.error('allowance', e, { vault, owner, asset });
490
+ (0, core_1.logChainError)('allowance', e, (0, core_1.isExpectedRevertError)(e), {
491
+ vault,
492
+ owner,
493
+ asset,
494
+ });
484
495
  throw new core_1.AugustSDKError('UNKNOWN', `allowance failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { vault, owner, asset } });
485
496
  }
486
497
  }
@@ -521,7 +532,7 @@ async function balanceOf(signer, options) {
521
532
  if (e instanceof core_1.AugustValidationError || e instanceof core_1.AugustSDKError) {
522
533
  throw e;
523
534
  }
524
- core_1.Logger.log.error('balanceOf', e, { asset, owner });
535
+ (0, core_1.logChainError)('balanceOf', e, (0, core_1.isExpectedRevertError)(e), { asset, owner });
525
536
  throw new core_1.AugustSDKError('UNKNOWN', `balanceOf failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { asset, owner } });
526
537
  }
527
538
  }
@@ -575,7 +586,10 @@ async function maxDeposit(signer, options) {
575
586
  if (e instanceof core_1.AugustValidationError || e instanceof core_1.AugustSDKError) {
576
587
  throw e;
577
588
  }
578
- core_1.Logger.log.error('maxDeposit', e, { vault, receiver });
589
+ (0, core_1.logChainError)('maxDeposit', e, (0, core_1.isExpectedRevertError)(e), {
590
+ vault,
591
+ receiver,
592
+ });
579
593
  throw new core_1.AugustSDKError('UNKNOWN', `maxDeposit failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { vault, receiver } });
580
594
  }
581
595
  }
@@ -297,7 +297,12 @@ async function approveCore(signer, options) {
297
297
  catch (e) {
298
298
  if (e instanceof core_1.AugustSDKError)
299
299
  throw e;
300
- core_1.Logger.log.error('vaultApprove', e, { target, amount });
300
+ // A user declining the approval in their wallet is product behaviour, not
301
+ // an SDK fault — demote it to a breadcrumb so it doesn't bill as an issue.
302
+ (0, core_1.logChainError)('vaultApprove', e, (0, core_1.isUserRejectionError)(e), {
303
+ target,
304
+ amount,
305
+ });
301
306
  throw new core_1.AugustSDKError('UNKNOWN', `Approval failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { target, amount } });
302
307
  }
303
308
  }
@@ -632,7 +637,12 @@ async function vaultDeposit(signer, options) {
632
637
  }
633
638
  if (e instanceof core_1.AugustSDKError)
634
639
  throw e;
635
- core_1.Logger.log.error('deposit', e, { target, amount, depositAsset });
640
+ // User-cancelled deposits are normal; only genuine failures stay at error.
641
+ (0, core_1.logChainError)('deposit', e, (0, core_1.isUserRejectionError)(e), {
642
+ target,
643
+ amount,
644
+ depositAsset,
645
+ });
636
646
  throw new core_1.AugustSDKError('UNKNOWN', `Deposit failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { target, amount, depositAsset } });
637
647
  }
638
648
  }
@@ -780,7 +790,11 @@ async function vaultRequestRedeem(signer, options) {
780
790
  }
781
791
  if (e instanceof core_1.AugustSDKError)
782
792
  throw e;
783
- core_1.Logger.log.error('requestRedeem', e, { target, amount });
793
+ // User-cancelled redeem requests are normal; only genuine failures stay at error.
794
+ (0, core_1.logChainError)('requestRedeem', e, (0, core_1.isUserRejectionError)(e), {
795
+ target,
796
+ amount,
797
+ });
784
798
  throw new core_1.AugustSDKError('UNKNOWN', `Request redeem failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { target, amount } });
785
799
  }
786
800
  }
@@ -823,7 +837,14 @@ async function vaultRedeem(signer, options) {
823
837
  }
824
838
  if (e instanceof core_1.AugustSDKError)
825
839
  throw e;
826
- core_1.Logger.log.error('redeem', e, { target, year, month, day, receiverIndex });
840
+ // User-cancelled redeems are normal; only genuine failures stay at error.
841
+ (0, core_1.logChainError)('redeem', e, (0, core_1.isUserRejectionError)(e), {
842
+ target,
843
+ year,
844
+ month,
845
+ day,
846
+ receiverIndex,
847
+ });
827
848
  throw new core_1.AugustSDKError('UNKNOWN', `Redeem failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { target, year, month, day, receiverIndex } });
828
849
  }
829
850
  }
@@ -914,7 +935,12 @@ async function depositNative(signer, options) {
914
935
  }
915
936
  if (e instanceof core_1.AugustSDKError)
916
937
  throw e;
917
- core_1.Logger.log.error('depositNative', e, { wrapperAddress, receiver, amount });
938
+ // User-cancelled native deposits are normal; only genuine failures stay at error.
939
+ (0, core_1.logChainError)('depositNative', e, (0, core_1.isUserRejectionError)(e), {
940
+ wrapperAddress,
941
+ receiver,
942
+ amount,
943
+ });
918
944
  throw new core_1.AugustSDKError('UNKNOWN', `Deposit native failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { wrapperAddress, receiver, amount } });
919
945
  }
920
946
  }
@@ -988,7 +1014,13 @@ async function rwaRedeemAsset(signer, options) {
988
1014
  }
989
1015
  if (e instanceof core_1.AugustSDKError)
990
1016
  throw e;
991
- core_1.Logger.log.error('rwaRedeemAsset', e, { target, asset, amount, minOut });
1017
+ // User-cancelled RWA redeems are normal; only genuine failures stay at error.
1018
+ (0, core_1.logChainError)('rwaRedeemAsset', e, (0, core_1.isUserRejectionError)(e), {
1019
+ target,
1020
+ asset,
1021
+ amount,
1022
+ minOut,
1023
+ });
992
1024
  throw new core_1.AugustSDKError('UNKNOWN', `RWA redeem failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { target, asset, amount, minOut } });
993
1025
  }
994
1026
  }
package/lib/sdk.d.ts CHANGED
@@ -19011,7 +19011,7 @@ export declare function depositNative(signer: Signer | Wallet, options: INativeD
19011
19011
  * only refresh user identity and the cached API-key hash.
19012
19012
  *
19013
19013
  * @param config - Analytics configuration. Pass `tracesSampleRate` to
19014
- * override the Phase 1/2 default of `1.0`.
19014
+ * override the default of `0.1`.
19015
19015
  * @param environment - Current environment (DEV or PROD).
19016
19016
  * @param walletAddress - Optional wallet address for user identification.
19017
19017
  * @param apiKey - Optional API key (hashed for identification).
@@ -19405,6 +19405,34 @@ export declare function depositNative(signer: Signer | Wallet, options: INativeD
19405
19405
 
19406
19406
  export declare function isEarlierThanNow(startTime: Date): boolean;
19407
19407
 
19408
+ /**
19409
+ * Is this error a routine on-chain read revert rather than a real failure?
19410
+ *
19411
+ * Reading a function a contract doesn't implement, or an address that holds no
19412
+ * (or incompatible) bytecode, reverts — a normal outcome when the SDK probes
19413
+ * heterogeneous vaults. Matches ethers' `CALL_EXCEPTION` code and the
19414
+ * message variants emitted by ethers and viem (`missing revert data`,
19415
+ * `execution reverted`, `call revert exception`, and the bare `reverted`
19416
+ * phrasing such as `the contract function "totalAssets" reverted`).
19417
+ *
19418
+ * Scope note: callers apply this on **read** paths only. A reverted *write*
19419
+ * (a tx that failed on-chain) is a genuine error and is intentionally not
19420
+ * demoted by this predicate's use in `write.actions`.
19421
+ *
19422
+ * @param error - The caught value, of unknown type.
19423
+ * @returns `true` when the failure is an expected/benign contract revert.
19424
+ *
19425
+ * @example
19426
+ * ```ts
19427
+ * try { return await vaultContract.maxDepositAmount(); }
19428
+ * catch (e) {
19429
+ * logChainError('maxDeposit', e, isExpectedRevertError(e));
19430
+ * throw e;
19431
+ * }
19432
+ * ```
19433
+ */
19434
+ export declare function isExpectedRevertError(error: unknown): boolean;
19435
+
19408
19436
  /**
19409
19437
  * Whether a vault's receipt token is hub-only (no cross-chain withdraw).
19410
19438
  * The deposit UI must not offer a spoke share destination for these vaults.
@@ -19676,6 +19704,27 @@ export declare function depositNative(signer: Signer | Wallet, options: INativeD
19676
19704
  ownerAddr: IAddress;
19677
19705
  }
19678
19706
 
19707
+ /**
19708
+ * Is this error a wallet/user rejection of a transaction or signature request?
19709
+ *
19710
+ * Detects ethers v6's `ACTION_REJECTED` code, the EIP-1193 `4001`
19711
+ * ("User rejected the request") code (top-level or nested), and the common
19712
+ * human-readable phrasings as a fallback for providers that omit a code.
19713
+ *
19714
+ * @param error - The caught value, of unknown type.
19715
+ * @returns `true` when the failure was the user declining in their wallet.
19716
+ *
19717
+ * @example
19718
+ * ```ts
19719
+ * try { await vault.deposit(...); }
19720
+ * catch (e) {
19721
+ * if (isUserRejectionError(e)) return; // user cancelled — not an error
19722
+ * throw e;
19723
+ * }
19724
+ * ```
19725
+ */
19726
+ export declare function isUserRejectionError(error: unknown): boolean;
19727
+
19679
19728
  /**
19680
19729
  * Signer Compatibility Helpers
19681
19730
  *
@@ -21072,6 +21121,26 @@ export declare function depositNative(signer: Signer | Wallet, options: INativeD
21072
21121
 
21073
21122
  export declare function loanStateToReadable(loanState: number | bigint): "PREAPPROVED" | "FUNDING_REQUIRED" | "FUNDED" | "ACTIVE" | "CANCELLED" | "MATURED" | "CLOSED";
21074
21123
 
21124
+ /**
21125
+ * Log a caught chain error at the severity its category warrants, without
21126
+ * swallowing it. When `isBenign` is `true` the failure is recorded as a `warn`
21127
+ * (a Sentry breadcrumb that rides along with the next real issue, not a billed
21128
+ * standalone issue); otherwise it is logged at `error` (a Sentry issue). The
21129
+ * caller is still responsible for re-throwing — this only routes telemetry.
21130
+ *
21131
+ * Pass the benign decision explicitly (via {@link isUserRejectionError} or
21132
+ * {@link isExpectedRevertError}) so the call site documents *why* the demotion
21133
+ * is safe and each path opts into only the category that applies to it.
21134
+ *
21135
+ * @param tag - Low-cardinality call-site label (e.g. `'deposit'`), used as the
21136
+ * Sentry breadcrumb/issue grouping key.
21137
+ * @param error - The caught value, of unknown type.
21138
+ * @param isBenign - `true` to demote to `warn`; `false` to keep at `error`.
21139
+ * @param context - Optional structured context attached to the log entry. It is
21140
+ * sanitized by the logger before transport.
21141
+ */
21142
+ export declare function logChainError(tag: string, error: unknown, isBenign: boolean, context?: Record<string, unknown>): void;
21143
+
21075
21144
  export declare const Logger: {
21076
21145
  setLogger: typeof setLogger;
21077
21146
  setStructuredLogger: typeof setStructuredLogger;
@@ -183,12 +183,19 @@ const NEW_DEPOSIT_QUERY_PROPS = `
183
183
  senderAddr
184
184
  receiverAddr
185
185
  `;
186
+ // The `block_number: blockNumber` alias on these new-schema withdrawal prop
187
+ // sets is required, not cosmetic: getWithdrawalRequestsWithStatus prunes by
188
+ // `Number(req.block_number) >= blockCutoff` (per the ISubgraphBase contract).
189
+ // New-schema subgraphs expose the field as `blockNumber`, so without the alias
190
+ // it reads undefined → Number(undefined) is NaN → every request is pruned
191
+ // (AUGUST-6514). Only the REQUESTED set is read today; the processed/withdrawals
192
+ // aliases below are kept for the same type contract so the NaN prune can't recur.
186
193
  const NEW_WITHDRAWALS_REQUESTED_QUERY_PROPS = `
187
194
  id
188
195
  shares
189
196
  receiverAddr
190
197
  holderAddr
191
- blockNumber
198
+ block_number: blockNumber
192
199
  transactionHash_: transactionHash
193
200
  timestamp_: blockTimestamp
194
201
  `;
@@ -196,7 +203,7 @@ const NEW_WITHDRAWALS_PROCESSED_QUERY_PROPS = `
196
203
  id
197
204
  receiverAddr
198
205
  assetsAmount
199
- blockNumber
206
+ block_number: blockNumber
200
207
  transactionHash_: transactionHash
201
208
  timestamp_: blockTimestamp
202
209
  `;
@@ -204,7 +211,7 @@ const NEW_WITHDRAWALS_QUERY_PROPS = `
204
211
  id
205
212
  transactionHash_: transactionHash
206
213
  timestamp_: blockTimestamp
207
- blockNumber
214
+ block_number: blockNumber
208
215
  assets
209
216
  receiver
210
217
  sender
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@augustdigital/sdk",
3
- "version": "8.6.0",
3
+ "version": "8.6.1",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/sdk.d.ts",
6
6
  "keywords": [