@augustdigital/sdk 8.9.0 → 8.11.0

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.
@@ -25,6 +25,11 @@ const SENSITIVE_KEYS = [
25
25
  'privateKey',
26
26
  'mnemonic',
27
27
  'seed',
28
+ // RPC endpoints can carry the API key in the path (e.g. Alchemy `/v2/<key>`),
29
+ // so a `sorobanRpcUrl` / `rpcUrl` argument must never reach telemetry. Matched
30
+ // as a substring, so it also covers `sorobanRpcUrls` and snake_case variants.
31
+ 'rpcurl',
32
+ 'rpc_url',
28
33
  ];
29
34
  /** Regex patterns applied in order to scrub secrets from free-form strings. */
30
35
  const STRING_REDACTION_PATTERNS = [
@@ -34,6 +39,9 @@ const STRING_REDACTION_PATTERNS = [
34
39
  '$1[REDACTED]',
35
40
  ],
36
41
  [/(gateway\.thegraph\.com\/api\/)[A-Za-z0-9]{20,}(\/)/g, '$1[REDACTED]$2'],
42
+ // Provider path-embedded API keys (e.g. Alchemy `…/v2/<key>`, Infura `…/v3/<key>`):
43
+ // a 16+ char token right after a `/vN/` segment is a key, not a resource path.
44
+ [/(\/v\d+\/)[A-Za-z0-9_-]{16,}/g, '$1[REDACTED]'],
37
45
  [/eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, '[REDACTED_JWT]'],
38
46
  [/\bnpm_[A-Za-z0-9]{30,}\b/g, '[REDACTED_NPM_TOKEN]'],
39
47
  // Stop value match at & # so adjacent query params aren't swallowed.
@@ -172,8 +180,10 @@ function sanitizeArgs(args) {
172
180
  return;
173
181
  }
174
182
  if (typeof arg === 'string') {
175
- // Truncate long strings
176
- sanitized[key] = arg.length > 100 ? `${arg.substring(0, 100)}...` : arg;
183
+ // Scrub embedded secrets, then truncate long strings.
184
+ const scrubbed = sanitizeString(arg);
185
+ sanitized[key] =
186
+ scrubbed.length > 100 ? `${scrubbed.substring(0, 100)}...` : scrubbed;
177
187
  return;
178
188
  }
179
189
  if (typeof arg === 'number' || typeof arg === 'boolean') {
@@ -214,8 +224,14 @@ function sanitizeObject(obj, depth = 0) {
214
224
  sanitized[key] = '[REDACTED_HEX]';
215
225
  continue;
216
226
  }
227
+ // Scrub embedded secrets (API keys in URL paths/queries, bearer tokens,
228
+ // JWTs, …) from free-form values before truncating — key-name matching
229
+ // alone misses a secret carried inside a value under a benign key.
230
+ const scrubbedValue = sanitizeString(value);
217
231
  sanitized[key] =
218
- value.length > 100 ? `${value.substring(0, 100)}...` : value;
232
+ scrubbedValue.length > 100
233
+ ? `${scrubbedValue.substring(0, 100)}...`
234
+ : scrubbedValue;
219
235
  continue;
220
236
  }
221
237
  if (typeof value === 'number' || typeof value === 'boolean') {
@@ -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.9.0";
6
+ export declare const SDK_VERSION = "8.11.0";
@@ -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.9.0';
9
+ exports.SDK_VERSION = '8.11.0';
10
10
  //# sourceMappingURL=version.js.map
@@ -1,4 +1,4 @@
1
- import type { ISolanaConfig, IAddress, IChainId, IEnv, IProvidersConfig, IWSMonitorHeaders } from '../types';
1
+ import type { ISolanaConfig, IStellarConfig, IAddress, IChainId, IEnv, IProvidersConfig, IWSMonitorHeaders } from '../types';
2
2
  import { type IAnalyticsConfig } from './analytics';
3
3
  import { type IVersionCheckConfig } from './version-check';
4
4
  interface IKeys {
@@ -38,6 +38,7 @@ export interface IAugustBase {
38
38
  appName: string;
39
39
  providers?: IProvidersConfig;
40
40
  solana?: ISolanaConfig;
41
+ stellar?: IStellarConfig;
41
42
  keys: IKeys;
42
43
  monitoring?: IMonitoring;
43
44
  /** Analytics configuration. Enabled by default (opt-out model). */
@@ -45,6 +45,7 @@ export declare function isUserRejectionError(error: unknown): boolean;
45
45
  * ```
46
46
  */
47
47
  export declare function isExpectedRevertError(error: unknown): boolean;
48
+ export declare function isInsufficientFundsError(error: unknown): boolean;
48
49
  /**
49
50
  * Log a caught chain error at the severity its category warrants, without
50
51
  * swallowing it. When `isBenign` is `true` the failure is recorded as a `warn`
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isUserRejectionError = isUserRejectionError;
4
4
  exports.isExpectedRevertError = isExpectedRevertError;
5
+ exports.isInsufficientFundsError = isInsufficientFundsError;
5
6
  exports.logChainError = logChainError;
6
7
  const logger_1 = require("../logger");
7
8
  /**
@@ -21,6 +22,9 @@ const logger_1 = require("../logger");
21
22
  * 2. **Expected on-chain read reverts** — reading a function a vault doesn't
22
23
  * implement, or an address with no/incompatible bytecode, reverts. This is a
23
24
  * routine outcome of probing heterogeneous vaults, not a defect.
25
+ * 3. **Underfunded sender accounts** — the node rejects the transaction because
26
+ * the sender can't cover gas (or, on rollups such as Citrea, the extra L1
27
+ * data-availability fee). This is a "top up your wallet" prompt, not a bug.
24
28
  *
25
29
  * These predicates let call sites demote exactly those cases to `warn` while
26
30
  * leaving genuine failures at `error`. They are intentionally dependency-free
@@ -77,6 +81,39 @@ function errorCodes(error) {
77
81
  }
78
82
  return codes;
79
83
  }
84
+ /**
85
+ * Collect every human-readable message string an error-like value carries,
86
+ * across the nested locations providers stash them in.
87
+ *
88
+ * Why this is separate from {@link errorText}: ethers v6 flattens a rejected
89
+ * `eth_estimateGas`/`eth_call` to a generic *top-level* `message` (e.g.
90
+ * `"missing revert data"`) while preserving the node's original JSON-RPC error
91
+ * one level down at `info.error.message`. The real reason (an insufficient-funds
92
+ * report, say) is therefore invisible to a top-level `.message` read. We scan
93
+ * the top-level message plus `error.message`, `info.error.message`,
94
+ * `cause.message`, and `cause.info.error.message` so a caller catches the reason
95
+ * whether it holds the raw provider error or an SDK error that wrapped it as
96
+ * `cause`.
97
+ *
98
+ * @param error - The caught value, of unknown type.
99
+ * @returns Every non-empty message string found (possibly empty array).
100
+ */
101
+ function nestedMessages(error) {
102
+ const out = [];
103
+ const push = (value) => {
104
+ if (typeof value === 'string' && value.length > 0)
105
+ out.push(value);
106
+ };
107
+ push(errorText(error));
108
+ if (error && typeof error === 'object') {
109
+ const e = error;
110
+ push(e.error?.message);
111
+ push(e.info?.error?.message);
112
+ push(e.cause?.message);
113
+ push(e.cause?.info?.error?.message);
114
+ }
115
+ return out;
116
+ }
80
117
  /**
81
118
  * Is this error a wallet/user rejection of a transaction or signature request?
82
119
  *
@@ -143,6 +180,65 @@ function isExpectedRevertError(error) {
143
180
  msg.includes('call revert exception') ||
144
181
  msg.includes('reverted'));
145
182
  }
183
+ /**
184
+ * Is this error the chain node reporting that the sender can't afford the
185
+ * transaction's gas/fee — i.e. the account needs topping up, not a defect?
186
+ *
187
+ * This is distinct from a contract revert, and easy to misclassify. When the
188
+ * node rejects `eth_estimateGas` for an underfunded account, ethers v6 discards
189
+ * the node's reason and surfaces a generic `CALL_EXCEPTION` / `"missing revert
190
+ * data"` at the top level — which {@link isExpectedRevertError} matches. The
191
+ * real reason survives only in the nested JSON-RPC error, so this predicate
192
+ * scans there (via `nestedMessages`). On a write path, check this **before**
193
+ * {@link isExpectedRevertError}, or a genuine funds shortfall reads as a benign
194
+ * revert.
195
+ *
196
+ * Matching is anchored to the node's pre-execution funds-check phrasings (see
197
+ * `INSUFFICIENT_FUNDS_PHRASES`), each of which carries a `for <purpose>`
198
+ * qualifier — `insufficient funds for gas`, `insufficient funds for transfer`,
199
+ * `not enough funds for L1 fee` (Citrea's data-availability fee), etc. This is
200
+ * deliberately narrower than a bare `"insufficient funds"` substring: a
201
+ * *contract revert reason* that merely contains the word "funds" (e.g.
202
+ * `execution reverted: insufficient funds in pool`) must stay a genuine failure,
203
+ * not be demoted to an ACCOUNT_NOT_FUNDED "top up gas" prompt.
204
+ *
205
+ * @param error - The caught value, of unknown type.
206
+ * @returns `true` when the failure is an unfunded/underfunded sender account.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * try { await vault.requestRedeem(...); }
211
+ * catch (e) {
212
+ * if (isInsufficientFundsError(e))
213
+ * throw new AugustValidationError(
214
+ * 'ACCOUNT_NOT_FUNDED',
215
+ * 'Add gas to continue',
216
+ * { cause: e },
217
+ * );
218
+ * throw e;
219
+ * }
220
+ * ```
221
+ */
222
+ /**
223
+ * The node pre-execution funds-check phrasings {@link isInsufficientFundsError}
224
+ * recognises, lower-cased. Each keeps the `for <purpose>` qualifier so the match
225
+ * is anchored to a funding rejection and can't be tripped by a contract revert
226
+ * reason that merely mentions "funds". `insufficient funds for gas` covers the
227
+ * geth/reth `"... for gas * price + value"` message; `not enough funds for l1
228
+ * fee` is the rollup (Citrea) data-availability-fee shortfall.
229
+ */
230
+ const INSUFFICIENT_FUNDS_PHRASES = [
231
+ 'insufficient funds for gas',
232
+ 'insufficient funds for transfer',
233
+ 'insufficient funds for intrinsic transaction cost',
234
+ 'not enough funds for l1 fee',
235
+ ];
236
+ function isInsufficientFundsError(error) {
237
+ return nestedMessages(error).some((raw) => {
238
+ const msg = raw.toLowerCase();
239
+ return INSUFFICIENT_FUNDS_PHRASES.some((phrase) => msg.includes(phrase));
240
+ });
241
+ }
146
242
  /**
147
243
  * Log a caught chain error at the severity its category warrants, without
148
244
  * swallowing it. When `isBenign` is `true` the failure is recorded as a `warn`
package/lib/main.js CHANGED
@@ -108,7 +108,12 @@ class AugustSDK extends core_1.AugustBase {
108
108
  (0, analytics_1.instrumentClass)(this.solana, () => core_1.SPECIAL_CHAINS.solana.chainId);
109
109
  }
110
110
  this.sui = new sui_1.default();
111
- this.stellar = new stellar_1.default();
111
+ // @stellar: mirror the Solana injection — the consumer may supply a keyed
112
+ // Soroban RPC (e.g. Alchemy) via `baseConfig.stellar`; otherwise the adapter
113
+ // uses its built-in public endpoints. Endpoint is injected, not hardcoded.
114
+ this.stellar = new stellar_1.default(baseConfig.stellar?.network, {
115
+ sorobanRpcUrl: baseConfig.stellar?.rpcUrl,
116
+ });
112
117
  this.vaults = new vaults_1.AugustVaults(baseConfig, this.solana, this.sui);
113
118
  this.subaccounts = new sub_accounts_1.AugustSubAccounts(baseConfig);
114
119
  this.api = new api_1.AugustApi(baseConfig);
@@ -1058,7 +1058,11 @@ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, o
1058
1058
  let balance = (0, core_1.toNormalizedBn)(0);
1059
1059
  if (stellarWallet && (0, utils_3.isStellarAddress)(stellarWallet)) {
1060
1060
  const stellarNetwork = v.stellar_vault_metadata?.network_name ?? 'mainnet';
1061
- const position = await StellarGetters.getStellarUserPosition(v.address, stellarWallet, stellarNetwork);
1061
+ // Only use the configured RPC override for its own network.
1062
+ const stellarOverrideRpcUrl = options.stellarRpc?.network === stellarNetwork
1063
+ ? options.stellarRpc.rpcUrl
1064
+ : undefined;
1065
+ const position = await StellarGetters.getStellarUserPosition(v.address, stellarWallet, stellarNetwork, stellarOverrideRpcUrl);
1062
1066
  if (position && !position.decimalsFromFallback) {
1063
1067
  balance = (0, core_1.toNormalizedBn)(BigInt(position.shares), position.decimals);
1064
1068
  }
@@ -23,6 +23,12 @@ export declare class AugustVaults extends AugustBase {
23
23
  private solanaService;
24
24
  private suiService;
25
25
  private headers;
26
+ /**
27
+ * Consumer-supplied Soroban RPC override for Stellar vault reads, with the
28
+ * network it was configured for so it's only applied to matching-network
29
+ * vaults (see `IVaultBaseOptions.stellarRpc`).
30
+ */
31
+ private _stellarRpc?;
26
32
  constructor(baseConfig: IAugustBase, solana: SolanaAdapter, sui: SuiAdapter);
27
33
  /**
28
34
  * @description deposit underlying token into the specified pool with adapter support. This is for when we cannot pass in a signer in sdk and need to do so as an arg
@@ -74,10 +74,23 @@ class AugustVaults extends core_1.AugustBase {
74
74
  solanaService;
75
75
  suiService;
76
76
  headers = null;
77
+ /**
78
+ * Consumer-supplied Soroban RPC override for Stellar vault reads, with the
79
+ * network it was configured for so it's only applied to matching-network
80
+ * vaults (see `IVaultBaseOptions.stellarRpc`).
81
+ */
82
+ _stellarRpc;
77
83
  constructor(baseConfig, solana, sui) {
78
84
  super(baseConfig);
79
85
  this.solanaService = solana;
80
86
  this.suiService = sui || new sui_1.default();
87
+ // Network defaults to mainnet, matching the StellarAdapter constructor.
88
+ this._stellarRpc = baseConfig.stellar?.rpcUrl
89
+ ? {
90
+ rpcUrl: baseConfig.stellar.rpcUrl,
91
+ network: baseConfig.stellar.network ?? 'mainnet',
92
+ }
93
+ : undefined;
81
94
  this.headers = {
82
95
  'x-environment': this.monitoring?.['x-environment'],
83
96
  'x-user-id': this.monitoring?.['x-user-id'],
@@ -166,6 +179,7 @@ class AugustVaults extends core_1.AugustBase {
166
179
  options: {
167
180
  rpcUrl,
168
181
  solanaService: this.solanaService,
182
+ stellarRpc: this._stellarRpc,
169
183
  env: this.monitoring?.env,
170
184
  augustKey: this.keys?.august,
171
185
  subgraphKey: this.keys?.graph,
@@ -243,6 +257,7 @@ class AugustVaults extends core_1.AugustBase {
243
257
  options: {
244
258
  rpcUrl: this.providers?.[v.chain],
245
259
  solanaService: this.solanaService,
260
+ stellarRpc: this._stellarRpc,
246
261
  env: this.monitoring?.env,
247
262
  augustKey: this.keys?.august,
248
263
  subgraphKey: this.keys?.graph,
@@ -316,6 +331,7 @@ class AugustVaults extends core_1.AugustBase {
316
331
  ? this.providers?.[chainId]
317
332
  : this.activeNetwork?.rpcUrl,
318
333
  solanaService: this.solanaService,
334
+ stellarRpc: this._stellarRpc,
319
335
  env: this.monitoring?.env,
320
336
  augustKey: this.keys?.august,
321
337
  subgraphKey: this.keys?.graph,
@@ -340,6 +356,7 @@ class AugustVaults extends core_1.AugustBase {
340
356
  ? this.providers?.[chainId]
341
357
  : this.activeNetwork?.rpcUrl,
342
358
  solanaService: this.solanaService,
359
+ stellarRpc: this._stellarRpc,
343
360
  env: this.monitoring?.env,
344
361
  augustKey: this.keys?.august,
345
362
  subgraphKey: this.keys?.graph,
@@ -385,6 +402,7 @@ class AugustVaults extends core_1.AugustBase {
385
402
  subgraphKey: this.keys?.graph,
386
403
  chainId: chainId,
387
404
  solanaService: this.solanaService,
405
+ stellarRpc: this._stellarRpc,
388
406
  headers: this.headers,
389
407
  });
390
408
  return vaultResponse;
@@ -416,6 +434,7 @@ class AugustVaults extends core_1.AugustBase {
416
434
  subgraphKey: this.keys?.graph,
417
435
  chainId: chainId,
418
436
  solanaService: this.solanaService,
437
+ stellarRpc: this._stellarRpc,
419
438
  headers: this.headers,
420
439
  });
421
440
  return vaultResponse;
@@ -447,6 +466,7 @@ class AugustVaults extends core_1.AugustBase {
447
466
  ? this.providers?.[chainId]
448
467
  : this.activeNetwork?.rpcUrl,
449
468
  solanaService: this.solanaService,
469
+ stellarRpc: this._stellarRpc,
450
470
  env: this.monitoring?.env,
451
471
  augustKey: this.keys?.august,
452
472
  subgraphKey: this.keys?.graph,
@@ -527,6 +547,7 @@ class AugustVaults extends core_1.AugustBase {
527
547
  ? this.providers?.[chainId]
528
548
  : this.activeNetwork?.rpcUrl,
529
549
  solanaService: this.solanaService,
550
+ stellarRpc: this._stellarRpc,
530
551
  env: this.monitoring?.env,
531
552
  augustKey: this.keys?.august,
532
553
  subgraphKey: this.keys?.graph,
@@ -611,6 +632,7 @@ class AugustVaults extends core_1.AugustBase {
611
632
  ? this.providers?.[chainId]
612
633
  : this.activeNetwork?.rpcUrl,
613
634
  solanaService: this.solanaService,
635
+ stellarRpc: this._stellarRpc,
614
636
  env: this.monitoring?.env,
615
637
  augustKey: this.keys?.august,
616
638
  subgraphKey: this.keys?.graph,
@@ -632,6 +654,7 @@ class AugustVaults extends core_1.AugustBase {
632
654
  options: {
633
655
  rpcUrl: this.providers?.[v.chain],
634
656
  solanaService: this.solanaService,
657
+ stellarRpc: this._stellarRpc,
635
658
  env: this.monitoring?.env,
636
659
  augustKey: this.keys?.august,
637
660
  subgraphKey: this.keys?.graph,
@@ -667,6 +690,7 @@ class AugustVaults extends core_1.AugustBase {
667
690
  options: {
668
691
  rpcUrl: this.providers?.[v.chain],
669
692
  solanaService: this.solanaService,
693
+ stellarRpc: this._stellarRpc,
670
694
  env: this.monitoring?.env,
671
695
  augustKey: this.keys?.august,
672
696
  subgraphKey: this.keys?.graph,
@@ -706,6 +730,10 @@ class AugustVaults extends core_1.AugustBase {
706
730
  chainId: _chainId,
707
731
  type: h.type,
708
732
  transactionHash: h.transactionHash_,
733
+ // Deposit rows on multi-asset (pre-deposit) vaults carry the actual
734
+ // deposited token address; propagate it so consumers render the right
735
+ // asset symbol instead of guessing the vault's first deposit asset.
736
+ assetIn: h.assetIn,
709
737
  }));
710
738
  }
711
739
  // Helper function to fetch history for a vault
@@ -826,6 +854,7 @@ class AugustVaults extends core_1.AugustBase {
826
854
  ? this.providers?.[chainId]
827
855
  : this.requireActiveNetwork().rpcUrl,
828
856
  solanaService: this.solanaService,
857
+ stellarRpc: this._stellarRpc,
829
858
  env: this.monitoring?.env,
830
859
  augustKey: this.keys?.august,
831
860
  subgraphKey: this.keys?.graph,
@@ -851,6 +880,7 @@ class AugustVaults extends core_1.AugustBase {
851
880
  ? this.providers?.[chainId]
852
881
  : this.requireActiveNetwork().rpcUrl,
853
882
  solanaService: this.solanaService,
883
+ stellarRpc: this._stellarRpc,
854
884
  env: this.monitoring?.env,
855
885
  augustKey: this.keys?.august,
856
886
  subgraphKey: this.keys?.graph,
@@ -1033,6 +1063,7 @@ class AugustVaults extends core_1.AugustBase {
1033
1063
  rpcUrl: rpcUrl,
1034
1064
  chainId: resolvedChainId,
1035
1065
  solanaService: this.solanaService,
1066
+ stellarRpc: this._stellarRpc,
1036
1067
  env: this.monitoring?.env,
1037
1068
  augustKey: this.keys?.august,
1038
1069
  subgraphKey: this.keys?.graph,
@@ -1056,6 +1087,7 @@ class AugustVaults extends core_1.AugustBase {
1056
1087
  options: {
1057
1088
  rpcUrl: rpcUrl,
1058
1089
  solanaService: this.solanaService,
1090
+ stellarRpc: this._stellarRpc,
1059
1091
  env: this.monitoring?.env,
1060
1092
  augustKey: this.keys?.august,
1061
1093
  subgraphKey: this.keys?.graph,
@@ -1103,6 +1135,7 @@ class AugustVaults extends core_1.AugustBase {
1103
1135
  ? this.providers?.[chainId]
1104
1136
  : rpcUrl || this.requireActiveNetwork().rpcUrl,
1105
1137
  solanaService: this.solanaService,
1138
+ stellarRpc: this._stellarRpc,
1106
1139
  env: this.monitoring?.env,
1107
1140
  augustKey: this.keys?.august,
1108
1141
  subgraphKey: this.keys?.graph,
@@ -1376,6 +1409,7 @@ class AugustVaults extends core_1.AugustBase {
1376
1409
  options: {
1377
1410
  rpcUrl: chainId ? this.providers?.[chainId] : this.activeNetwork.rpcUrl,
1378
1411
  solanaService: this.solanaService,
1412
+ stellarRpc: this._stellarRpc,
1379
1413
  env: this.monitoring?.env,
1380
1414
  augustKey: this.keys?.august,
1381
1415
  subgraphKey: this.keys?.graph,
@@ -1428,6 +1462,7 @@ class AugustVaults extends core_1.AugustBase {
1428
1462
  options: {
1429
1463
  rpcUrl,
1430
1464
  solanaService: this.solanaService,
1465
+ stellarRpc: this._stellarRpc,
1431
1466
  env: this.monitoring?.env,
1432
1467
  augustKey: this.keys?.august,
1433
1468
  subgraphKey: this.keys?.graph,
@@ -1,5 +1,5 @@
1
1
  import type { Connection } from '@solana/web3.js';
2
- import type { IAddress, IEnv, IWSMonitorHeaders } from '../../types';
2
+ import type { IAddress, IEnv, IStellarNetwork, IWSMonitorHeaders } from '../../types';
3
3
  /**
4
4
  * Structural interface for the Solana adapter surface used by vault operations.
5
5
  * Avoids importing the concrete SolanaAdapter class to prevent circular dependencies.
@@ -40,6 +40,16 @@ export interface IVaultBaseOptions {
40
40
  subgraphKey?: string;
41
41
  chainId?: number;
42
42
  solanaService?: ISolanaService;
43
+ /**
44
+ * Optional Soroban RPC override (e.g. a keyed Alchemy URL) for Stellar vault
45
+ * reads, threaded from the SDK's `stellar` config. Carries the network it was
46
+ * configured for so it is only applied to matching-network vaults — a testnet
47
+ * override must not be used for a mainnet read. Unset → built-in endpoints.
48
+ */
49
+ stellarRpc?: {
50
+ rpcUrl: string;
51
+ network: IStellarNetwork;
52
+ };
43
53
  headers?: IWSMonitorHeaders;
44
54
  /**
45
55
  * Portfolio mode: when true, the per-vault EVM getter does NOT null out a
@@ -185,6 +185,39 @@ async function tryRecoverTxHash(error, signer, wait) {
185
185
  }
186
186
  return txHash;
187
187
  }
188
+ /**
189
+ * Rethrow a caught write-path error as a typed {@link AugustValidationError}
190
+ * when it is the chain node reporting the sender can't afford the transaction's
191
+ * gas/fee (see {@link isInsufficientFundsError}).
192
+ *
193
+ * Why: for an underfunded account ethers surfaces an opaque `CALL_EXCEPTION` /
194
+ * "missing revert data" that gives the caller no stable way to tell "user needs
195
+ * gas" apart from a genuine on-chain failure. Converting it to the
196
+ * `ACCOUNT_NOT_FUNDED` code lets a consuming UI branch on `err.code` (e.g. show
197
+ * a "top up to cover network fees" prompt) instead of string-matching. Every
198
+ * write path shares this so the classification is identical across
199
+ * deposit/redeem/approve rather than drifting per call site.
200
+ *
201
+ * A no-op when `isInsufficient` is `false`, so callers fall through to their
202
+ * existing handling unchanged. The caller passes the already-computed
203
+ * classification (it also feeds the telemetry-demotion decision) so
204
+ * {@link isInsufficientFundsError} runs once per catch, not twice.
205
+ *
206
+ * @param isInsufficient - Result of {@link isInsufficientFundsError} for
207
+ * `error`, computed once by the caller and shared with the benign-log flag.
208
+ * @param operation - Human-readable label for the attempted write (e.g.
209
+ * `'Request redeem'`), used verbatim in the thrown error's message.
210
+ * @param error - The caught value, of unknown type, preserved as the thrown
211
+ * error's `cause`.
212
+ * @param context - Structured context attached to the thrown error for logging.
213
+ * @throws {AugustValidationError} with code `ACCOUNT_NOT_FUNDED` when
214
+ * `isInsufficient` is `true`.
215
+ */
216
+ function throwIfInsufficientFunds(isInsufficient, operation, error, context) {
217
+ if (!isInsufficient)
218
+ return;
219
+ throw new core_1.AugustValidationError('ACCOUNT_NOT_FUNDED', `${operation} failed: insufficient native balance to cover gas/network fees`, { cause: error, context });
220
+ }
188
221
  async function approveCore(signer, options) {
189
222
  const { wallet, target, wait, amount, depositAsset } = options;
190
223
  const [goodWallet, goodPool] = [
@@ -293,7 +326,12 @@ async function approveCore(signer, options) {
293
326
  throw e;
294
327
  // A user declining the approval in their wallet is product behaviour, not
295
328
  // an SDK fault — demote it to a breadcrumb so it doesn't bill as an issue.
296
- (0, core_1.logChainError)('vaultApprove', e, (0, core_1.isUserRejectionError)(e), {
329
+ const insufficientFunds = (0, core_1.isInsufficientFundsError)(e);
330
+ (0, core_1.logChainError)('vaultApprove', e, (0, core_1.isUserRejectionError)(e) || insufficientFunds, {
331
+ target,
332
+ amount,
333
+ });
334
+ throwIfInsufficientFunds(insufficientFunds, 'Approval', e, {
297
335
  target,
298
336
  amount,
299
337
  });
@@ -601,7 +639,13 @@ async function vaultDeposit(signer, options) {
601
639
  if (e instanceof core_1.AugustSDKError)
602
640
  throw e;
603
641
  // User-cancelled deposits are normal; only genuine failures stay at error.
604
- (0, core_1.logChainError)('deposit', e, (0, core_1.isUserRejectionError)(e), {
642
+ const insufficientFunds = (0, core_1.isInsufficientFundsError)(e);
643
+ (0, core_1.logChainError)('deposit', e, (0, core_1.isUserRejectionError)(e) || insufficientFunds, {
644
+ target,
645
+ amount,
646
+ depositAsset,
647
+ });
648
+ throwIfInsufficientFunds(insufficientFunds, 'Deposit', e, {
605
649
  target,
606
650
  amount,
607
651
  depositAsset,
@@ -754,7 +798,12 @@ async function vaultRequestRedeem(signer, options) {
754
798
  if (e instanceof core_1.AugustSDKError)
755
799
  throw e;
756
800
  // User-cancelled redeem requests are normal; only genuine failures stay at error.
757
- (0, core_1.logChainError)('requestRedeem', e, (0, core_1.isUserRejectionError)(e), {
801
+ const insufficientFunds = (0, core_1.isInsufficientFundsError)(e);
802
+ (0, core_1.logChainError)('requestRedeem', e, (0, core_1.isUserRejectionError)(e) || insufficientFunds, {
803
+ target,
804
+ amount,
805
+ });
806
+ throwIfInsufficientFunds(insufficientFunds, 'Request redeem', e, {
758
807
  target,
759
808
  amount,
760
809
  });
@@ -801,7 +850,15 @@ async function vaultRedeem(signer, options) {
801
850
  if (e instanceof core_1.AugustSDKError)
802
851
  throw e;
803
852
  // User-cancelled redeems are normal; only genuine failures stay at error.
804
- (0, core_1.logChainError)('redeem', e, (0, core_1.isUserRejectionError)(e), {
853
+ const insufficientFunds = (0, core_1.isInsufficientFundsError)(e);
854
+ (0, core_1.logChainError)('redeem', e, (0, core_1.isUserRejectionError)(e) || insufficientFunds, {
855
+ target,
856
+ year,
857
+ month,
858
+ day,
859
+ receiverIndex,
860
+ });
861
+ throwIfInsufficientFunds(insufficientFunds, 'Redeem', e, {
805
862
  target,
806
863
  year,
807
864
  month,
@@ -899,7 +956,13 @@ async function depositNative(signer, options) {
899
956
  if (e instanceof core_1.AugustSDKError)
900
957
  throw e;
901
958
  // User-cancelled native deposits are normal; only genuine failures stay at error.
902
- (0, core_1.logChainError)('depositNative', e, (0, core_1.isUserRejectionError)(e), {
959
+ const insufficientFunds = (0, core_1.isInsufficientFundsError)(e);
960
+ (0, core_1.logChainError)('depositNative', e, (0, core_1.isUserRejectionError)(e) || insufficientFunds, {
961
+ wrapperAddress,
962
+ receiver,
963
+ amount,
964
+ });
965
+ throwIfInsufficientFunds(insufficientFunds, 'Deposit native', e, {
903
966
  wrapperAddress,
904
967
  receiver,
905
968
  amount,
@@ -978,7 +1041,14 @@ async function rwaRedeemAsset(signer, options) {
978
1041
  if (e instanceof core_1.AugustSDKError)
979
1042
  throw e;
980
1043
  // User-cancelled RWA redeems are normal; only genuine failures stay at error.
981
- (0, core_1.logChainError)('rwaRedeemAsset', e, (0, core_1.isUserRejectionError)(e), {
1044
+ const insufficientFunds = (0, core_1.isInsufficientFundsError)(e);
1045
+ (0, core_1.logChainError)('rwaRedeemAsset', e, (0, core_1.isUserRejectionError)(e) || insufficientFunds, {
1046
+ target,
1047
+ asset,
1048
+ amount,
1049
+ minOut,
1050
+ });
1051
+ throwIfInsufficientFunds(insufficientFunds, 'RWA redeem', e, {
982
1052
  target,
983
1053
  asset,
984
1054
  amount,