@augustdigital/sdk 8.10.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.
@@ -50,10 +50,10 @@ function buildSelfOperationArgs(amount, userAddress) {
50
50
  * @returns Base64-encoded XDR of the unsigned transaction, ready for wallet signing.
51
51
  */
52
52
  async function handleStellarDeposit(params) {
53
- const { contractId, amount, senderAddress, network } = params;
53
+ const { contractId, amount, senderAddress, network, sorobanRpcUrl } = params;
54
54
  validateContractAddress(contractId);
55
55
  validateAccountAddress(senderAddress, 'sender');
56
- const config = (0, soroban_1.resolveNetworkConfig)(network);
56
+ const config = (0, soroban_1.resolveNetworkConfig)(network, sorobanRpcUrl);
57
57
  const args = buildSelfOperationArgs((0, soroban_1.toBigIntAmount)(amount, 'deposit amount'), senderAddress);
58
58
  return (0, soroban_1.buildSorobanTx)(config, senderAddress, contractId, 'deposit', args);
59
59
  }
@@ -67,10 +67,10 @@ async function handleStellarDeposit(params) {
67
67
  * XDR to {@link submitStellarTransaction}.
68
68
  */
69
69
  async function handleStellarRedeem(params) {
70
- const { contractId, shares, receiverAddress, network } = params;
70
+ const { contractId, shares, receiverAddress, network, sorobanRpcUrl } = params;
71
71
  validateContractAddress(contractId);
72
72
  validateAccountAddress(receiverAddress, 'receiver');
73
- const config = (0, soroban_1.resolveNetworkConfig)(network);
73
+ const config = (0, soroban_1.resolveNetworkConfig)(network, sorobanRpcUrl);
74
74
  const args = buildSelfOperationArgs((0, soroban_1.toBigIntAmount)(shares, 'redeem shares'), receiverAddress);
75
75
  return (0, soroban_1.buildSorobanTx)(config, receiverAddress, contractId, 'redeem', args);
76
76
  }
@@ -16,7 +16,7 @@ import type { IStellarNetwork, IStellarUserPosition } from './types';
16
16
  * Falls back to backend `latest_reported_tvl` for total assets, or zero
17
17
  * for total supply, when on-chain queries fail.
18
18
  */
19
- export declare const getStellarVault: (tokenizedVault: ITokenizedVault, _options: IVaultBaseOptions) => Promise<IVault>;
19
+ export declare const getStellarVault: (tokenizedVault: ITokenizedVault, options: IVaultBaseOptions) => Promise<IVault>;
20
20
  /**
21
21
  * Get user position for a Stellar vault.
22
22
  * Queries the vault contract's balance(address) and decimals() functions via Soroban RPC.
@@ -37,6 +37,9 @@ export declare const getStellarVault: (tokenizedVault: ITokenizedVault, _options
37
37
  * @param vaultAddress - Stellar vault contract ID (C… address)
38
38
  * @param walletAddress - User's Stellar account (G… address); validated upfront
39
39
  * @param network - 'mainnet' | 'testnet' (defaults to 'mainnet')
40
+ * @param sorobanRpcUrl - Optional override Soroban RPC endpoint (e.g. a keyed
41
+ * Alchemy URL); becomes the primary with the SDK's public endpoints kept as
42
+ * failover. Omit to use the public endpoints.
40
43
  * @returns The position, or `null` if the balance read failed or was unparseable.
41
44
  * @throws AugustValidationError on an invalid `walletAddress`.
42
45
  * @example
@@ -49,7 +52,7 @@ export declare const getStellarVault: (tokenizedVault: ITokenizedVault, _options
49
52
  * const human = Number(pos.shares) / 10 ** pos.decimals;
50
53
  * ```
51
54
  */
52
- export declare const getStellarUserPosition: (vaultAddress: string, walletAddress: string, network?: IStellarNetwork) => Promise<IStellarUserPosition | null>;
55
+ export declare const getStellarUserPosition: (vaultAddress: string, walletAddress: string, network?: IStellarNetwork, sorobanRpcUrl?: string) => Promise<IStellarUserPosition | null>;
53
56
  /**
54
57
  * Query the vault's `convert_to_shares` to preview how many shares a deposit
55
58
  * amount would yield. Returns the raw share amount as a string, or null on
@@ -58,5 +61,8 @@ export declare const getStellarUserPosition: (vaultAddress: string, walletAddres
58
61
  * @param vaultAddress Stellar contract ID (C… address)
59
62
  * @param rawAmount Deposit amount in the deposit token's smallest unit
60
63
  * @param network 'mainnet' | 'testnet'
64
+ * @param sorobanRpcUrl - Optional override Soroban RPC endpoint (e.g. a keyed
65
+ * Alchemy URL); becomes the primary with the SDK's public endpoints kept as
66
+ * failover. Omit to use the public endpoints.
61
67
  */
62
- export declare const convertToShares: (vaultAddress: string, rawAmount: string, network?: IStellarNetwork) => Promise<string | null>;
68
+ export declare const convertToShares: (vaultAddress: string, rawAmount: string, network?: IStellarNetwork, sorobanRpcUrl?: string) => Promise<string | null>;
@@ -78,7 +78,7 @@ function resolveNetwork(tokenizedVault) {
78
78
  * Falls back to backend `latest_reported_tvl` for total assets, or zero
79
79
  * for total supply, when on-chain queries fail.
80
80
  */
81
- const getStellarVault = async (tokenizedVault, _options) => {
81
+ const getStellarVault = async (tokenizedVault, options) => {
82
82
  const stellarMetadata = tokenizedVault.stellar_vault_metadata;
83
83
  const depositDecimals = stellarMetadata?.deposit_token_decimals ?? constants_1.STELLAR_FALLBACK_DECIMALS;
84
84
  if (stellarMetadata?.deposit_token_decimals == null) {
@@ -90,7 +90,13 @@ const getStellarVault = async (tokenizedVault, _options) => {
90
90
  }
91
91
  // Attempt on-chain queries for total assets, total supply, and share decimals
92
92
  const network = resolveNetwork(tokenizedVault);
93
- const config = (0, soroban_1.resolveNetworkConfig)(network);
93
+ // Only apply the configured RPC override when it matches this vault's network
94
+ // (a testnet override must not be used for a mainnet read). `options` may be
95
+ // omitted by JS callers, so guard it.
96
+ const overrideRpcUrl = options?.stellarRpc?.network === network
97
+ ? options.stellarRpc.rpcUrl
98
+ : undefined;
99
+ const config = (0, soroban_1.resolveNetworkConfig)(network, overrideRpcUrl);
94
100
  const [assetsResult, supplyResult, decimalsResult] = await Promise.all([
95
101
  (0, soroban_1.queryContract)(config, tokenizedVault.address, 'total_assets'),
96
102
  (0, soroban_1.queryContract)(config, tokenizedVault.address, 'total_supply'),
@@ -171,6 +177,9 @@ exports.getStellarVault = getStellarVault;
171
177
  * @param vaultAddress - Stellar vault contract ID (C… address)
172
178
  * @param walletAddress - User's Stellar account (G… address); validated upfront
173
179
  * @param network - 'mainnet' | 'testnet' (defaults to 'mainnet')
180
+ * @param sorobanRpcUrl - Optional override Soroban RPC endpoint (e.g. a keyed
181
+ * Alchemy URL); becomes the primary with the SDK's public endpoints kept as
182
+ * failover. Omit to use the public endpoints.
174
183
  * @returns The position, or `null` if the balance read failed or was unparseable.
175
184
  * @throws AugustValidationError on an invalid `walletAddress`.
176
185
  * @example
@@ -183,8 +192,8 @@ exports.getStellarVault = getStellarVault;
183
192
  * const human = Number(pos.shares) / 10 ** pos.decimals;
184
193
  * ```
185
194
  */
186
- const getStellarUserPosition = async (vaultAddress, walletAddress, network = 'mainnet') => {
187
- const config = (0, soroban_1.resolveNetworkConfig)(network);
195
+ const getStellarUserPosition = async (vaultAddress, walletAddress, network = 'mainnet', sorobanRpcUrl) => {
196
+ const config = (0, soroban_1.resolveNetworkConfig)(network, sorobanRpcUrl);
188
197
  // Validate address upfront — invalid input is a caller bug, not an RPC issue
189
198
  let userAddress;
190
199
  try {
@@ -243,8 +252,11 @@ exports.getStellarUserPosition = getStellarUserPosition;
243
252
  * @param vaultAddress Stellar contract ID (C… address)
244
253
  * @param rawAmount Deposit amount in the deposit token's smallest unit
245
254
  * @param network 'mainnet' | 'testnet'
255
+ * @param sorobanRpcUrl - Optional override Soroban RPC endpoint (e.g. a keyed
256
+ * Alchemy URL); becomes the primary with the SDK's public endpoints kept as
257
+ * failover. Omit to use the public endpoints.
246
258
  */
247
- const convertToShares = async (vaultAddress, rawAmount, network = 'mainnet') => {
259
+ const convertToShares = async (vaultAddress, rawAmount, network = 'mainnet', sorobanRpcUrl) => {
248
260
  // Validate address upfront — invalid input is a caller bug, not an RPC issue
249
261
  try {
250
262
  new stellar_sdk_1.Address(vaultAddress);
@@ -252,7 +264,7 @@ const convertToShares = async (vaultAddress, rawAmount, network = 'mainnet') =>
252
264
  catch (addrErr) {
253
265
  throw new core_1.AugustValidationError('INVALID_ADDRESS', `convertToShares: invalid vaultAddress "${vaultAddress}": ${String(addrErr)}`, { cause: addrErr });
254
266
  }
255
- const config = (0, soroban_1.resolveNetworkConfig)(network);
267
+ const config = (0, soroban_1.resolveNetworkConfig)(network, sorobanRpcUrl);
256
268
  const amountBigInt = (0, soroban_1.toBigIntAmount)(rawAmount, 'rawAmount');
257
269
  const amountScVal = (0, stellar_sdk_1.nativeToScVal)(amountBigInt, { type: 'i128' });
258
270
  const result = await (0, soroban_1.queryContract)(config, vaultAddress, 'convert_to_shares', [amountScVal]);
@@ -36,9 +36,24 @@ export declare const Stellar: {
36
36
  * // Sign xdr with wallet (e.g. Freighter), then submit via adapter.submitTransaction()
37
37
  * ```
38
38
  */
39
- declare class StellarAdapter {
39
+ export declare class StellarAdapter {
40
40
  private _network;
41
- constructor(network?: IStellarNetwork);
41
+ /**
42
+ * Optional consumer-supplied Soroban RPC endpoint (e.g. a keyed Alchemy URL).
43
+ * When set it is the primary, with the SDK's public endpoints as failover.
44
+ */
45
+ private _sorobanRpcUrl?;
46
+ /**
47
+ * @param network - Stellar network to operate on; defaults to `'mainnet'`.
48
+ * @param options - Optional adapter configuration. Its `sorobanRpcUrl` field
49
+ * overrides the Soroban RPC endpoint (e.g. a keyed Alchemy URL): it becomes
50
+ * the primary with the SDK's built-in public endpoints kept behind it as
51
+ * failover, and a per-call `sorobanRpcUrl` (on the deposit/redeem params)
52
+ * takes precedence over this adapter-level one.
53
+ */
54
+ constructor(network?: IStellarNetwork, options?: {
55
+ sorobanRpcUrl?: string;
56
+ });
42
57
  get network(): IStellarNetwork;
43
58
  isStellarAddress(address: string): boolean;
44
59
  getExplorerLink(id: string, type?: 'contract' | 'account' | 'tx'): string;
@@ -41,7 +41,7 @@ var __importStar = (this && this.__importStar) || (function () {
41
41
  };
42
42
  })();
43
43
  Object.defineProperty(exports, "__esModule", { value: true });
44
- exports.Stellar = void 0;
44
+ exports.StellarAdapter = exports.Stellar = void 0;
45
45
  const StellarConstants = __importStar(require("./constants"));
46
46
  const StellarGetters = __importStar(require("./getters"));
47
47
  const StellarActions = __importStar(require("./actions"));
@@ -70,8 +70,22 @@ exports.Stellar = {
70
70
  */
71
71
  class StellarAdapter {
72
72
  _network;
73
- constructor(network = 'mainnet') {
73
+ /**
74
+ * Optional consumer-supplied Soroban RPC endpoint (e.g. a keyed Alchemy URL).
75
+ * When set it is the primary, with the SDK's public endpoints as failover.
76
+ */
77
+ _sorobanRpcUrl;
78
+ /**
79
+ * @param network - Stellar network to operate on; defaults to `'mainnet'`.
80
+ * @param options - Optional adapter configuration. Its `sorobanRpcUrl` field
81
+ * overrides the Soroban RPC endpoint (e.g. a keyed Alchemy URL): it becomes
82
+ * the primary with the SDK's built-in public endpoints kept behind it as
83
+ * failover, and a per-call `sorobanRpcUrl` (on the deposit/redeem params)
84
+ * takes precedence over this adapter-level one.
85
+ */
86
+ constructor(network = 'mainnet', options) {
74
87
  this._network = network;
88
+ this._sorobanRpcUrl = options?.sorobanRpcUrl;
75
89
  }
76
90
  get network() {
77
91
  return this._network;
@@ -90,6 +104,8 @@ class StellarAdapter {
90
104
  return StellarActions.handleStellarDeposit({
91
105
  ...params,
92
106
  network: this._network,
107
+ // A per-call override (params.sorobanRpcUrl) wins over the adapter-level one.
108
+ sorobanRpcUrl: params.sorobanRpcUrl ?? this._sorobanRpcUrl,
93
109
  });
94
110
  }
95
111
  /**
@@ -100,6 +116,8 @@ class StellarAdapter {
100
116
  return StellarActions.handleStellarRedeem({
101
117
  ...params,
102
118
  network: this._network,
119
+ // A per-call override (params.sorobanRpcUrl) wins over the adapter-level one.
120
+ sorobanRpcUrl: params.sorobanRpcUrl ?? this._sorobanRpcUrl,
103
121
  });
104
122
  }
105
123
  /**
@@ -129,7 +147,7 @@ class StellarAdapter {
129
147
  * ```
130
148
  */
131
149
  async submitTransaction(signedXdr) {
132
- return StellarSubmit.submitStellarTransaction(signedXdr, this._network);
150
+ return StellarSubmit.submitStellarTransaction(signedXdr, this._network, this._sorobanRpcUrl);
133
151
  }
134
152
  /**
135
153
  * Get user's vault share balance.
@@ -140,7 +158,7 @@ class StellarAdapter {
140
158
  * a redeem MUST refuse rather than trust it (see AUGUST-6381).
141
159
  */
142
160
  async getUserPosition(vaultAddress, walletAddress) {
143
- return StellarGetters.getStellarUserPosition(vaultAddress, walletAddress, this._network);
161
+ return StellarGetters.getStellarUserPosition(vaultAddress, walletAddress, this._network, this._sorobanRpcUrl);
144
162
  }
145
163
  /**
146
164
  * Preview how many vault shares a deposit amount would yield.
@@ -149,8 +167,9 @@ class StellarAdapter {
149
167
  * @returns Raw share amount as a string, or null if query fails.
150
168
  */
151
169
  async convertToShares(vaultAddress, rawAmount) {
152
- return StellarGetters.convertToShares(vaultAddress, rawAmount, this._network);
170
+ return StellarGetters.convertToShares(vaultAddress, rawAmount, this._network, this._sorobanRpcUrl);
153
171
  }
154
172
  }
173
+ exports.StellarAdapter = StellarAdapter;
155
174
  exports.default = StellarAdapter;
156
175
  //# sourceMappingURL=index.js.map
@@ -8,14 +8,101 @@ import { type xdr, rpc } from '@stellar/stellar-sdk';
8
8
  import type { IStellarNetwork } from './types';
9
9
  /** Resolved network configuration for Soroban RPC calls. */
10
10
  export interface ISorobanNetworkConfig {
11
+ /** Primary endpoint — the first of {@link rpcUrls}; kept for convenience. */
11
12
  rpcUrl: string;
13
+ /** Ordered endpoints for health-gated failover: primary first, fallbacks after. */
14
+ rpcUrls: readonly string[];
12
15
  passphrase: string;
13
16
  }
14
17
  export declare function createServer(rpcUrl: string): rpc.Server;
15
18
  /**
16
- * Resolve RPC URL and network passphrase for a Stellar network.
19
+ * Resolve the ordered RPC endpoints and network passphrase for a Stellar network.
20
+ *
21
+ * When `overrideRpcUrl` is supplied (e.g. a consumer's keyed Alchemy endpoint),
22
+ * it becomes the primary with the built-in public endpoints kept behind it as
23
+ * failover — so opting into a premium provider still degrades to the public
24
+ * network rather than becoming a new single point of failure. The endpoint is
25
+ * injected rather than hardcoded, so the adapter depends on configuration, not a
26
+ * constant.
27
+ */
28
+ export declare function resolveNetworkConfig(network: IStellarNetwork, overrideRpcUrl?: string): ISorobanNetworkConfig;
29
+ /**
30
+ * Clear the health-check cache and any in-flight probes. Test-only seam.
31
+ * @internal
32
+ */
33
+ export declare function resetHealthyServerCache(): void;
34
+ /**
35
+ * Host-only view of an RPC URL, safe for logs. Keyed provider endpoints put the
36
+ * API key in the path (e.g. Alchemy `/v2/<key>`), so logging the raw URL would
37
+ * leak the secret into SDK logs / crash reporters. Returns just protocol+host.
38
+ * @internal
39
+ */
40
+ export declare function redactRpcUrl(url: string): string;
41
+ /**
42
+ * Return a Soroban RPC server backed by a healthy endpoint for `config`. Thin
43
+ * wrapper over {@link resolveHealthyServer} for callers (e.g. submission) that
44
+ * only need the server, not the URL it resolved to.
45
+ *
46
+ * @internal
47
+ */
48
+ export declare function getHealthyServer(config: ISorobanNetworkConfig): Promise<rpc.Server>;
49
+ /**
50
+ * A thrown RPC error is retryable on another endpoint UNLESS it is one of our
51
+ * typed domain errors. Those mark a *deterministic* outcome — a contract revert,
52
+ * an unfunded account, archived state needing restore, a bad input, or a local
53
+ * assembly failure — which reproduces identically on every node, so retrying
54
+ * elsewhere only wastes calls. Any other throw (a transport hang/reset, or a
55
+ * method-specific node error from an endpoint that still answered `getHealth()`)
56
+ * is worth the next endpoint.
57
+ *
58
+ * Exported so an operation with different semantics (e.g. an idempotency-aware
59
+ * submission path) can compose or replace this policy via
60
+ * {@link FailoverOptions.isRetryable} — extension without editing the core.
61
+ * @internal
62
+ */
63
+ export declare function isRetryableRpcError(err: unknown): boolean;
64
+ /**
65
+ * Whether a simulation *error* string is a node/infrastructure failure (which
66
+ * clears on another endpoint) rather than a genuine contract revert (which
67
+ * reproduces on every node). `isSimulationError` alone can't tell these apart —
68
+ * both arrive as a populated `error` field — so callers use this to decide
69
+ * whether to fail over or treat the error as terminal.
70
+ * @internal
71
+ */
72
+ export declare function isRetryableSimulationError(errorText: string): boolean;
73
+ /**
74
+ * Tunables for {@link withEndpointFailover}, injected with sensible defaults so
75
+ * a new operation can vary retry policy or latency budget without editing the
76
+ * failover core (Open/Closed at the call site).
77
+ */
78
+ export interface FailoverOptions {
79
+ /** Which thrown errors get the next endpoint. Defaults to {@link isRetryableRpcError}. */
80
+ isRetryable?: (err: unknown) => boolean;
81
+ /** Per-attempt timeout in ms. Defaults to {@link RPC_OPERATION_TIMEOUT_MS}. */
82
+ timeoutMs?: number;
83
+ }
84
+ /**
85
+ * Run an idempotent, side-effect-free `operation` (a read or a tx build) against
86
+ * a Soroban endpoint, failing over to the remaining endpoints on a retryable
87
+ * failure.
88
+ *
89
+ * {@link getHealthyServer} only proves an endpoint answered `getHealth()`, and
90
+ * caches that choice for {@link HEALTHY_SERVER_TTL_MS} — but the node can still
91
+ * hang or return a method-specific error on the *real* call. So we run the op on
92
+ * the health-gated server first (the fast, cached path) and, on a retryable
93
+ * failure, drop that cached choice and retry the op against every endpoint in
94
+ * priority order. Deterministic contract/domain errors short-circuit (see
95
+ * {@link isRetryableRpcError}); each attempt is time-boxed so a hung node can't
96
+ * stall the loop.
97
+ *
98
+ * The `operation` is a strategy (dependency-injected), so new read/build calls
99
+ * reuse this failover without changing it; `options` keeps retry policy and
100
+ * timeout open for extension. Submission is deliberately NOT routed through here
101
+ * — re-sending a signed transaction across endpoints risks a double-submit, so
102
+ * it needs sequence/DUPLICATE-based idempotency rather than blind retry.
103
+ * @internal
17
104
  */
18
- export declare function resolveNetworkConfig(network: IStellarNetwork): ISorobanNetworkConfig;
105
+ export declare function withEndpointFailover<T>(config: ISorobanNetworkConfig, method: string, operation: (server: rpc.Server) => Promise<T>, options?: FailoverOptions): Promise<T>;
19
106
  /**
20
107
  * Validate and convert a string amount to a non-negative BigInt.
21
108
  * Throws a descriptive error if the value is not a valid non-negative integer string.