@augustdigital/sdk 8.19.0 → 8.20.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.
@@ -1,4 +1,4 @@
1
- export declare const SUI_CHAIN_ID = 101;
1
+ export { SUI_CHAIN_ID } from '../../core/constants/web3';
2
2
  export declare const EMBER_API_BASE_URL = "https://vaults.api.sui-prod.bluefin.io/api/v1/vaults";
3
3
  export declare const EMBER_ENDPOINTS: {
4
4
  readonly VAULTS: "/";
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ALLOWED_SUI_VAULT_ADDRESSES = exports.EMBER_DEFAULTS = exports.EMBER_ENDPOINTS = exports.EMBER_API_BASE_URL = exports.SUI_CHAIN_ID = void 0;
4
- exports.SUI_CHAIN_ID = 101;
4
+ // Canonical definition lives in `core/constants/web3.ts` so `core/` can
5
+ // recognise the Sui chain ID without importing the adapter layer. Re-exported
6
+ // here to keep `adapters/sui/constants` import sites (and the published API)
7
+ // unchanged.
8
+ var web3_1 = require("../../core/constants/web3");
9
+ Object.defineProperty(exports, "SUI_CHAIN_ID", { enumerable: true, get: function () { return web3_1.SUI_CHAIN_ID; } });
5
10
  exports.EMBER_API_BASE_URL = 'https://vaults.api.sui-prod.bluefin.io/api/v1/vaults';
6
11
  exports.EMBER_ENDPOINTS = {
7
12
  VAULTS: '/',
@@ -1,6 +1,13 @@
1
1
  import type * as Sentry from '@sentry/browser';
2
2
  import type { IEnv } from '../../types';
3
3
  import type { IAnalyticsConfig } from './types';
4
+ /**
5
+ * Clear the error dedupe bookkeeping. Test-only — the rate limiter is process
6
+ * lifetime state and would otherwise leak between test cases.
7
+ *
8
+ * @internal
9
+ */
10
+ export declare function resetErrorDedupe(): void;
4
11
  /**
5
12
  * Initialize Sentry with SDK-specific configuration. Idempotent: re-calls
6
13
  * only refresh user identity and the cached API-key hash.
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resetErrorDedupe = resetErrorDedupe;
3
4
  exports.initializeSentry = initializeSentry;
4
5
  exports.updateUser = updateUser;
5
6
  exports.clearUser = clearUser;
@@ -78,6 +79,164 @@ function safeSetUser(user) {
78
79
  // Silently fail - analytics should never break SDK
79
80
  }
80
81
  }
82
+ /**
83
+ * Chains whose errors are dropped before reaching Sentry.
84
+ *
85
+ * Mezo is muted by request: its public dRPC endpoint is unreliable enough that
86
+ * its failures dominated the error stream without ever describing an SDK
87
+ * defect. This is a deliberate blind spot — Mezo bugs (bad vault data, reverts,
88
+ * decode failures) are silenced along with the transport noise. Remove the
89
+ * entry to restore reporting; nothing else needs to change.
90
+ *
91
+ * Keyed by both the `chainIdToTagValue` slug and the numeric chain ID, because
92
+ * the two arrive on different events.
93
+ */
94
+ const MUTED_CHAIN_SLUGS = new Set(['mezo']);
95
+ /** Numeric chain IDs matching {@link MUTED_CHAIN_SLUGS}. */
96
+ const MUTED_CHAIN_IDS = new Set(['31612']);
97
+ /**
98
+ * Substrings that identify an event as Mezo-related regardless of tags.
99
+ *
100
+ * Tag-based matching alone is not enough: `sdk.chain` / `sdk.last_chainId`
101
+ * record the last *instrumented method's* chain, not the chain of the RPC that
102
+ * actually failed — the production Mezo batch-rejection events are tagged
103
+ * `sdk.chain: ethereum` while carrying `"requestUrl": "https://mezo.drpc.org"`
104
+ * in the message. Matching the message body catches those.
105
+ */
106
+ const MUTED_CHAIN_MESSAGE_PATTERNS = [/mezo/i];
107
+ /**
108
+ * Whether an event belongs to a muted chain and must not be sent.
109
+ *
110
+ * Checks, in order: the chain tags, then the event message and every exception
111
+ * value/type in the chain (an RPC failure's identifying detail often lives in a
112
+ * wrapped inner error rather than the top-level message).
113
+ *
114
+ * @param event - The Sentry event about to be sent.
115
+ * @returns `true` when the event should be dropped.
116
+ */
117
+ function isMutedChainEvent(event) {
118
+ const chainTag = event.tags?.['sdk.chain'];
119
+ if (typeof chainTag === 'string' && MUTED_CHAIN_SLUGS.has(chainTag)) {
120
+ return true;
121
+ }
122
+ const chainIdTag = event.tags?.['sdk.last_chainId'];
123
+ if (typeof chainIdTag === 'string' && MUTED_CHAIN_IDS.has(chainIdTag)) {
124
+ return true;
125
+ }
126
+ const haystacks = [];
127
+ if (typeof event.message === 'string')
128
+ haystacks.push(event.message);
129
+ for (const exception of event.exception?.values ?? []) {
130
+ if (typeof exception.value === 'string')
131
+ haystacks.push(exception.value);
132
+ if (typeof exception.type === 'string')
133
+ haystacks.push(exception.type);
134
+ }
135
+ return haystacks.some((text) => MUTED_CHAIN_MESSAGE_PATTERNS.some((pattern) => pattern.test(text)));
136
+ }
137
+ /**
138
+ * Rolling window, in milliseconds, over which identical errors are counted.
139
+ */
140
+ const ERROR_DEDUPE_WINDOW_MS = 60_000;
141
+ /**
142
+ * Maximum captures of one error signature per {@link ERROR_DEDUPE_WINDOW_MS}.
143
+ * Chosen so a genuine, ongoing failure is still visible (and its rate
144
+ * observable via `sdk.suppressed_since_last`) while a hot loop hammering a
145
+ * broken RPC or a 404-per-borrower fan-out cannot bill thousands of identical
146
+ * events. Beyond this, further captures in the window are dropped.
147
+ */
148
+ const ERROR_DEDUPE_MAX_PER_WINDOW = 3;
149
+ /** Hard cap on tracked signatures, so the map cannot grow without bound. */
150
+ const ERROR_DEDUPE_MAX_KEYS = 500;
151
+ /**
152
+ * Per-signature capture bookkeeping for the dedupe window.
153
+ *
154
+ * Deliberately process-global, matching the Sentry client it feeds: the client
155
+ * is a singleton, so every `AugustSDK` instance in the process shares one
156
+ * event stream and one quota. The signature intentionally omits partner/app
157
+ * identity — a host serving several partners reports an identical upstream
158
+ * failure ≤{@link ERROR_DEDUPE_MAX_PER_WINDOW} times per window in aggregate,
159
+ * not per partner, because the failure is one event regardless of how many
160
+ * tenants observed it. Per-tenant attribution comes from the `partner.id` tag
161
+ * on the events that are sent, and `sdk.suppressed_since_last` preserves the
162
+ * true rate. `resetAnalytics()` clears this map for the whole process.
163
+ */
164
+ const errorDedupeState = new Map();
165
+ /**
166
+ * Build a low-cardinality signature for an error.
167
+ *
168
+ * Message tails carry the variable parts (addresses, block ranges, response
169
+ * bodies), so only the leading 120 characters participate — enough to separate
170
+ * distinct failure modes without splitting one failure mode into thousands of
171
+ * unique keys.
172
+ *
173
+ * @param error - The normalized error being captured.
174
+ * @param origin - The `sdk.origin` call-site tag, when present.
175
+ * @returns A stable key identifying this class of failure.
176
+ */
177
+ function errorSignature(error, origin) {
178
+ const tag = typeof origin === 'string' ? origin : '';
179
+ return `${tag}|${error.name}|${(error.message || '').slice(0, 120)}`;
180
+ }
181
+ /**
182
+ * Decide whether an error should be sent to Sentry, applying a per-signature
183
+ * rolling rate limit.
184
+ *
185
+ * Why this exists: the SDK is called from server-rendered pages that fan out
186
+ * per vault and per borrower, so a single upstream condition (a 502 from the
187
+ * backend, an RPC rejecting a batch) reproduces on every iteration of every
188
+ * request. Those are the same event, and paying Sentry per copy buys no extra
189
+ * signal.
190
+ *
191
+ * Side effect: mutates {@link errorDedupeState}.
192
+ *
193
+ * @param error - The normalized error about to be captured.
194
+ * @param origin - The `sdk.origin` call-site tag, when present.
195
+ * @returns `send: false` to drop the event. When `send: true`, `suppressed` is
196
+ * the number of copies dropped since the last event that was sent, and is
197
+ * attached to the outgoing event so the true rate stays recoverable.
198
+ */
199
+ function admitError(error, origin) {
200
+ const key = errorSignature(error, origin);
201
+ const now = Date.now();
202
+ const entry = errorDedupeState.get(key);
203
+ // Touch the key on every access so iteration order is true
204
+ // least-recently-used, not first-inserted: a Map's `set` on an existing key
205
+ // does NOT reorder it, so without the delete a signature that keeps
206
+ // recurring would hold its original position and be evicted ahead of a stale
207
+ // one-shot signature registered after it — silently handing the hot
208
+ // signature a fresh allowance every time it churned out of the map.
209
+ errorDedupeState.delete(key);
210
+ if (!entry || now - entry.windowStart >= ERROR_DEDUPE_WINDOW_MS) {
211
+ // Bound the map: drop the least-recently-used signature before inserting.
212
+ if (errorDedupeState.size >= ERROR_DEDUPE_MAX_KEYS) {
213
+ const oldest = errorDedupeState.keys().next();
214
+ if (!oldest.done)
215
+ errorDedupeState.delete(oldest.value);
216
+ }
217
+ const suppressed = entry?.suppressed ?? 0;
218
+ errorDedupeState.set(key, { windowStart: now, captured: 1, suppressed: 0 });
219
+ return { send: true, suppressed };
220
+ }
221
+ errorDedupeState.set(key, entry);
222
+ if (entry.captured < ERROR_DEDUPE_MAX_PER_WINDOW) {
223
+ // `suppressed` can only be non-zero once the allowance is exhausted, so
224
+ // inside the allowance there is never a carried count to report.
225
+ entry.captured += 1;
226
+ return { send: true, suppressed: 0 };
227
+ }
228
+ entry.suppressed += 1;
229
+ return { send: false, suppressed: entry.suppressed };
230
+ }
231
+ /**
232
+ * Clear the error dedupe bookkeeping. Test-only — the rate limiter is process
233
+ * lifetime state and would otherwise leak between test cases.
234
+ *
235
+ * @internal
236
+ */
237
+ function resetErrorDedupe() {
238
+ errorDedupeState.clear();
239
+ }
81
240
  /**
82
241
  * Build the SDK's internal Sentry sink — the bridge that forwards
83
242
  * `Logger.log.*` output to the resolved Sentry SDK. Errors are captured as
@@ -95,6 +254,12 @@ function createSentrySink() {
95
254
  const sdk = (0, sentry_runtime_1.getSentrySDK)();
96
255
  if (!sdk)
97
256
  return;
257
+ const normalized = error instanceof Error ? error : new Error(String(error));
258
+ // Rate-limit identical failures before touching Sentry at all — the
259
+ // scope work and the event payload are both wasted if it is dropped.
260
+ const admission = admitError(normalized, context?.tag);
261
+ if (!admission.send)
262
+ return;
98
263
  sdk.withScope((scope) => {
99
264
  // `beforeSend` drops any event not tagged as ours; the tag is global
100
265
  // already, but set it on the scope defensively in case a future
@@ -115,7 +280,10 @@ function createSentrySink() {
115
280
  }
116
281
  }
117
282
  }
118
- const normalized = error instanceof Error ? error : new Error(String(error));
283
+ // Preserve the true failure rate even though copies were dropped.
284
+ if (admission.suppressed > 0) {
285
+ scope.setExtra('sdk.suppressed_since_last', admission.suppressed);
286
+ }
119
287
  sdk.captureException(normalized);
120
288
  });
121
289
  }
@@ -243,12 +411,22 @@ function initializeSentry(config, environment, walletAddress, apiKey, appName) {
243
411
  // Local dev/test node leaking into a prod DSN: :8545 is the canonical
244
412
  // Anvil/Hardhat port and is never a production RPC endpoint.
245
413
  /ECONNREFUSED .*:8545/,
414
+ // AugustHistoryUnavailableError is a designed, documented outcome:
415
+ // the getter refuses to return a fabricated number when the indexer
416
+ // has no deposit/withdrawal history. It is thrown to the caller, who
417
+ // renders "temporarily unavailable" — the SDK is behaving correctly,
418
+ // so it is not an SDK issue. Indexer health is monitored separately.
419
+ /transaction history unavailable/i,
246
420
  ],
247
421
  beforeSend(event) {
248
422
  // Only send events tagged as SDK-related
249
423
  if (event.tags?.sdk !== 'august-digital') {
250
424
  return null;
251
425
  }
426
+ // Muted chains (see MUTED_CHAIN_SLUGS) are dropped wholesale.
427
+ if (isMutedChainEvent(event)) {
428
+ return null;
429
+ }
252
430
  return event;
253
431
  },
254
432
  beforeSendTransaction(transaction) {
@@ -383,6 +561,9 @@ function resetAnalytics() {
383
561
  cachedApiKey = undefined;
384
562
  logger_1.Logger.setSentrySink(null);
385
563
  (0, sentry_runtime_1.resetSentryRuntime)();
564
+ // The error rate limiter is process-lifetime state; a reset analytics layer
565
+ // must not inherit a half-full dedupe window from the previous one.
566
+ resetErrorDedupe();
386
567
  }
387
568
  /**
388
569
  * Capture an exception into Sentry with SDK-specific context. Use from
@@ -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.19.0";
6
+ export declare const SDK_VERSION = "8.20.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.19.0';
9
+ exports.SDK_VERSION = '8.20.1';
10
10
  //# sourceMappingURL=version.js.map
@@ -5,6 +5,33 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DEFAULT_FETCH_OPTIONS = exports.WEBSERVER_ENDPOINTS = exports.WEBSERVER_URL = exports.PRICE_SERVER_URL = exports.REQUEST_TIMEOUT_MS = void 0;
7
7
  const ethers_1 = require("ethers");
8
+ /**
9
+ * Build the path segment for a subaccount identifier.
10
+ *
11
+ * Why this exists: subaccounts are not EVM-only. Vault operators and loan
12
+ * borrowers can be Solana base58 keys or Stellar `G…`/`C…` addresses, and the
13
+ * backend keys those records by their native string form. Calling ethers'
14
+ * `getAddress()` on them throws `INVALID_ARGUMENT` before the request is ever
15
+ * made — which is how a Solana borrower turned every
16
+ * `getVaultAllocations` call for a mixed-chain vault into a
17
+ * `TypeError: invalid address` (see `getVaultAllocations:cefi` /
18
+ * `:otc` in Sentry).
19
+ *
20
+ * EVM addresses are still checksummed, because the backend expects the
21
+ * checksummed form for those and case-normalising avoids cache misses.
22
+ * Everything else is passed through URL-encoded so a malformed value can never
23
+ * inject extra path segments.
24
+ *
25
+ * @param subaccount - Subaccount identifier: EVM hex address, Solana base58
26
+ * public key, or Stellar strkey. Not validated beyond the EVM branch — the
27
+ * backend is the authority on whether a non-EVM identifier exists.
28
+ * @returns A single URL path segment safe to interpolate into an endpoint.
29
+ */
30
+ function subaccountSegment(subaccount) {
31
+ return (0, ethers_1.isAddress)(subaccount)
32
+ ? (0, ethers_1.getAddress)(subaccount)
33
+ : encodeURIComponent(subaccount);
34
+ }
8
35
  /**
9
36
  * Request timeout in milliseconds.
10
37
  * Set to 90 seconds to accommodate slow API responses.
@@ -34,22 +61,22 @@ exports.WEBSERVER_ENDPOINTS = {
34
61
  },
35
62
  subaccount: {
36
63
  list: (offset = 0, limit = 100) => `/subaccount?offset=${offset}&limit=${limit}`,
37
- rewards: (subaccount) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/rewards`,
38
- tokens: (subaccount) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/tokens`,
64
+ rewards: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/rewards`,
65
+ tokens: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/tokens`,
39
66
  twap: {
40
- create: (subaccount) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/twap`,
41
- stop: (subaccount, id) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/twap/${id}/stop`,
42
- fills: (subaccount, id) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/twap/${id}/fills`,
67
+ create: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/twap`,
68
+ stop: (subaccount, id) => `/subaccount/${subaccountSegment(subaccount)}/twap/${id}/stop`,
69
+ fills: (subaccount, id) => `/subaccount/${subaccountSegment(subaccount)}/twap/${id}/fills`,
43
70
  },
44
- debank: (subaccount) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/debank`,
45
- health_factor: (subaccount) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/health_factor`,
46
- summary: (subaccount) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/summary`,
47
- batch: (subaccount) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/tx_batcher_integrations`,
48
- loans: (subaccount, side = 'BOTH', active = true) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/loans?side=${side}&active=${active}`,
49
- cefi: (subaccount) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/cefi_position`,
50
- otc_positions: (subaccount) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}/otc_positions`,
71
+ debank: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/debank`,
72
+ health_factor: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/health_factor`,
73
+ summary: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/summary`,
74
+ batch: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/tx_batcher_integrations`,
75
+ loans: (subaccount, side = 'BOTH', active = true) => `/subaccount/${subaccountSegment(subaccount)}/loans?side=${side}&active=${active}`,
76
+ cefi: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/cefi_position`,
77
+ otc_positions: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}/otc_positions`,
51
78
  loanByAddress: (loanAddress, chainId) => `/subaccount/loans/${encodeURIComponent(loanAddress)}?chain_id=${chainId}`,
52
- _: (subaccount) => `/subaccount/${(0, ethers_1.getAddress)(subaccount)}`,
79
+ _: (subaccount) => `/subaccount/${subaccountSegment(subaccount)}`,
53
80
  },
54
81
  transactions: {
55
82
  v2: (subaccount, startTime, endTime) => {
@@ -108,7 +135,7 @@ exports.WEBSERVER_ENDPOINTS = {
108
135
  },
109
136
  prices: (symbol) => `/prices/${symbol}`,
110
137
  metrics: {
111
- pnl: (subaccount, startTime, endTime) => `/metrics/pnl?subaccount_address=${(0, ethers_1.getAddress)(subaccount)}${startTime && endTime ? `&start=${startTime}&end=${endTime}` : ''}`,
138
+ pnl: (subaccount, startTime, endTime) => `/metrics/pnl?subaccount_address=${subaccountSegment(subaccount)}${startTime && endTime ? `&start=${startTime}&end=${endTime}` : ''}`,
112
139
  vaultPerformanceFees: (params) => {
113
140
  const q = new URLSearchParams({
114
141
  vault_address: params.vaultAddress,
@@ -126,7 +153,7 @@ exports.WEBSERVER_ENDPOINTS = {
126
153
  public: {
127
154
  integrations: {
128
155
  morpho: {
129
- apy: (subaccount, vaultAddress) => `/integrations/morpho/apy?subaccount_address=${(0, ethers_1.getAddress)(subaccount)}&vault_address=${(0, ethers_1.getAddress)(vaultAddress)}`,
156
+ apy: (subaccount, vaultAddress) => `/integrations/morpho/apy?subaccount_address=${subaccountSegment(subaccount)}&vault_address=${(0, ethers_1.getAddress)(vaultAddress)}`,
130
157
  },
131
158
  },
132
159
  tokenizedVault: {
@@ -11,6 +11,23 @@ export declare const SPECIAL_CHAINS: {
11
11
  explorer: string;
12
12
  };
13
13
  };
14
+ /**
15
+ * Chain ID used to address Sui vaults.
16
+ *
17
+ * Unlike Solana (`-1`) and Stellar (`-3`), Sui does not use a synthetic
18
+ * negative ID — `101` is the SLIP-44-derived value the Sui adapter has always
19
+ * used. It lives here rather than in `adapters/sui/constants.ts` so that
20
+ * `core/` can recognise it without importing the adapter layer (which would
21
+ * create a `core → adapters` back-edge); the adapter re-exports it.
22
+ */
23
+ export declare const SUI_CHAIN_ID = 101;
24
+ /**
25
+ * Chain IDs the SDK understands that are **not** served by an EVM JSON-RPC
26
+ * provider. Callers may legitimately pass these to vault methods without a
27
+ * matching entry in the `providers` map — the request routes through the
28
+ * Solana / Stellar / Sui adapter instead.
29
+ */
30
+ export declare const NON_EVM_CHAIN_IDS: ReadonlySet<number>;
14
31
  export declare const NATIVE_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
15
32
  /**
16
33
  * Decimal precision of the native gas token on every EVM chain this SDK
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FALLBACK_RPC_URLS = exports.FALLBACK_CHAINID = exports.FALLBACK_DECIMALS = exports.MULTICALL3_VERIFIED_CHAINS = exports.MULTICALL3_ADDRESS = exports.AVAILABLE_CHAINS = exports.NETWORKS = exports.ORACLE_CONTRACTS = exports.MIN_ABIS = exports.EVM_NATIVE_DECIMALS = exports.NATIVE_ADDRESS = exports.SPECIAL_CHAINS = void 0;
3
+ exports.FALLBACK_RPC_URLS = exports.FALLBACK_CHAINID = exports.FALLBACK_DECIMALS = exports.MULTICALL3_VERIFIED_CHAINS = exports.MULTICALL3_ADDRESS = exports.AVAILABLE_CHAINS = exports.NETWORKS = exports.ORACLE_CONTRACTS = exports.MIN_ABIS = exports.EVM_NATIVE_DECIMALS = exports.NATIVE_ADDRESS = exports.NON_EVM_CHAIN_IDS = exports.SUI_CHAIN_ID = exports.SPECIAL_CHAINS = void 0;
4
4
  // Special Chains
5
5
  exports.SPECIAL_CHAINS = {
6
6
  solana: {
@@ -14,6 +14,27 @@ exports.SPECIAL_CHAINS = {
14
14
  explorer: 'https://stellar.expert',
15
15
  },
16
16
  };
17
+ /**
18
+ * Chain ID used to address Sui vaults.
19
+ *
20
+ * Unlike Solana (`-1`) and Stellar (`-3`), Sui does not use a synthetic
21
+ * negative ID — `101` is the SLIP-44-derived value the Sui adapter has always
22
+ * used. It lives here rather than in `adapters/sui/constants.ts` so that
23
+ * `core/` can recognise it without importing the adapter layer (which would
24
+ * create a `core → adapters` back-edge); the adapter re-exports it.
25
+ */
26
+ exports.SUI_CHAIN_ID = 101;
27
+ /**
28
+ * Chain IDs the SDK understands that are **not** served by an EVM JSON-RPC
29
+ * provider. Callers may legitimately pass these to vault methods without a
30
+ * matching entry in the `providers` map — the request routes through the
31
+ * Solana / Stellar / Sui adapter instead.
32
+ */
33
+ exports.NON_EVM_CHAIN_IDS = new Set([
34
+ exports.SPECIAL_CHAINS.solana.chainId,
35
+ exports.SPECIAL_CHAINS.stellar.chainId,
36
+ exports.SUI_CHAIN_ID,
37
+ ]);
17
38
  // General
18
39
  exports.NATIVE_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
19
40
  /**
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Chain-ID validation for public SDK entry points.
3
+ *
4
+ * Two distinct failure modes are covered here, and they are deliberately kept
5
+ * separate because they have different remediations:
6
+ *
7
+ * 1. **Unknown chain ID** — the caller passed an ID the SDK has no concept of
8
+ * (a typo, a stale constant, an off-by-one on a non-EVM synthetic ID). No
9
+ * configuration can make this work; the call site is wrong.
10
+ * 2. **Known EVM chain, no RPC configured** — the ID is valid but the SDK was
11
+ * constructed without a provider for it. Remediated by passing an RPC URL.
12
+ *
13
+ * Both used to be logged-and-ignored, letting execution continue with an
14
+ * `undefined` RPC URL. That produced a cascade of misleading downstream
15
+ * failures (`connect ECONNREFUSED 127.0.0.1:8545` from ethers' localhost
16
+ * default, `missing revert data` from `decimals()` reads issued against the
17
+ * wrong chain, and `TypeError: Cannot read properties of undefined`). Failing
18
+ * fast at the boundary replaces that cascade with one actionable error.
19
+ *
20
+ * @module core/helpers/chain-support
21
+ */
22
+ /**
23
+ * Whether `chainId` is an EVM chain the SDK knows about (i.e. has an entry in
24
+ * {@link NETWORKS}).
25
+ *
26
+ * @param chainId - Numeric chain ID to test.
27
+ * @returns `true` for supported EVM chains, `false` for non-EVM synthetic IDs
28
+ * (Solana, Stellar, Sui) and for anything unrecognised.
29
+ */
30
+ export declare function isEvmChainId(chainId: number): boolean;
31
+ /**
32
+ * Whether `chainId` is recognised by the SDK at all — either a supported EVM
33
+ * chain or one of the non-EVM chain IDs in {@link NON_EVM_CHAIN_IDS}.
34
+ *
35
+ * @param chainId - Numeric chain ID to test.
36
+ * @returns `true` when the ID maps to a chain the SDK can route to.
37
+ */
38
+ export declare function isKnownChainId(chainId: number): boolean;
39
+ /**
40
+ * Throw when `chainId` is not a chain the SDK recognises.
41
+ *
42
+ * Called at public vault entry points so an unroutable ID surfaces as one
43
+ * typed error instead of an opaque downstream RPC failure.
44
+ *
45
+ * @param chainId - Chain ID supplied by the caller. `undefined` is a no-op —
46
+ * omitting the chain is valid and falls back to the SDK's active network.
47
+ * @param providers - The SDK's configured `chainId → RPC URL` map. A chain
48
+ * with a caller-configured provider is routable even if it is missing from
49
+ * {@link NETWORKS} (the SDK ships fallback RPCs/oracles for some EVM chains
50
+ * ahead of adding them to `NETWORKS`), so it is accepted here too.
51
+ * @param method - Public method name, used to make the message actionable.
52
+ * @throws {@link AugustValidationError} with code `INVALID_CHAIN` when the ID
53
+ * is not in {@link NETWORKS} or {@link NON_EVM_CHAIN_IDS}, and has no
54
+ * caller-configured provider.
55
+ * @example
56
+ * ```ts
57
+ * assertKnownChainId(-2, undefined, 'getVault');
58
+ * // AugustValidationError: getVault: unknown chainId -2.
59
+ * ```
60
+ */
61
+ export declare function assertKnownChainId(chainId: number | undefined, providers: Partial<Record<number, string>> | undefined, method: string): void;
62
+ /**
63
+ * Throw when a known EVM `chainId` has no RPC URL configured on the SDK.
64
+ *
65
+ * Non-EVM chain IDs are exempt: Solana, Stellar and Sui vaults route through
66
+ * their adapters and never read from the `providers` map, so a missing entry
67
+ * is expected rather than a misconfiguration.
68
+ *
69
+ * @param chainId - Chain ID supplied by the caller. `undefined` is a no-op.
70
+ * @param providers - The SDK's configured `chainId → RPC URL` map.
71
+ * @param method - Public method name, used to make the message actionable.
72
+ * @throws {@link AugustValidationError} with code `INVALID_CHAIN` when
73
+ * `chainId` is an EVM chain with no configured provider.
74
+ * @example
75
+ * ```ts
76
+ * assertEvmProviderConfigured(143, {}, 'getVault');
77
+ * // AugustValidationError: getVault: no RPC URL configured for chainId 143 (Monad).
78
+ * ```
79
+ */
80
+ export declare function assertEvmProviderConfigured(chainId: number | undefined, providers: Partial<Record<number, string>> | undefined, method: string): void;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ /**
3
+ * Chain-ID validation for public SDK entry points.
4
+ *
5
+ * Two distinct failure modes are covered here, and they are deliberately kept
6
+ * separate because they have different remediations:
7
+ *
8
+ * 1. **Unknown chain ID** — the caller passed an ID the SDK has no concept of
9
+ * (a typo, a stale constant, an off-by-one on a non-EVM synthetic ID). No
10
+ * configuration can make this work; the call site is wrong.
11
+ * 2. **Known EVM chain, no RPC configured** — the ID is valid but the SDK was
12
+ * constructed without a provider for it. Remediated by passing an RPC URL.
13
+ *
14
+ * Both used to be logged-and-ignored, letting execution continue with an
15
+ * `undefined` RPC URL. That produced a cascade of misleading downstream
16
+ * failures (`connect ECONNREFUSED 127.0.0.1:8545` from ethers' localhost
17
+ * default, `missing revert data` from `decimals()` reads issued against the
18
+ * wrong chain, and `TypeError: Cannot read properties of undefined`). Failing
19
+ * fast at the boundary replaces that cascade with one actionable error.
20
+ *
21
+ * @module core/helpers/chain-support
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.isEvmChainId = isEvmChainId;
25
+ exports.isKnownChainId = isKnownChainId;
26
+ exports.assertKnownChainId = assertKnownChainId;
27
+ exports.assertEvmProviderConfigured = assertEvmProviderConfigured;
28
+ const web3_1 = require("../constants/web3");
29
+ const errors_1 = require("../errors");
30
+ /**
31
+ * Whether `chainId` is an EVM chain the SDK knows about (i.e. has an entry in
32
+ * {@link NETWORKS}).
33
+ *
34
+ * @param chainId - Numeric chain ID to test.
35
+ * @returns `true` for supported EVM chains, `false` for non-EVM synthetic IDs
36
+ * (Solana, Stellar, Sui) and for anything unrecognised.
37
+ */
38
+ function isEvmChainId(chainId) {
39
+ return Object.hasOwn(web3_1.NETWORKS, chainId);
40
+ }
41
+ /**
42
+ * Whether `chainId` is recognised by the SDK at all — either a supported EVM
43
+ * chain or one of the non-EVM chain IDs in {@link NON_EVM_CHAIN_IDS}.
44
+ *
45
+ * @param chainId - Numeric chain ID to test.
46
+ * @returns `true` when the ID maps to a chain the SDK can route to.
47
+ */
48
+ function isKnownChainId(chainId) {
49
+ return isEvmChainId(chainId) || web3_1.NON_EVM_CHAIN_IDS.has(chainId);
50
+ }
51
+ /**
52
+ * Throw when `chainId` is not a chain the SDK recognises.
53
+ *
54
+ * Called at public vault entry points so an unroutable ID surfaces as one
55
+ * typed error instead of an opaque downstream RPC failure.
56
+ *
57
+ * @param chainId - Chain ID supplied by the caller. `undefined` is a no-op —
58
+ * omitting the chain is valid and falls back to the SDK's active network.
59
+ * @param providers - The SDK's configured `chainId → RPC URL` map. A chain
60
+ * with a caller-configured provider is routable even if it is missing from
61
+ * {@link NETWORKS} (the SDK ships fallback RPCs/oracles for some EVM chains
62
+ * ahead of adding them to `NETWORKS`), so it is accepted here too.
63
+ * @param method - Public method name, used to make the message actionable.
64
+ * @throws {@link AugustValidationError} with code `INVALID_CHAIN` when the ID
65
+ * is not in {@link NETWORKS} or {@link NON_EVM_CHAIN_IDS}, and has no
66
+ * caller-configured provider.
67
+ * @example
68
+ * ```ts
69
+ * assertKnownChainId(-2, undefined, 'getVault');
70
+ * // AugustValidationError: getVault: unknown chainId -2.
71
+ * ```
72
+ */
73
+ function assertKnownChainId(chainId, providers, method) {
74
+ if (typeof chainId === 'undefined')
75
+ return;
76
+ if (isKnownChainId(chainId))
77
+ return;
78
+ if (providers?.[chainId])
79
+ return;
80
+ throw new errors_1.AugustValidationError('INVALID_CHAIN', `${method}: unknown chainId ${chainId}. ` +
81
+ 'Pass a supported EVM chain ID (see NETWORKS) or a non-EVM chain ID ' +
82
+ '(-1 Solana, -3 Stellar, 101 Sui).', { context: { method, chainId } });
83
+ }
84
+ /**
85
+ * Throw when a known EVM `chainId` has no RPC URL configured on the SDK.
86
+ *
87
+ * Non-EVM chain IDs are exempt: Solana, Stellar and Sui vaults route through
88
+ * their adapters and never read from the `providers` map, so a missing entry
89
+ * is expected rather than a misconfiguration.
90
+ *
91
+ * @param chainId - Chain ID supplied by the caller. `undefined` is a no-op.
92
+ * @param providers - The SDK's configured `chainId → RPC URL` map.
93
+ * @param method - Public method name, used to make the message actionable.
94
+ * @throws {@link AugustValidationError} with code `INVALID_CHAIN` when
95
+ * `chainId` is an EVM chain with no configured provider.
96
+ * @example
97
+ * ```ts
98
+ * assertEvmProviderConfigured(143, {}, 'getVault');
99
+ * // AugustValidationError: getVault: no RPC URL configured for chainId 143 (Monad).
100
+ * ```
101
+ */
102
+ function assertEvmProviderConfigured(chainId, providers, method) {
103
+ if (typeof chainId === 'undefined')
104
+ return;
105
+ if (!isEvmChainId(chainId))
106
+ return;
107
+ if (providers?.[chainId])
108
+ return;
109
+ const name = web3_1.NETWORKS[chainId]?.name;
110
+ throw new errors_1.AugustValidationError('INVALID_CHAIN', `${method}: no RPC URL configured for chainId ${chainId}` +
111
+ `${name ? ` (${name})` : ''}. ` +
112
+ 'Pass one when constructing AugustSDK: ' +
113
+ `new AugustSDK({ providers: { ${chainId}: '<rpc-url>' } }).`, { context: { method, chainId } });
114
+ }
115
+ //# sourceMappingURL=chain-support.js.map
@@ -33,7 +33,26 @@ export declare const determineSecondsPerBlock: (chain: number) => number;
33
33
  * @param chain Chain ID
34
34
  * @returns Block interval for pagination
35
35
  */
36
- export declare const determineBlockSkipInternal: (chain: number) => 1000 | 8000 | 50000;
36
+ export declare const determineBlockSkipInternal: (chain: number) => 1000 | 8000 | 10000;
37
+ /**
38
+ * Maximum JSON-RPC calls ethers may coalesce into a single batched HTTP
39
+ * request for a given endpoint.
40
+ *
41
+ * Providers advertise wildly different batch limits and reject the whole batch
42
+ * — not the excess — when exceeded, so one oversized batch fails every call
43
+ * inside it. dRPC's free tier caps batches at 3, which made every Mezo read
44
+ * fail with `server response 500 … "Batch of more than 3 requests are not
45
+ * allowed"` (the single highest-volume SDK error in production).
46
+ *
47
+ * Detection is host-based rather than chain-based because the limit is a
48
+ * property of the endpoint, not the chain: the same chain served by a
49
+ * self-hosted node has no such cap.
50
+ *
51
+ * @param rpcUrl - The RPC endpoint URL. Unparseable values fall back to the
52
+ * conservative default rather than throwing.
53
+ * @returns Batch size cap to pass to ethers as `batchMaxCount`.
54
+ */
55
+ export declare const determineRpcBatchMaxCount: (rpcUrl: string) => number;
37
56
  /**
38
57
  * Retrieve chain ID from web3 provider.
39
58
  * Handles both initialized and uninitialized provider states.