@trustware/sdk-staging 1.1.10-staging.0 → 1.1.11-staging.2

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.
Files changed (38) hide show
  1. package/README.md +54 -21
  2. package/dist/constants.cjs +1 -1
  3. package/dist/constants.mjs +1 -1
  4. package/dist/{core-BPLiD1UG.d.ts → core-B1WCJfDI.d.ts} +15 -2
  5. package/dist/{core-B7Ez-5-r.d.cts → core-CeNTuqPz.d.cts} +15 -2
  6. package/dist/core.cjs +365 -279
  7. package/dist/core.cjs.map +1 -1
  8. package/dist/core.d.cts +2 -2
  9. package/dist/core.d.ts +2 -2
  10. package/dist/core.mjs +363 -278
  11. package/dist/core.mjs.map +1 -1
  12. package/dist/index.cjs +375 -170
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +11 -5
  15. package/dist/index.d.ts +11 -5
  16. package/dist/index.mjs +373 -169
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/{manager-B6NNV-E_.d.ts → manager-Bw8b1wSD.d.ts} +136 -21
  19. package/dist/{manager-BwjyFjjz.d.cts → manager-DNGk1YkR.d.cts} +136 -21
  20. package/dist/{metadata-BiTMMvlY.d.ts → metadata-C__KMeuz.d.ts} +1 -1
  21. package/dist/{metadata-HCflXcsF.d.cts → metadata-Dk4R8_-T.d.cts} +1 -1
  22. package/dist/smart-account.cjs +49 -44
  23. package/dist/smart-account.cjs.map +1 -1
  24. package/dist/smart-account.d.cts +17 -12
  25. package/dist/smart-account.d.ts +17 -12
  26. package/dist/smart-account.mjs +49 -44
  27. package/dist/smart-account.mjs.map +1 -1
  28. package/dist/wallet.cjs +54 -39
  29. package/dist/wallet.cjs.map +1 -1
  30. package/dist/wallet.d.cts +3 -3
  31. package/dist/wallet.d.ts +3 -3
  32. package/dist/wallet.mjs +54 -39
  33. package/dist/wallet.mjs.map +1 -1
  34. package/dist/widget.cjs +369 -170
  35. package/dist/widget.cjs.map +1 -1
  36. package/dist/widget.mjs +369 -169
  37. package/dist/widget.mjs.map +1 -1
  38. package/package.json +73 -5
@@ -197,6 +197,13 @@ type BuildRouteBody = {
197
197
  postHook?: PostHookRequest;
198
198
  };
199
199
  };
200
+ /**
201
+ * Validates a `BuildRouteBody.hooks` value before it's sent, so a malformed
202
+ * postHook fails fast client-side instead of round-tripping to the backend.
203
+ * A no-op when `hooks`/`hooks.postHook` is omitted. Exported so callers can
204
+ * validate a postHook ahead of time (e.g. in their own form validation).
205
+ */
206
+ declare function assertValidPostHook(hooks: BuildRouteBody["hooks"]): void;
200
207
  type TxRequest = {
201
208
  to?: string;
202
209
  target?: string;
@@ -236,7 +243,36 @@ declare function submitReceipt(intentId: string, txHash: string, sponsorshipRequ
236
243
  * executes from a smart account, so the backend records the tx against the
237
244
  * address the user actually connects with. */
238
245
  eoaAddress?: string): Promise<any>;
246
+ /**
247
+ * Reports the submitted tx hash for one execution step of a multi-step
248
+ * route — approve steps only; the main tx keeps going through
249
+ * `submitReceipt`. `stepIndex` is the step's position in
250
+ * `route.execution.approvals` (the backend seeds its step plan in the same
251
+ * order). Best-effort telemetry: callers fire-and-forget so a failed report
252
+ * never blocks the payment flow, but a successful one lets the backend tell
253
+ * "approve landed, main never followed" apart from an intent the user
254
+ * abandoned before signing anything.
255
+ */
256
+ declare function submitStepReceipt(intentId: string, stepIndex: number, txHash: string): Promise<any>;
257
+ /**
258
+ * Fetches the current status for a route intent.
259
+ *
260
+ * Response contract:
261
+ * - 404 (throws) → intent doesn't exist. Stop polling.
262
+ * - 200 status: "pending" → intent exists, no receipt yet. Keep polling.
263
+ * - 200 status: "submitted" → receipt in, tx in flight. Keep polling.
264
+ * - 200 status: "bridging" → cross-chain leg in progress. Keep polling.
265
+ * - 200 status: "success"/"failed" → terminal. Stop polling.
266
+ *
267
+ * A "pending" payload is a stub ({intent_id, status, intent_status,
268
+ * create_date}) — the full Transaction fields appear once a receipt lands.
269
+ */
239
270
  declare function getStatus(intentId: string): Promise<Transaction>;
271
+ /**
272
+ * Polls intent status until terminal ("success"/"failed") or timeout.
273
+ * Non-terminal statuses ("pending", "submitted", "bridging") keep the loop
274
+ * going; a 404 (unknown intent) throws out of the loop — don't retry it.
275
+ */
240
276
  declare function pollStatus(intentId: string, { intervalMs, timeoutMs }?: {
241
277
  intervalMs?: number | undefined;
242
278
  timeoutMs?: number | undefined;
@@ -263,31 +299,90 @@ type RouteIntent = {
263
299
  fromAmountWei: string | number;
264
300
  quoteToAmountWei: string | number;
265
301
  minToAmountWei: string | number;
302
+ /** Trustware's own request ID (a UUID) for the call that created this
303
+ * intent — the same value the response carried in `X-Request-Id`, so it is
304
+ * the ID to quote in a Trustware support request. Wire field:
305
+ * `request_id`. Absent on intents created before it was split from
306
+ * `providerRequestId`. */
266
307
  requestId?: string;
308
+ /** The routing provider's own correlation ID: Squid `requestId`, LiFi/Relay
309
+ * route id, or Khalani's `{quoteId,routeId}` composite — the ID *that
310
+ * provider's* status API and support recognise. Wire field:
311
+ * `provider_request_id`. Absent when the winning provider returned none. */
312
+ providerRequestId?: string;
267
313
  routeRaw?: unknown;
268
314
  status: "created" | "submitted" | "bridging" | "success" | "failed";
269
315
  createdDate: Date | string;
270
316
  updatedDate: Date | string;
271
317
  };
318
+ /**
319
+ * A status payload from `getStatus`/`pollStatus`.
320
+ *
321
+ * Every field except `status` is optional, because a `"pending"` payload is a
322
+ * stub — the intent exists but no receipt has landed, so there is no
323
+ * transaction row to describe yet. Check `status` before reading anything
324
+ * else.
325
+ *
326
+ * The wire is snake_case (`source_tx_hash`, `intent_id`, ...) while this type
327
+ * is camelCase. `getStatus` maps one onto the other (see
328
+ * `normalizeStatusPayload` in core/routes.ts), so anything reaching you through
329
+ * `getStatus`/`pollStatus`/`runTopUp` has the camelCase names populated.
330
+ *
331
+ * The raw snake_case keys are still present **at runtime** — dropping them
332
+ * would break callers that already read them — but they are deliberately not
333
+ * declared here, because camelCase is the supported spelling and the raw ones
334
+ * are only kept for back-compat. Reading `tx.source_tx_hash` therefore needs a
335
+ * cast, and new code should not: use `tx.sourceTxHash`.
336
+ *
337
+ * `origin_eoa` and `landed_amount_verified` are the exception — the wire has no
338
+ * camelCase spelling for those two, so they are declared as-is below.
339
+ */
272
340
  type Transaction = {
273
- id: string;
274
- intentId: string;
275
- fromAddress: string;
276
- toAddress: string;
341
+ id?: string;
342
+ intentId?: string;
343
+ fromAddress?: string;
344
+ toAddress?: string;
277
345
  /** Connected wallet (EOA) that originated the payment when the sender is a
278
346
  * smart account. Wire field: `origin_eoa`. */
279
347
  origin_eoa?: string;
280
- fromChainId: string | number;
281
- toChainId: string | number;
282
- sourceTxHash: string;
283
- destTxHash: string;
284
- requestId: string;
285
- transactionRequest: unknown;
286
- status: "submitted" | "bridging" | "success" | "failed";
348
+ fromChainId?: string | number;
349
+ toChainId?: string | number;
350
+ sourceTxHash?: string;
351
+ destTxHash?: string;
352
+ /** Trustware's own request ID (a UUID), copied from the intent. Wire field:
353
+ * `request_id`. Optional: absent on a "pending" payload, and on rows
354
+ * created before it was split from `providerRequestId`. */
355
+ requestId?: string;
356
+ /** The routing provider's own correlation ID, copied from the intent — what
357
+ * that provider's status API polls with. Wire field:
358
+ * `provider_request_id`. See RouteIntent.providerRequestId. */
359
+ providerRequestId?: string;
360
+ transactionRequest?: unknown;
361
+ /**
362
+ * "pending" means the intent exists but no receipt has been submitted yet —
363
+ * the backend returns it (HTTP 200) instead of the old 404 so pollers can
364
+ * tell "not ready yet, keep polling" apart from "unknown intent" (a real
365
+ * 404, which means stop polling). A pending response carries only
366
+ * `intent_id`, `status`, `intent_status`, `create_date` and — once the
367
+ * intent has an execution step plan — `steps`; every other field here is
368
+ * absent until a receipt lands.
369
+ */
370
+ status: "pending" | "submitted" | "bridging" | "success" | "failed";
287
371
  statusRaw?: unknown;
288
372
  routePath?: unknown;
289
373
  routeStatus?: unknown;
374
+ /**
375
+ * Exact once landed_amount_verified is true; otherwise a pre-trade quote
376
+ * estimate copied in when the transaction was submitted — neither Squid's
377
+ * nor LiFi's status API carries a real landed-amount field, so the
378
+ * backend confirms it by reading the destination chain directly instead
379
+ * (see BVT-246). Check `landed_amount_verified` before auto-continuing
380
+ * on this value.
381
+ */
290
382
  toAmountWei?: string | number;
383
+ /** True once toAmountWei has been confirmed on-chain rather than being the
384
+ * pre-trade quote estimate. Wire field: `landed_amount_verified`. */
385
+ landed_amount_verified?: boolean;
291
386
  fromChainBlock: number;
292
387
  toChainBlock: number;
293
388
  fromChainTxUrl?: string;
@@ -410,6 +505,11 @@ type RoutePlan = {
410
505
  };
411
506
  steps?: unknown[];
412
507
  provider?: string;
508
+ /** The winning provider's own correlation ID, not Trustware's — it is
509
+ * absent when that provider issued none, and it is what lands on the
510
+ * intent as `providerRequestId`. Trustware's request ID for the same call
511
+ * comes back in the `X-Request-Id` response header (the provider's is also
512
+ * mirrored there as `X-Provider-Request-Id`). */
413
513
  requestId?: string;
414
514
  reliabilityScore?: number;
415
515
  diagnostics?: {
@@ -628,13 +728,14 @@ type RateLimitInfo = {
628
728
  /** Seconds until rate limit resets (only present on 429 responses) */
629
729
  retryAfter?: number;
630
730
  };
731
+ /**
732
+ * Rate limit *observability*. Retry timing itself is not configurable: the
733
+ * limit is enforced server-side per API key, so the only retry schedule that
734
+ * can actually succeed is the one the server dictates through Retry-After.
735
+ * Tuning it client-side could only make requests fail sooner or hammer a
736
+ * limit that is already closed — see RETRY_POLICY.
737
+ */
631
738
  type RetryConfig = {
632
- /** Enable automatic retry on 429 responses (default: true). Note: This does NOT disable backend rate limits, only client-side retry behavior. */
633
- autoRetry?: boolean;
634
- /** Maximum number of retries on 429 (default: 3) */
635
- maxRetries?: number;
636
- /** Base delay in ms for exponential backoff (default: 1000) */
637
- baseDelayMs?: number;
638
739
  /** Callback when rate limit info is received from server */
639
740
  onRateLimitInfo?: (info: RateLimitInfo) => void;
640
741
  /** Callback when rate limit is hit (429 received) */
@@ -645,15 +746,29 @@ type RetryConfig = {
645
746
  approachingThreshold?: number;
646
747
  };
647
748
  type ResolvedRetryConfig = {
648
- autoRetry: boolean;
649
- maxRetries: number;
650
- baseDelayMs: number;
651
749
  approachingThreshold: number;
652
750
  onRateLimitInfo?: (info: RateLimitInfo) => void;
653
751
  onRateLimited?: (info: RateLimitInfo, retryCount: number) => void;
654
752
  onRateLimitApproaching?: (info: RateLimitInfo, threshold: number) => void;
655
753
  };
656
754
  declare const DEFAULT_RETRY_CONFIG: ResolvedRetryConfig;
755
+ /**
756
+ * The one retry number the SDK chooses for itself: the total time it will
757
+ * spend waiting out rate limits before handing the caller a RateLimitError.
758
+ *
759
+ * Everything else — how long to wait, and so whether to retry at all — is read
760
+ * off the response. The limit is per API key and settable per key server-side
761
+ * (rate_limit_per_min), and the backend reports the state of *that* key on
762
+ * every response: X-RateLimit-Limit / -Remaining / -Reset, plus Retry-After on
763
+ * a 429. A schedule hardcoded here could only disagree with it.
764
+ *
765
+ * This budget stays client-side because the server cannot know it: it is a
766
+ * property of the caller, not of the key. A payment widget cannot sit on a
767
+ * spinner for the minute a fresh window might be away, so past the budget the
768
+ * SDK stops and reports the wait (retriesExhausted: false, retryAfter set) for
769
+ * the app to render.
770
+ */
771
+ declare const RATE_LIMIT_WAIT_BUDGET_MS = 10000;
657
772
  declare const DEFAULT_FEATURE_FLAGS: ResolvedFeatureFlags;
658
773
 
659
774
  type WagmiConnector = {
@@ -793,4 +908,4 @@ declare function useWalletInfo(wagmi?: WagmiBridge): {
793
908
  };
794
909
  declare function useWalletExternalDisconnect(cb: () => void): void;
795
910
 
796
- export { type WalletConnectConfig as $, type ResolvedWalletConnectConfig as A, type BuildRouteBody as B, type RetryConfig as C, type DetectedWallet as D, type EIP1193 as E, type FeatureFlags as F, type RouteApproval as G, type RouteEstimate as H, IdentityStore as I, type RouteIntent as J, type RouteParams as K, type RoutePlan as L, type RouteSponsorship as M, type RoutesConfig as N, type SimpleWalletInterface as O, type PostHookRequest as P, type SponsorshipApproval as Q, type RateLimitInfo as R, type SolanaProviderLike as S, type TrustwareConfigOptions as T, type SwapTokenRef as U, type TrustwareTheme as V, type WalletMeta as W, type TrustwareWidgetMessages as X, type WalletAddressResolution as Y, type WalletAddressSource as Z, type WalletCategory as _, type WalletId as a, type WalletEcosystem as a0, type WalletIdentity as a1, type WalletIdentityAddress as a2, type WalletIdentityChainLike as a3, buildWalletIdentityAddress as a4, createWalletIdentity as a5, resolveWalletAddressForChain as a6, upsertWalletIdentityAddress as a7, buildRoute as a8, buildDepositAddress as a9, submitReceipt as aa, getStatus as ab, pollStatus as ac, type SolanaWalletInterface as b, type WagmiBridge as c, type WagmiConnector as d, type WalletSnapshot as e, useWalletExternalDisconnect as f, useWalletInfo as g, useWireDetectionIntoManager as h, type WalletInterFaceAPI as i, TrustwareError as j, type Transaction as k, type TrustwareEvent as l, type BuildRouteResponse as m, type BuildRouteResult as n, DEFAULT_AUTO_DETECT_PROVIDER as o, DEFAULT_FEATURE_FLAGS as p, DEFAULT_MESSAGES as q, DEFAULT_RETRY_CONFIG as r, DEFAULT_SLIPPAGE as s, type EIP6963ProviderDetail as t, useWalletConnectConnect as u, type EvmWalletInterface as v, walletManager as w, type ResolvedFeatureFlags as x, type ResolvedRetryConfig as y, type ResolvedTrustwareConfig as z };
911
+ export { type WalletCategory as $, type ResolvedTrustwareConfig as A, type BuildRouteBody as B, type ResolvedWalletConnectConfig as C, type DetectedWallet as D, type EIP1193 as E, type FeatureFlags as F, type RetryConfig as G, type RouteApproval as H, IdentityStore as I, type RouteEstimate as J, type RouteIntent as K, type RouteParams as L, type RoutePlan as M, type RouteSponsorship as N, type RoutesConfig as O, type PostHookRequest as P, type SimpleWalletInterface as Q, type RateLimitInfo as R, type SolanaProviderLike as S, type TrustwareConfigOptions as T, type SponsorshipApproval as U, type SwapTokenRef as V, type WalletMeta as W, type TrustwareTheme as X, type TrustwareWidgetMessages as Y, type WalletAddressResolution as Z, type WalletAddressSource as _, type WalletId as a, type WalletConnectConfig as a0, type WalletEcosystem as a1, type WalletIdentity as a2, type WalletIdentityAddress as a3, type WalletIdentityChainLike as a4, assertValidPostHook as a5, buildWalletIdentityAddress as a6, createWalletIdentity as a7, resolveWalletAddressForChain as a8, upsertWalletIdentityAddress as a9, buildRoute as aa, buildDepositAddress as ab, submitReceipt as ac, submitStepReceipt as ad, getStatus as ae, pollStatus as af, type SolanaWalletInterface as b, type WagmiBridge as c, type WagmiConnector as d, type WalletSnapshot as e, useWalletExternalDisconnect as f, useWalletInfo as g, useWireDetectionIntoManager as h, type WalletInterFaceAPI as i, TrustwareError as j, type Transaction as k, type TrustwareEvent as l, type BuildRouteResponse as m, type BuildRouteResult as n, DEFAULT_AUTO_DETECT_PROVIDER as o, DEFAULT_FEATURE_FLAGS as p, DEFAULT_MESSAGES as q, DEFAULT_RETRY_CONFIG as r, DEFAULT_SLIPPAGE as s, type EIP6963ProviderDetail as t, useWalletConnectConnect as u, type EvmWalletInterface as v, walletManager as w, RATE_LIMIT_WAIT_BUDGET_MS as x, type ResolvedFeatureFlags as y, type ResolvedRetryConfig as z };
@@ -197,6 +197,13 @@ type BuildRouteBody = {
197
197
  postHook?: PostHookRequest;
198
198
  };
199
199
  };
200
+ /**
201
+ * Validates a `BuildRouteBody.hooks` value before it's sent, so a malformed
202
+ * postHook fails fast client-side instead of round-tripping to the backend.
203
+ * A no-op when `hooks`/`hooks.postHook` is omitted. Exported so callers can
204
+ * validate a postHook ahead of time (e.g. in their own form validation).
205
+ */
206
+ declare function assertValidPostHook(hooks: BuildRouteBody["hooks"]): void;
200
207
  type TxRequest = {
201
208
  to?: string;
202
209
  target?: string;
@@ -236,7 +243,36 @@ declare function submitReceipt(intentId: string, txHash: string, sponsorshipRequ
236
243
  * executes from a smart account, so the backend records the tx against the
237
244
  * address the user actually connects with. */
238
245
  eoaAddress?: string): Promise<any>;
246
+ /**
247
+ * Reports the submitted tx hash for one execution step of a multi-step
248
+ * route — approve steps only; the main tx keeps going through
249
+ * `submitReceipt`. `stepIndex` is the step's position in
250
+ * `route.execution.approvals` (the backend seeds its step plan in the same
251
+ * order). Best-effort telemetry: callers fire-and-forget so a failed report
252
+ * never blocks the payment flow, but a successful one lets the backend tell
253
+ * "approve landed, main never followed" apart from an intent the user
254
+ * abandoned before signing anything.
255
+ */
256
+ declare function submitStepReceipt(intentId: string, stepIndex: number, txHash: string): Promise<any>;
257
+ /**
258
+ * Fetches the current status for a route intent.
259
+ *
260
+ * Response contract:
261
+ * - 404 (throws) → intent doesn't exist. Stop polling.
262
+ * - 200 status: "pending" → intent exists, no receipt yet. Keep polling.
263
+ * - 200 status: "submitted" → receipt in, tx in flight. Keep polling.
264
+ * - 200 status: "bridging" → cross-chain leg in progress. Keep polling.
265
+ * - 200 status: "success"/"failed" → terminal. Stop polling.
266
+ *
267
+ * A "pending" payload is a stub ({intent_id, status, intent_status,
268
+ * create_date}) — the full Transaction fields appear once a receipt lands.
269
+ */
239
270
  declare function getStatus(intentId: string): Promise<Transaction>;
271
+ /**
272
+ * Polls intent status until terminal ("success"/"failed") or timeout.
273
+ * Non-terminal statuses ("pending", "submitted", "bridging") keep the loop
274
+ * going; a 404 (unknown intent) throws out of the loop — don't retry it.
275
+ */
240
276
  declare function pollStatus(intentId: string, { intervalMs, timeoutMs }?: {
241
277
  intervalMs?: number | undefined;
242
278
  timeoutMs?: number | undefined;
@@ -263,31 +299,90 @@ type RouteIntent = {
263
299
  fromAmountWei: string | number;
264
300
  quoteToAmountWei: string | number;
265
301
  minToAmountWei: string | number;
302
+ /** Trustware's own request ID (a UUID) for the call that created this
303
+ * intent — the same value the response carried in `X-Request-Id`, so it is
304
+ * the ID to quote in a Trustware support request. Wire field:
305
+ * `request_id`. Absent on intents created before it was split from
306
+ * `providerRequestId`. */
266
307
  requestId?: string;
308
+ /** The routing provider's own correlation ID: Squid `requestId`, LiFi/Relay
309
+ * route id, or Khalani's `{quoteId,routeId}` composite — the ID *that
310
+ * provider's* status API and support recognise. Wire field:
311
+ * `provider_request_id`. Absent when the winning provider returned none. */
312
+ providerRequestId?: string;
267
313
  routeRaw?: unknown;
268
314
  status: "created" | "submitted" | "bridging" | "success" | "failed";
269
315
  createdDate: Date | string;
270
316
  updatedDate: Date | string;
271
317
  };
318
+ /**
319
+ * A status payload from `getStatus`/`pollStatus`.
320
+ *
321
+ * Every field except `status` is optional, because a `"pending"` payload is a
322
+ * stub — the intent exists but no receipt has landed, so there is no
323
+ * transaction row to describe yet. Check `status` before reading anything
324
+ * else.
325
+ *
326
+ * The wire is snake_case (`source_tx_hash`, `intent_id`, ...) while this type
327
+ * is camelCase. `getStatus` maps one onto the other (see
328
+ * `normalizeStatusPayload` in core/routes.ts), so anything reaching you through
329
+ * `getStatus`/`pollStatus`/`runTopUp` has the camelCase names populated.
330
+ *
331
+ * The raw snake_case keys are still present **at runtime** — dropping them
332
+ * would break callers that already read them — but they are deliberately not
333
+ * declared here, because camelCase is the supported spelling and the raw ones
334
+ * are only kept for back-compat. Reading `tx.source_tx_hash` therefore needs a
335
+ * cast, and new code should not: use `tx.sourceTxHash`.
336
+ *
337
+ * `origin_eoa` and `landed_amount_verified` are the exception — the wire has no
338
+ * camelCase spelling for those two, so they are declared as-is below.
339
+ */
272
340
  type Transaction = {
273
- id: string;
274
- intentId: string;
275
- fromAddress: string;
276
- toAddress: string;
341
+ id?: string;
342
+ intentId?: string;
343
+ fromAddress?: string;
344
+ toAddress?: string;
277
345
  /** Connected wallet (EOA) that originated the payment when the sender is a
278
346
  * smart account. Wire field: `origin_eoa`. */
279
347
  origin_eoa?: string;
280
- fromChainId: string | number;
281
- toChainId: string | number;
282
- sourceTxHash: string;
283
- destTxHash: string;
284
- requestId: string;
285
- transactionRequest: unknown;
286
- status: "submitted" | "bridging" | "success" | "failed";
348
+ fromChainId?: string | number;
349
+ toChainId?: string | number;
350
+ sourceTxHash?: string;
351
+ destTxHash?: string;
352
+ /** Trustware's own request ID (a UUID), copied from the intent. Wire field:
353
+ * `request_id`. Optional: absent on a "pending" payload, and on rows
354
+ * created before it was split from `providerRequestId`. */
355
+ requestId?: string;
356
+ /** The routing provider's own correlation ID, copied from the intent — what
357
+ * that provider's status API polls with. Wire field:
358
+ * `provider_request_id`. See RouteIntent.providerRequestId. */
359
+ providerRequestId?: string;
360
+ transactionRequest?: unknown;
361
+ /**
362
+ * "pending" means the intent exists but no receipt has been submitted yet —
363
+ * the backend returns it (HTTP 200) instead of the old 404 so pollers can
364
+ * tell "not ready yet, keep polling" apart from "unknown intent" (a real
365
+ * 404, which means stop polling). A pending response carries only
366
+ * `intent_id`, `status`, `intent_status`, `create_date` and — once the
367
+ * intent has an execution step plan — `steps`; every other field here is
368
+ * absent until a receipt lands.
369
+ */
370
+ status: "pending" | "submitted" | "bridging" | "success" | "failed";
287
371
  statusRaw?: unknown;
288
372
  routePath?: unknown;
289
373
  routeStatus?: unknown;
374
+ /**
375
+ * Exact once landed_amount_verified is true; otherwise a pre-trade quote
376
+ * estimate copied in when the transaction was submitted — neither Squid's
377
+ * nor LiFi's status API carries a real landed-amount field, so the
378
+ * backend confirms it by reading the destination chain directly instead
379
+ * (see BVT-246). Check `landed_amount_verified` before auto-continuing
380
+ * on this value.
381
+ */
290
382
  toAmountWei?: string | number;
383
+ /** True once toAmountWei has been confirmed on-chain rather than being the
384
+ * pre-trade quote estimate. Wire field: `landed_amount_verified`. */
385
+ landed_amount_verified?: boolean;
291
386
  fromChainBlock: number;
292
387
  toChainBlock: number;
293
388
  fromChainTxUrl?: string;
@@ -410,6 +505,11 @@ type RoutePlan = {
410
505
  };
411
506
  steps?: unknown[];
412
507
  provider?: string;
508
+ /** The winning provider's own correlation ID, not Trustware's — it is
509
+ * absent when that provider issued none, and it is what lands on the
510
+ * intent as `providerRequestId`. Trustware's request ID for the same call
511
+ * comes back in the `X-Request-Id` response header (the provider's is also
512
+ * mirrored there as `X-Provider-Request-Id`). */
413
513
  requestId?: string;
414
514
  reliabilityScore?: number;
415
515
  diagnostics?: {
@@ -628,13 +728,14 @@ type RateLimitInfo = {
628
728
  /** Seconds until rate limit resets (only present on 429 responses) */
629
729
  retryAfter?: number;
630
730
  };
731
+ /**
732
+ * Rate limit *observability*. Retry timing itself is not configurable: the
733
+ * limit is enforced server-side per API key, so the only retry schedule that
734
+ * can actually succeed is the one the server dictates through Retry-After.
735
+ * Tuning it client-side could only make requests fail sooner or hammer a
736
+ * limit that is already closed — see RETRY_POLICY.
737
+ */
631
738
  type RetryConfig = {
632
- /** Enable automatic retry on 429 responses (default: true). Note: This does NOT disable backend rate limits, only client-side retry behavior. */
633
- autoRetry?: boolean;
634
- /** Maximum number of retries on 429 (default: 3) */
635
- maxRetries?: number;
636
- /** Base delay in ms for exponential backoff (default: 1000) */
637
- baseDelayMs?: number;
638
739
  /** Callback when rate limit info is received from server */
639
740
  onRateLimitInfo?: (info: RateLimitInfo) => void;
640
741
  /** Callback when rate limit is hit (429 received) */
@@ -645,15 +746,29 @@ type RetryConfig = {
645
746
  approachingThreshold?: number;
646
747
  };
647
748
  type ResolvedRetryConfig = {
648
- autoRetry: boolean;
649
- maxRetries: number;
650
- baseDelayMs: number;
651
749
  approachingThreshold: number;
652
750
  onRateLimitInfo?: (info: RateLimitInfo) => void;
653
751
  onRateLimited?: (info: RateLimitInfo, retryCount: number) => void;
654
752
  onRateLimitApproaching?: (info: RateLimitInfo, threshold: number) => void;
655
753
  };
656
754
  declare const DEFAULT_RETRY_CONFIG: ResolvedRetryConfig;
755
+ /**
756
+ * The one retry number the SDK chooses for itself: the total time it will
757
+ * spend waiting out rate limits before handing the caller a RateLimitError.
758
+ *
759
+ * Everything else — how long to wait, and so whether to retry at all — is read
760
+ * off the response. The limit is per API key and settable per key server-side
761
+ * (rate_limit_per_min), and the backend reports the state of *that* key on
762
+ * every response: X-RateLimit-Limit / -Remaining / -Reset, plus Retry-After on
763
+ * a 429. A schedule hardcoded here could only disagree with it.
764
+ *
765
+ * This budget stays client-side because the server cannot know it: it is a
766
+ * property of the caller, not of the key. A payment widget cannot sit on a
767
+ * spinner for the minute a fresh window might be away, so past the budget the
768
+ * SDK stops and reports the wait (retriesExhausted: false, retryAfter set) for
769
+ * the app to render.
770
+ */
771
+ declare const RATE_LIMIT_WAIT_BUDGET_MS = 10000;
657
772
  declare const DEFAULT_FEATURE_FLAGS: ResolvedFeatureFlags;
658
773
 
659
774
  type WagmiConnector = {
@@ -793,4 +908,4 @@ declare function useWalletInfo(wagmi?: WagmiBridge): {
793
908
  };
794
909
  declare function useWalletExternalDisconnect(cb: () => void): void;
795
910
 
796
- export { type WalletConnectConfig as $, type ResolvedWalletConnectConfig as A, type BuildRouteBody as B, type RetryConfig as C, type DetectedWallet as D, type EIP1193 as E, type FeatureFlags as F, type RouteApproval as G, type RouteEstimate as H, IdentityStore as I, type RouteIntent as J, type RouteParams as K, type RoutePlan as L, type RouteSponsorship as M, type RoutesConfig as N, type SimpleWalletInterface as O, type PostHookRequest as P, type SponsorshipApproval as Q, type RateLimitInfo as R, type SolanaProviderLike as S, type TrustwareConfigOptions as T, type SwapTokenRef as U, type TrustwareTheme as V, type WalletMeta as W, type TrustwareWidgetMessages as X, type WalletAddressResolution as Y, type WalletAddressSource as Z, type WalletCategory as _, type WalletId as a, type WalletEcosystem as a0, type WalletIdentity as a1, type WalletIdentityAddress as a2, type WalletIdentityChainLike as a3, buildWalletIdentityAddress as a4, createWalletIdentity as a5, resolveWalletAddressForChain as a6, upsertWalletIdentityAddress as a7, buildRoute as a8, buildDepositAddress as a9, submitReceipt as aa, getStatus as ab, pollStatus as ac, type SolanaWalletInterface as b, type WagmiBridge as c, type WagmiConnector as d, type WalletSnapshot as e, useWalletExternalDisconnect as f, useWalletInfo as g, useWireDetectionIntoManager as h, type WalletInterFaceAPI as i, TrustwareError as j, type Transaction as k, type TrustwareEvent as l, type BuildRouteResponse as m, type BuildRouteResult as n, DEFAULT_AUTO_DETECT_PROVIDER as o, DEFAULT_FEATURE_FLAGS as p, DEFAULT_MESSAGES as q, DEFAULT_RETRY_CONFIG as r, DEFAULT_SLIPPAGE as s, type EIP6963ProviderDetail as t, useWalletConnectConnect as u, type EvmWalletInterface as v, walletManager as w, type ResolvedFeatureFlags as x, type ResolvedRetryConfig as y, type ResolvedTrustwareConfig as z };
911
+ export { type WalletCategory as $, type ResolvedTrustwareConfig as A, type BuildRouteBody as B, type ResolvedWalletConnectConfig as C, type DetectedWallet as D, type EIP1193 as E, type FeatureFlags as F, type RetryConfig as G, type RouteApproval as H, IdentityStore as I, type RouteEstimate as J, type RouteIntent as K, type RouteParams as L, type RoutePlan as M, type RouteSponsorship as N, type RoutesConfig as O, type PostHookRequest as P, type SimpleWalletInterface as Q, type RateLimitInfo as R, type SolanaProviderLike as S, type TrustwareConfigOptions as T, type SponsorshipApproval as U, type SwapTokenRef as V, type WalletMeta as W, type TrustwareTheme as X, type TrustwareWidgetMessages as Y, type WalletAddressResolution as Z, type WalletAddressSource as _, type WalletId as a, type WalletConnectConfig as a0, type WalletEcosystem as a1, type WalletIdentity as a2, type WalletIdentityAddress as a3, type WalletIdentityChainLike as a4, assertValidPostHook as a5, buildWalletIdentityAddress as a6, createWalletIdentity as a7, resolveWalletAddressForChain as a8, upsertWalletIdentityAddress as a9, buildRoute as aa, buildDepositAddress as ab, submitReceipt as ac, submitStepReceipt as ad, getStatus as ae, pollStatus as af, type SolanaWalletInterface as b, type WagmiBridge as c, type WagmiConnector as d, type WalletSnapshot as e, useWalletExternalDisconnect as f, useWalletInfo as g, useWireDetectionIntoManager as h, type WalletInterFaceAPI as i, TrustwareError as j, type Transaction as k, type TrustwareEvent as l, type BuildRouteResponse as m, type BuildRouteResult as n, DEFAULT_AUTO_DETECT_PROVIDER as o, DEFAULT_FEATURE_FLAGS as p, DEFAULT_MESSAGES as q, DEFAULT_RETRY_CONFIG as r, DEFAULT_SLIPPAGE as s, type EIP6963ProviderDetail as t, useWalletConnectConnect as u, type EvmWalletInterface as v, walletManager as w, RATE_LIMIT_WAIT_BUDGET_MS as x, type ResolvedFeatureFlags as y, type ResolvedRetryConfig as z };
@@ -1,4 +1,4 @@
1
- import { D as DetectedWallet, c as WagmiBridge, i as WalletInterFaceAPI, E as EIP1193, a as WalletId, t as EIP6963ProviderDetail, W as WalletMeta } from './manager-B6NNV-E_.js';
1
+ import { D as DetectedWallet, c as WagmiBridge, i as WalletInterFaceAPI, E as EIP1193, a as WalletId, t as EIP6963ProviderDetail, W as WalletMeta } from './manager-Bw8b1wSD.js';
2
2
 
3
3
  /** Try wagmi bridge first (if provided), otherwise return EIP-1193 adapter. */
4
4
  declare function connectDetectedWallet(dw: DetectedWallet, opts?: {
@@ -1,4 +1,4 @@
1
- import { D as DetectedWallet, c as WagmiBridge, i as WalletInterFaceAPI, E as EIP1193, a as WalletId, t as EIP6963ProviderDetail, W as WalletMeta } from './manager-BwjyFjjz.cjs';
1
+ import { D as DetectedWallet, c as WagmiBridge, i as WalletInterFaceAPI, E as EIP1193, a as WalletId, t as EIP6963ProviderDetail, W as WalletMeta } from './manager-DNGk1YkR.cjs';
2
2
 
3
3
  /** Try wagmi bridge first (if provided), otherwise return EIP-1193 adapter. */
4
4
  declare function connectDetectedWallet(dw: DetectedWallet, opts?: {
@@ -12265,7 +12265,7 @@ var init_constants = __esm({
12265
12265
  "src/constants.ts"() {
12266
12266
  "use strict";
12267
12267
  SDK_NAME = "@trustware/sdk";
12268
- SDK_VERSION = "1.1.10-staging.0";
12268
+ SDK_VERSION = "1.1.11-staging.2";
12269
12269
  API_ROOT = "https://bv-staging-api.trustware.io";
12270
12270
  API_PREFIX = "/api";
12271
12271
  }
@@ -12286,16 +12286,14 @@ var init_defaults3 = __esm({
12286
12286
  });
12287
12287
 
12288
12288
  // src/types/config.ts
12289
- var DEFAULT_RETRY_CONFIG, DEFAULT_FEATURE_FLAGS;
12289
+ var DEFAULT_RETRY_CONFIG, RATE_LIMIT_WAIT_BUDGET_MS, DEFAULT_FEATURE_FLAGS;
12290
12290
  var init_config = __esm({
12291
12291
  "src/types/config.ts"() {
12292
12292
  "use strict";
12293
12293
  DEFAULT_RETRY_CONFIG = {
12294
- autoRetry: true,
12295
- maxRetries: 3,
12296
- baseDelayMs: 1e3,
12297
12294
  approachingThreshold: 5
12298
12295
  };
12296
+ RATE_LIMIT_WAIT_BUDGET_MS = 1e4;
12299
12297
  DEFAULT_FEATURE_FLAGS = {
12300
12298
  tokensPagination: true,
12301
12299
  balanceStreaming: false,
@@ -13571,9 +13569,6 @@ function resolveConfig(input) {
13571
13569
  const theme = input.theme ?? "system";
13572
13570
  const messages = deepMerge(DEFAULT_MESSAGES, input.messages);
13573
13571
  const retry = {
13574
- autoRetry: input.retry?.autoRetry ?? DEFAULT_RETRY_CONFIG.autoRetry,
13575
- maxRetries: input.retry?.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,
13576
- baseDelayMs: input.retry?.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,
13577
13572
  approachingThreshold: input.retry?.approachingThreshold ?? DEFAULT_RETRY_CONFIG.approachingThreshold,
13578
13573
  onRateLimitInfo: input.retry?.onRateLimitInfo,
13579
13574
  onRateLimited: input.retry?.onRateLimited,
@@ -13684,60 +13679,66 @@ function notifyRateLimitCallbacks(info, isRateLimited, retryCount) {
13684
13679
  retry.onRateLimitApproaching(info, retry.approachingThreshold);
13685
13680
  }
13686
13681
  }
13687
- function calculateBackoffDelay(baseDelayMs, retryCount, retryAfter) {
13688
- if (retryAfter && retryAfter > 0) {
13689
- return retryAfter * 1e3;
13682
+ function serverDirectedWaitMs(info) {
13683
+ if (!info) return null;
13684
+ if (typeof info.retryAfter === "number" && info.retryAfter > 0) {
13685
+ return info.retryAfter * 1e3;
13690
13686
  }
13691
- return baseDelayMs * Math.pow(2, retryCount);
13687
+ if (typeof info.reset === "number" && info.reset > 0) {
13688
+ const untilReset = info.reset * 1e3 - Date.now();
13689
+ if (untilReset > 0) return untilReset;
13690
+ }
13691
+ return null;
13692
13692
  }
13693
13693
  function sleep(ms) {
13694
13694
  return new Promise((resolve) => setTimeout(resolve, ms));
13695
13695
  }
13696
- async function rateLimitedFetch(url, options = {}) {
13697
- const { skipRateLimit, ...fetchOptions } = options;
13698
- const cfg = TrustwareConfigStore.get();
13699
- if (!cfg.retry.autoRetry || skipRateLimit) {
13700
- return fetch(url, fetchOptions);
13701
- }
13702
- const { maxRetries, baseDelayMs } = cfg.retry;
13696
+ async function withRateLimitRetry(attempt) {
13697
+ let waitedMs = 0;
13703
13698
  let retryCount = 0;
13704
13699
  while (true) {
13705
- const response = await fetch(url, fetchOptions);
13700
+ const response = await attempt();
13706
13701
  const rateLimitInfo = parseRateLimitHeaders(response);
13707
- if (response.status === 429) {
13702
+ if (response.status !== 429) {
13708
13703
  if (rateLimitInfo) {
13709
- notifyRateLimitCallbacks(rateLimitInfo, true, retryCount);
13710
- }
13711
- if (retryCount >= maxRetries) {
13712
- throw new RateLimitError(
13713
- rateLimitInfo || { limit: 0, remaining: 0, reset: 0 },
13714
- true
13715
- );
13704
+ notifyRateLimitCallbacks(rateLimitInfo, false, 0);
13716
13705
  }
13717
- const delay = calculateBackoffDelay(
13718
- baseDelayMs,
13719
- retryCount,
13720
- rateLimitInfo?.retryAfter
13721
- );
13722
- await sleep(delay);
13723
- retryCount++;
13724
- continue;
13706
+ return response;
13725
13707
  }
13726
13708
  if (rateLimitInfo) {
13727
- notifyRateLimitCallbacks(rateLimitInfo, false, 0);
13709
+ notifyRateLimitCallbacks(rateLimitInfo, true, retryCount);
13710
+ }
13711
+ const waitMs = serverDirectedWaitMs(rateLimitInfo);
13712
+ const info = rateLimitInfo || { limit: 0, remaining: 0, reset: 0 };
13713
+ if (waitMs === null) {
13714
+ throw new RateLimitError(info, true);
13715
+ }
13716
+ if (waitedMs + waitMs > RATE_LIMIT_WAIT_BUDGET_MS) {
13717
+ throw new RateLimitError(info, false);
13728
13718
  }
13729
- return response;
13719
+ await sleep(waitMs);
13720
+ waitedMs += waitMs;
13721
+ retryCount++;
13730
13722
  }
13731
13723
  }
13724
+ async function rateLimitedFetch(url, options = {}) {
13725
+ const { skipRateLimit, ...fetchOptions } = options;
13726
+ if (skipRateLimit) {
13727
+ return fetch(url, fetchOptions);
13728
+ }
13729
+ return withRateLimitRetry(() => fetch(url, fetchOptions));
13730
+ }
13732
13731
  var RateLimitError;
13733
13732
  var init_http = __esm({
13734
13733
  "src/core/http.ts"() {
13735
13734
  "use strict";
13736
13735
  init_constants();
13737
13736
  init_config2();
13737
+ init_config();
13738
13738
  RateLimitError = class extends Error {
13739
13739
  constructor(info, retriesExhausted) {
13740
- const message = retriesExhausted ? `Rate limit exceeded after max retries. Try again in ${info.retryAfter ?? Math.ceil((info.reset * 1e3 - Date.now()) / 1e3)} seconds.` : `Rate limit exceeded. Try again in ${info.retryAfter} seconds.`;
13740
+ const seconds = info.retryAfter ?? (info.reset > 0 ? Math.max(0, Math.ceil((info.reset * 1e3 - Date.now()) / 1e3)) : null);
13741
+ const message = seconds === null ? "Rate limit exceeded. The server did not say when to retry." : `Rate limit exceeded. Try again in ${seconds} seconds.`;
13741
13742
  super(message);
13742
13743
  this.name = "RateLimitError";
13743
13744
  this.rateLimitInfo = info;
@@ -13748,7 +13749,7 @@ var init_http = __esm({
13748
13749
  });
13749
13750
 
13750
13751
  // src/smart-account/client.ts
13751
- async function fetchWithTimeout(url, init) {
13752
+ async function fetchOnceWithTimeout(url, init) {
13752
13753
  const controller = new AbortController();
13753
13754
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
13754
13755
  try {
@@ -13769,6 +13770,9 @@ async function fetchWithTimeout(url, init) {
13769
13770
  clearTimeout(timer);
13770
13771
  }
13771
13772
  }
13773
+ async function fetchWithTimeout(url, init) {
13774
+ return withRateLimitRetry(() => fetchOnceWithTimeout(url, init));
13775
+ }
13772
13776
  function extractRevertHex(err) {
13773
13777
  if (typeof err.data === "string" && err.data.startsWith("0x"))
13774
13778
  return err.data;
@@ -14261,7 +14265,7 @@ async function estimateRelayFeeInToken(valueWei, fromAmountWei, fromAmountUSD, c
14261
14265
  tokenUnitsPerDollar = Number(fromAmountWei) / fromUSD;
14262
14266
  } else if (fromToken && fromDecimals !== void 0) {
14263
14267
  try {
14264
- const resp = await fetch(
14268
+ const resp = await rateLimitedFetch(
14265
14269
  `${apiBase()}/v1/price/token?chainId=${cid}&address=${fromToken}`,
14266
14270
  { headers: jsonHeaders() }
14267
14271
  );
@@ -14277,9 +14281,10 @@ async function estimateRelayFeeInToken(valueWei, fromAmountWei, fromAmountUSD, c
14277
14281
  return 0n;
14278
14282
  }
14279
14283
  try {
14280
- const resp = await fetch(`${apiBase()}/v1/price/native?chainId=${cid}`, {
14281
- headers: jsonHeaders()
14282
- });
14284
+ const resp = await rateLimitedFetch(
14285
+ `${apiBase()}/v1/price/native?chainId=${cid}`,
14286
+ { headers: jsonHeaders() }
14287
+ );
14283
14288
  if (!resp.ok) throw new Error(`native price HTTP ${resp.status}`);
14284
14289
  const json = await resp.json();
14285
14290
  const ethPriceUSD = parseFloat(json.data?.priceUSD ?? "0");