@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
package/dist/widget.cjs CHANGED
@@ -557,16 +557,14 @@ var init_defaults = __esm({
557
557
  });
558
558
 
559
559
  // src/types/config.ts
560
- var DEFAULT_RETRY_CONFIG, DEFAULT_FEATURE_FLAGS;
560
+ var DEFAULT_RETRY_CONFIG, RATE_LIMIT_WAIT_BUDGET_MS, DEFAULT_FEATURE_FLAGS;
561
561
  var init_config = __esm({
562
562
  "src/types/config.ts"() {
563
563
  "use strict";
564
564
  DEFAULT_RETRY_CONFIG = {
565
- autoRetry: true,
566
- maxRetries: 3,
567
- baseDelayMs: 1e3,
568
565
  approachingThreshold: 5
569
566
  };
567
+ RATE_LIMIT_WAIT_BUDGET_MS = 1e4;
570
568
  DEFAULT_FEATURE_FLAGS = {
571
569
  tokensPagination: true,
572
570
  balanceStreaming: false,
@@ -585,7 +583,7 @@ var init_constants = __esm({
585
583
  "src/constants.ts"() {
586
584
  "use strict";
587
585
  SDK_NAME = "@trustware/sdk";
588
- SDK_VERSION = "1.1.10-staging.0";
586
+ SDK_VERSION = "1.1.11-staging.2";
589
587
  API_ROOT = "https://bv-staging-api.trustware.io";
590
588
  GTM_ID = "GTM-TZDGNCXB";
591
589
  API_PREFIX = "/api";
@@ -629,6 +627,9 @@ async function assertOK(r) {
629
627
  }
630
628
  throw new Error(`HTTP ${r.status}: ${msg}`);
631
629
  }
630
+ function isNotFoundError(err) {
631
+ return err instanceof Error && err.message.startsWith("HTTP 404");
632
+ }
632
633
  async function validateSdkAccess() {
633
634
  const r = await fetch(`${apiBase()}/sdk/validate`, {
634
635
  method: "GET",
@@ -669,60 +670,66 @@ function notifyRateLimitCallbacks(info, isRateLimited, retryCount) {
669
670
  retry.onRateLimitApproaching(info, retry.approachingThreshold);
670
671
  }
671
672
  }
672
- function calculateBackoffDelay(baseDelayMs, retryCount, retryAfter) {
673
- if (retryAfter && retryAfter > 0) {
674
- return retryAfter * 1e3;
673
+ function serverDirectedWaitMs(info) {
674
+ if (!info) return null;
675
+ if (typeof info.retryAfter === "number" && info.retryAfter > 0) {
676
+ return info.retryAfter * 1e3;
677
+ }
678
+ if (typeof info.reset === "number" && info.reset > 0) {
679
+ const untilReset = info.reset * 1e3 - Date.now();
680
+ if (untilReset > 0) return untilReset;
675
681
  }
676
- return baseDelayMs * Math.pow(2, retryCount);
682
+ return null;
677
683
  }
678
684
  function sleep(ms) {
679
685
  return new Promise((resolve) => setTimeout(resolve, ms));
680
686
  }
681
- async function rateLimitedFetch(url, options = {}) {
682
- const { skipRateLimit, ...fetchOptions } = options;
683
- const cfg = TrustwareConfigStore.get();
684
- if (!cfg.retry.autoRetry || skipRateLimit) {
685
- return fetch(url, fetchOptions);
686
- }
687
- const { maxRetries, baseDelayMs } = cfg.retry;
687
+ async function withRateLimitRetry(attempt) {
688
+ let waitedMs = 0;
688
689
  let retryCount = 0;
689
690
  while (true) {
690
- const response = await fetch(url, fetchOptions);
691
+ const response = await attempt();
691
692
  const rateLimitInfo = parseRateLimitHeaders(response);
692
- if (response.status === 429) {
693
+ if (response.status !== 429) {
693
694
  if (rateLimitInfo) {
694
- notifyRateLimitCallbacks(rateLimitInfo, true, retryCount);
695
+ notifyRateLimitCallbacks(rateLimitInfo, false, 0);
695
696
  }
696
- if (retryCount >= maxRetries) {
697
- throw new RateLimitError(
698
- rateLimitInfo || { limit: 0, remaining: 0, reset: 0 },
699
- true
700
- );
701
- }
702
- const delay = calculateBackoffDelay(
703
- baseDelayMs,
704
- retryCount,
705
- rateLimitInfo?.retryAfter
706
- );
707
- await sleep(delay);
708
- retryCount++;
709
- continue;
697
+ return response;
710
698
  }
711
699
  if (rateLimitInfo) {
712
- notifyRateLimitCallbacks(rateLimitInfo, false, 0);
700
+ notifyRateLimitCallbacks(rateLimitInfo, true, retryCount);
713
701
  }
714
- return response;
702
+ const waitMs = serverDirectedWaitMs(rateLimitInfo);
703
+ const info = rateLimitInfo || { limit: 0, remaining: 0, reset: 0 };
704
+ if (waitMs === null) {
705
+ throw new RateLimitError(info, true);
706
+ }
707
+ if (waitedMs + waitMs > RATE_LIMIT_WAIT_BUDGET_MS) {
708
+ throw new RateLimitError(info, false);
709
+ }
710
+ await sleep(waitMs);
711
+ waitedMs += waitMs;
712
+ retryCount++;
715
713
  }
716
714
  }
715
+ async function rateLimitedFetch(url, options = {}) {
716
+ const { skipRateLimit, ...fetchOptions } = options;
717
+ if (skipRateLimit) {
718
+ return fetch(url, fetchOptions);
719
+ }
720
+ return withRateLimitRetry(() => fetch(url, fetchOptions));
721
+ }
717
722
  var RateLimitError;
718
723
  var init_http = __esm({
719
724
  "src/core/http.ts"() {
720
725
  "use strict";
721
726
  init_constants();
722
727
  init_config2();
728
+ init_config();
723
729
  RateLimitError = class extends Error {
724
730
  constructor(info, retriesExhausted) {
725
- 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.`;
731
+ const seconds = info.retryAfter ?? (info.reset > 0 ? Math.max(0, Math.ceil((info.reset * 1e3 - Date.now()) / 1e3)) : null);
732
+ const message = seconds === null ? "Rate limit exceeded. The server did not say when to retry." : `Rate limit exceeded. Try again in ${seconds} seconds.`;
726
733
  super(message);
727
734
  this.name = "RateLimitError";
728
735
  this.rateLimitInfo = info;
@@ -2379,7 +2386,7 @@ async function getBalances(chainRef, address, opts) {
2379
2386
  if (cached) return cached;
2380
2387
  }
2381
2388
  const url = `${apiBase()}/v1/data/wallets/${encodeURIComponent(chainKey)}/${trimmedAddress}/balances`;
2382
- const response = await fetch(url, {
2389
+ const response = await rateLimitedFetch(url, {
2383
2390
  method: "GET",
2384
2391
  credentials: "omit",
2385
2392
  headers: jsonHeaders()
@@ -2601,6 +2608,19 @@ async function submitReceipt(intentId, txHash, sponsorshipRequestId, eoaAddress)
2601
2608
  const j = await r.json();
2602
2609
  return j.data;
2603
2610
  }
2611
+ async function submitStepReceipt(intentId, stepIndex, txHash) {
2612
+ const r = await rateLimitedFetch(
2613
+ `${apiBase()}/v1/route-intent/${intentId}/steps/${stepIndex}/receipt`,
2614
+ {
2615
+ method: "POST",
2616
+ headers: jsonHeaders({ "Idempotency-Key": txHash }),
2617
+ body: JSON.stringify({ txHash })
2618
+ }
2619
+ );
2620
+ await assertOK(r);
2621
+ const j = await r.json();
2622
+ return j.data;
2623
+ }
2604
2624
  async function getStatus(intentId) {
2605
2625
  const r = await rateLimitedFetch(
2606
2626
  `${apiBase()}/v1/route-intent/${intentId}/status`,
@@ -2610,7 +2630,18 @@ async function getStatus(intentId) {
2610
2630
  );
2611
2631
  await assertOK(r);
2612
2632
  const j = await r.json();
2613
- return j.data;
2633
+ return normalizeStatusPayload(j.data);
2634
+ }
2635
+ function normalizeStatusPayload(raw) {
2636
+ if (!raw || typeof raw !== "object") return raw;
2637
+ const wire = raw;
2638
+ const out = { ...wire };
2639
+ for (const [snake, camel] of Object.entries(STATUS_WIRE_TO_CAMEL)) {
2640
+ if (out[camel] !== void 0) continue;
2641
+ if (!(snake in wire)) continue;
2642
+ out[camel] = wire[snake];
2643
+ }
2644
+ return out;
2614
2645
  }
2615
2646
  async function pollStatus(intentId, { intervalMs = 2e3, timeoutMs = 5 * 6e4 } = {}) {
2616
2647
  const t0 = Date.now();
@@ -2621,12 +2652,39 @@ async function pollStatus(intentId, { intervalMs = 2e3, timeoutMs = 5 * 6e4 } =
2621
2652
  await new Promise((r) => setTimeout(r, intervalMs));
2622
2653
  }
2623
2654
  }
2655
+ var STATUS_WIRE_TO_CAMEL;
2624
2656
  var init_routes = __esm({
2625
2657
  "src/core/routes.ts"() {
2626
2658
  "use strict";
2627
2659
  init_http();
2628
2660
  init_store();
2629
2661
  init_address();
2662
+ STATUS_WIRE_TO_CAMEL = {
2663
+ intent_id: "intentId",
2664
+ from_address: "fromAddress",
2665
+ to_address: "toAddress",
2666
+ from_chain_id: "fromChainId",
2667
+ to_chain_id: "toChainId",
2668
+ source_tx_hash: "sourceTxHash",
2669
+ dest_tx_hash: "destTxHash",
2670
+ request_id: "requestId",
2671
+ provider_request_id: "providerRequestId",
2672
+ transaction_request: "transactionRequest",
2673
+ status_raw: "statusRaw",
2674
+ route_path: "routePath",
2675
+ route_status: "routeStatus",
2676
+ to_amount_wei: "toAmountWei",
2677
+ from_chain_block: "fromChainBlock",
2678
+ to_chain_block: "toChainBlock",
2679
+ from_chain_tx_url: "fromChainTxUrl",
2680
+ to_chain_tx_url: "toChainTxUrl",
2681
+ gas_status: "gasStatus",
2682
+ is_gmp_transaction: "isGMPTransaction",
2683
+ axelar_transaction_url: "axelarTransactionUrl",
2684
+ create_date: "createdDate",
2685
+ update_date: "updatedDate",
2686
+ time_spent_ms: "timeSpentMs"
2687
+ };
2630
2688
  }
2631
2689
  });
2632
2690
 
@@ -2917,12 +2975,9 @@ function useTrustwareConfig() {
2917
2975
  title: "Trustware SDK",
2918
2976
  description: "Seamlessly bridge assets across chains with Trustware."
2919
2977
  },
2920
- retry: {
2921
- autoRetry: true,
2922
- maxRetries: 3,
2923
- baseDelayMs: 1e3,
2924
- approachingThreshold: 5
2925
- },
2978
+ // Shared with the resolver rather than restated, so this placeholder
2979
+ // can't drift from the real defaults.
2980
+ retry: { ...DEFAULT_RETRY_CONFIG },
2926
2981
  features: {
2927
2982
  tokensPagination: true,
2928
2983
  balanceStreaming: false,
@@ -2949,6 +3004,7 @@ var init_useTrustwareConfig = __esm({
2949
3004
  "use strict";
2950
3005
  "use client";
2951
3006
  import_react2 = require("react");
3007
+ init_config();
2952
3008
  init_store();
2953
3009
  }
2954
3010
  });
@@ -5325,75 +5381,17 @@ var init_useTransactionSubmit = __esm({
5325
5381
  });
5326
5382
 
5327
5383
  // src/hooks/useGTM.ts
5328
- function useGTM(gtmId) {
5329
- const isInitialized = (0, import_react7.useRef)(false);
5330
- const scriptRef = (0, import_react7.useRef)(null);
5331
- const isGA4Allowed = (0, import_react7.useCallback)(() => {
5332
- try {
5333
- return Trustware.getConfig().features.shouldAllowGA4;
5334
- } catch {
5335
- return false;
5336
- }
5337
- }, []);
5338
- (0, import_react7.useEffect)(() => {
5339
- if (!isGA4Allowed()) {
5340
- console.warn(
5341
- "useGTM: GA4 tracking is disabled or Trustware config not initialized."
5342
- );
5343
- return;
5344
- }
5345
- if (!gtmId || typeof gtmId !== "string") {
5346
- console.error(
5347
- "useGTM: A valid GTM Container ID is required (format: GTM-XXXXX)."
5348
- );
5349
- return;
5350
- }
5351
- if (isInitialized.current) return;
5352
- const alreadyLoaded = document.querySelector(
5353
- `script[src*="googletagmanager.com/gtm.js?id=${gtmId}"]`
5354
- );
5355
- if (alreadyLoaded) {
5356
- window.dataLayer = window.dataLayer || [];
5357
- isInitialized.current = true;
5358
- return;
5359
- }
5360
- window.dataLayer = window.dataLayer || [];
5361
- window.dataLayer.push({
5362
- "gtm.start": (/* @__PURE__ */ new Date()).getTime(),
5363
- event: "gtm.js"
5364
- });
5365
- const firstScript = document.getElementsByTagName("script")[0];
5366
- const script = document.createElement("script");
5367
- script.async = true;
5368
- script.src = `https://www.googletagmanager.com/gtm.js?id=${gtmId}`;
5369
- firstScript.parentNode?.insertBefore(script, firstScript);
5370
- scriptRef.current = script;
5371
- isInitialized.current = true;
5372
- return () => {
5373
- if (scriptRef.current) {
5374
- scriptRef.current.parentNode?.removeChild(scriptRef.current);
5375
- scriptRef.current = null;
5376
- }
5377
- isInitialized.current = false;
5378
- };
5379
- }, [gtmId, isGA4Allowed]);
5380
- const addNoscriptIframe = (0, import_react7.useCallback)(() => {
5381
- if (!isGA4Allowed()) return;
5382
- if (document.querySelector('iframe[src*="googletagmanager.com/ns.html"]'))
5383
- return;
5384
- const noscript = document.createElement("noscript");
5385
- const iframe = document.createElement("iframe");
5386
- iframe.src = `https://www.googletagmanager.com/ns.html?id=${gtmId}`;
5387
- iframe.height = "0";
5388
- iframe.width = "0";
5389
- iframe.style.display = "none";
5390
- iframe.style.visibility = "hidden";
5391
- noscript.appendChild(iframe);
5392
- document.body.insertBefore(noscript, document.body.firstChild);
5393
- }, [gtmId, isGA4Allowed]);
5384
+ function isGA4Allowed() {
5385
+ try {
5386
+ return Trustware.getConfig().features.shouldAllowGA4;
5387
+ } catch {
5388
+ return false;
5389
+ }
5390
+ }
5391
+ function useGTMTracker() {
5394
5392
  const trackEvent = (0, import_react7.useCallback)(
5395
5393
  (eventName, eventParams = {}) => {
5396
- if (!isInitialized.current) {
5394
+ if (!isContainerInitialized) {
5397
5395
  console.warn(
5398
5396
  "useGTM: Not initialized. Ensure a valid GTM Container ID was provided."
5399
5397
  );
@@ -5420,7 +5418,7 @@ function useGTM(gtmId) {
5420
5418
  );
5421
5419
  const setUserProperty = (0, import_react7.useCallback)(
5422
5420
  (propertyName, value) => {
5423
- if (!isInitialized.current) {
5421
+ if (!isContainerInitialized) {
5424
5422
  console.warn(
5425
5423
  "useGTM: Not initialized. Ensure a valid GTM Container ID was provided."
5426
5424
  );
@@ -5446,22 +5444,78 @@ function useGTM(gtmId) {
5446
5444
  }
5447
5445
  window.gtag(command, ...args);
5448
5446
  },
5449
- [isGA4Allowed]
5447
+ []
5450
5448
  );
5451
- return {
5452
- trackEvent,
5453
- trackPageView,
5454
- setUserProperty,
5455
- directGtag,
5456
- addNoscriptIframe
5457
- };
5449
+ return { trackEvent, trackPageView, setUserProperty, directGtag };
5450
+ }
5451
+ function useGTM(gtmId) {
5452
+ const scriptRef = (0, import_react7.useRef)(null);
5453
+ const tracker = useGTMTracker();
5454
+ (0, import_react7.useEffect)(() => {
5455
+ if (!isGA4Allowed()) {
5456
+ console.warn(
5457
+ "useGTM: GA4 tracking is disabled or Trustware config not initialized."
5458
+ );
5459
+ return;
5460
+ }
5461
+ if (!gtmId || typeof gtmId !== "string") {
5462
+ console.error(
5463
+ "useGTM: A valid GTM Container ID is required (format: GTM-XXXXX)."
5464
+ );
5465
+ return;
5466
+ }
5467
+ if (isContainerInitialized) return;
5468
+ const alreadyLoaded = document.querySelector(
5469
+ `script[src*="googletagmanager.com/gtm.js?id=${gtmId}"]`
5470
+ );
5471
+ if (alreadyLoaded) {
5472
+ window.dataLayer = window.dataLayer || [];
5473
+ isContainerInitialized = true;
5474
+ return;
5475
+ }
5476
+ window.dataLayer = window.dataLayer || [];
5477
+ window.dataLayer.push({
5478
+ "gtm.start": (/* @__PURE__ */ new Date()).getTime(),
5479
+ event: "gtm.js"
5480
+ });
5481
+ const firstScript = document.getElementsByTagName("script")[0];
5482
+ const script = document.createElement("script");
5483
+ script.async = true;
5484
+ script.src = `https://www.googletagmanager.com/gtm.js?id=${gtmId}`;
5485
+ firstScript.parentNode?.insertBefore(script, firstScript);
5486
+ scriptRef.current = script;
5487
+ isContainerInitialized = true;
5488
+ return () => {
5489
+ if (scriptRef.current) {
5490
+ scriptRef.current.parentNode?.removeChild(scriptRef.current);
5491
+ scriptRef.current = null;
5492
+ }
5493
+ isContainerInitialized = false;
5494
+ };
5495
+ }, [gtmId]);
5496
+ const addNoscriptIframe = (0, import_react7.useCallback)(() => {
5497
+ if (!isGA4Allowed()) return;
5498
+ if (document.querySelector('iframe[src*="googletagmanager.com/ns.html"]'))
5499
+ return;
5500
+ const noscript = document.createElement("noscript");
5501
+ const iframe = document.createElement("iframe");
5502
+ iframe.src = `https://www.googletagmanager.com/ns.html?id=${gtmId}`;
5503
+ iframe.height = "0";
5504
+ iframe.width = "0";
5505
+ iframe.style.display = "none";
5506
+ iframe.style.visibility = "hidden";
5507
+ noscript.appendChild(iframe);
5508
+ document.body.insertBefore(noscript, document.body.firstChild);
5509
+ }, [gtmId]);
5510
+ return { ...tracker, addNoscriptIframe };
5458
5511
  }
5459
- var import_react7;
5512
+ var import_react7, isContainerInitialized;
5460
5513
  var init_useGTM = __esm({
5461
5514
  "src/hooks/useGTM.ts"() {
5462
5515
  "use strict";
5463
5516
  import_react7 = require("react");
5464
5517
  init_core2();
5518
+ isContainerInitialized = false;
5465
5519
  }
5466
5520
  });
5467
5521
 
@@ -5487,7 +5541,7 @@ function useTransactionPolling() {
5487
5541
  return void 0;
5488
5542
  }
5489
5543
  })();
5490
- const { trackEvent } = useGTM(GTM_ID);
5544
+ const { trackEvent } = useGTMTracker();
5491
5545
  const [state, setState] = (0, import_react8.useState)({
5492
5546
  isPolling: false,
5493
5547
  apiStatus: null,
@@ -5587,8 +5641,21 @@ function useTransactionPolling() {
5587
5641
  pollCountRef.current += 1;
5588
5642
  const pollInterval = pollCountRef.current <= 10 ? FAST_POLL_INTERVAL_MS : NORMAL_POLL_INTERVAL_MS;
5589
5643
  pollingRef.current = setTimeout(poll, pollInterval);
5590
- } catch {
5644
+ } catch (err) {
5591
5645
  if (abortRef.current) return;
5646
+ if (isNotFoundError(err)) {
5647
+ clearPolling();
5648
+ const notFoundError = "Transaction session expired. Please try again.";
5649
+ setState((prev) => ({
5650
+ ...prev,
5651
+ isPolling: false,
5652
+ error: notFoundError
5653
+ }));
5654
+ setErrorMessage(notFoundError);
5655
+ setTransactionStatus("error");
5656
+ setCurrentStep("error");
5657
+ return;
5658
+ }
5592
5659
  const elapsed = Date.now() - startTimeRef.current;
5593
5660
  if (elapsed > POLL_TIMEOUT_MS) {
5594
5661
  clearPolling();
@@ -5725,10 +5792,10 @@ var init_useTransactionPolling = __esm({
5725
5792
  "use client";
5726
5793
  import_react8 = require("react");
5727
5794
  init_routes();
5795
+ init_http();
5728
5796
  init_DepositContext();
5729
5797
  init_provider();
5730
5798
  init_core2();
5731
- init_constants();
5732
5799
  init_hooks();
5733
5800
  FAST_POLL_INTERVAL_MS = 1500;
5734
5801
  NORMAL_POLL_INTERVAL_MS = 2500;
@@ -5810,6 +5877,20 @@ var init_eipWallets = __esm({
5810
5877
  "src/wallets/eipWallets.ts"() {
5811
5878
  "use strict";
5812
5879
  CHAIN_PARAMS = {
5880
+ 1: {
5881
+ chainIdHex: "0x1",
5882
+ chainName: "Ethereum",
5883
+ rpcUrls: ["https://eth.llamarpc.com"],
5884
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
5885
+ blockExplorerUrls: ["https://etherscan.io"]
5886
+ },
5887
+ 137: {
5888
+ chainIdHex: "0x89",
5889
+ chainName: "Polygon",
5890
+ rpcUrls: ["https://polygon-rpc.com"],
5891
+ nativeCurrency: { name: "POL", symbol: "POL", decimals: 18 },
5892
+ blockExplorerUrls: ["https://polygonscan.com"]
5893
+ },
5813
5894
  8453: {
5814
5895
  chainIdHex: "0x2105",
5815
5896
  chainName: "Base",
@@ -8915,9 +8996,6 @@ function resolveConfig(input) {
8915
8996
  const theme = input.theme ?? "system";
8916
8997
  const messages = deepMerge(DEFAULT_MESSAGES, input.messages);
8917
8998
  const retry = {
8918
- autoRetry: input.retry?.autoRetry ?? DEFAULT_RETRY_CONFIG.autoRetry,
8919
- maxRetries: input.retry?.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,
8920
- baseDelayMs: input.retry?.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,
8921
8999
  approachingThreshold: input.retry?.approachingThreshold ?? DEFAULT_RETRY_CONFIG.approachingThreshold,
8922
9000
  onRateLimitInfo: input.retry?.onRateLimitInfo,
8923
9001
  onRateLimited: input.retry?.onRateLimited,
@@ -9090,18 +9168,27 @@ async function waitForTxConfirmation(chainId, txHash) {
9090
9168
  }
9091
9169
  throw new Error("Timed out waiting for approval confirmation");
9092
9170
  }
9093
- async function ensureApprovals(w, approvals, fallbackChainId) {
9171
+ function requiredApprovalAmount(planAmountWei, routeAmountWei) {
9172
+ if (planAmountWei >= UNLIMITED_APPROVAL_FLOOR && routeAmountWei !== void 0 && routeAmountWei > 0n) {
9173
+ return routeAmountWei;
9174
+ }
9175
+ return planAmountWei;
9176
+ }
9177
+ function approvalSatisfied(currentAllowance, requiredWei) {
9178
+ if (requiredWei === 0n) return currentAllowance === 0n;
9179
+ return currentAllowance >= requiredWei;
9180
+ }
9181
+ async function ensureApprovals(w, approvals, fallbackChainId, intentId, routeAmountWei) {
9094
9182
  if (!approvals || approvals.length === 0) return;
9095
9183
  const owner = await w.getAddress();
9096
- for (const approval of approvals) {
9184
+ for (const [stepIndex, approval] of approvals.entries()) {
9097
9185
  const spender = approval.spender;
9098
9186
  const tokenAddress = approval.tokenAddress;
9099
9187
  const amount = approval.amount;
9100
9188
  if (!spender || !tokenAddress || !amount) continue;
9101
9189
  const chainId = approval.chainId || String(fallbackChainId ?? "");
9102
9190
  if (!chainId) continue;
9103
- const amountWei = BigInt(amount);
9104
- if (amountWei === 0n) continue;
9191
+ const amountWei = requiredApprovalAmount(BigInt(amount), routeAmountWei);
9105
9192
  let currentAllowance = 0n;
9106
9193
  try {
9107
9194
  const { allowance } = await getEVMAllowance({
@@ -9113,7 +9200,7 @@ async function ensureApprovals(w, approvals, fallbackChainId) {
9113
9200
  currentAllowance = BigInt(allowance || "0");
9114
9201
  } catch {
9115
9202
  }
9116
- if (currentAllowance >= amountWei) continue;
9203
+ if (approvalSatisfied(currentAllowance, amountWei)) continue;
9117
9204
  const data = (0, import_viem2.encodeFunctionData)({
9118
9205
  abi: erc20ApproveAbi,
9119
9206
  functionName: "approve",
@@ -9125,6 +9212,10 @@ async function ensureApprovals(w, approvals, fallbackChainId) {
9125
9212
  value: 0n,
9126
9213
  chainId: Number.isFinite(Number(chainId)) ? Number(chainId) : void 0
9127
9214
  });
9215
+ if (intentId) {
9216
+ void submitStepReceipt(intentId, stepIndex, hash).catch(() => {
9217
+ });
9218
+ }
9128
9219
  await waitForTxConfirmation(chainId, hash);
9129
9220
  }
9130
9221
  }
@@ -9134,7 +9225,7 @@ function isUserRejected(e2) {
9134
9225
  const msg = String(e2?.message || e2)?.toLowerCase?.() || "";
9135
9226
  return msg.includes("user rejected") || msg.includes("user denied");
9136
9227
  }
9137
- async function sendRouteTransaction(b, fallbackChainId) {
9228
+ async function sendRouteTransaction(b, fallbackChainId, options) {
9138
9229
  const w = walletManager.wallet;
9139
9230
  if (!w) throw new Error("Trustware.wallet not configured");
9140
9231
  const txReq = b.txReq;
@@ -9161,11 +9252,13 @@ async function sendRouteTransaction(b, fallbackChainId) {
9161
9252
  }
9162
9253
  }
9163
9254
  }
9164
- if (!validatedSponsorship) {
9255
+ if (!validatedSponsorship && !options?.approvalsEnsured) {
9165
9256
  await ensureApprovals(
9166
9257
  w,
9167
9258
  b.route?.execution?.approvals,
9168
- Number.isFinite(target) ? target : void 0
9259
+ Number.isFinite(target) ? target : void 0,
9260
+ b.intentId,
9261
+ BigInt(b.route?.estimate?.fromAmount ?? "0")
9169
9262
  );
9170
9263
  }
9171
9264
  if (w.type === "eip1193") {
@@ -9264,7 +9357,7 @@ async function runTopUp(params) {
9264
9357
  }
9265
9358
  }
9266
9359
  }
9267
- var import_viem2, erc20ApproveAbi;
9360
+ var import_viem2, erc20ApproveAbi, UNLIMITED_APPROVAL_FLOOR;
9268
9361
  var init_tx = __esm({
9269
9362
  "src/core/tx.ts"() {
9270
9363
  "use strict";
@@ -9275,6 +9368,7 @@ var init_tx = __esm({
9275
9368
  erc20ApproveAbi = (0, import_viem2.parseAbi)([
9276
9369
  "function approve(address spender, uint256 amount) returns (bool)"
9277
9370
  ]);
9371
+ UNLIMITED_APPROVAL_FLOOR = 1n << 255n;
9278
9372
  }
9279
9373
  });
9280
9374
 
@@ -9420,6 +9514,7 @@ var init_core = __esm({
9420
9514
  buildRoute,
9421
9515
  buildDepositAddress,
9422
9516
  submitReceipt,
9517
+ submitStepReceipt,
9423
9518
  getStatus,
9424
9519
  pollStatus,
9425
9520
  getBalances,
@@ -30610,7 +30705,7 @@ var init_src = __esm({
30610
30705
  });
30611
30706
 
30612
30707
  // src/smart-account/client.ts
30613
- async function fetchWithTimeout(url, init) {
30708
+ async function fetchOnceWithTimeout(url, init) {
30614
30709
  const controller = new AbortController();
30615
30710
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
30616
30711
  try {
@@ -30631,6 +30726,9 @@ async function fetchWithTimeout(url, init) {
30631
30726
  clearTimeout(timer);
30632
30727
  }
30633
30728
  }
30729
+ async function fetchWithTimeout(url, init) {
30730
+ return withRateLimitRetry(() => fetchOnceWithTimeout(url, init));
30731
+ }
30634
30732
  function extractRevertHex(err) {
30635
30733
  if (typeof err.data === "string" && err.data.startsWith("0x"))
30636
30734
  return err.data;
@@ -31123,7 +31221,7 @@ async function estimateRelayFeeInToken(valueWei, fromAmountWei, fromAmountUSD, c
31123
31221
  tokenUnitsPerDollar = Number(fromAmountWei) / fromUSD;
31124
31222
  } else if (fromToken && fromDecimals !== void 0) {
31125
31223
  try {
31126
- const resp = await fetch(
31224
+ const resp = await rateLimitedFetch(
31127
31225
  `${apiBase()}/v1/price/token?chainId=${cid}&address=${fromToken}`,
31128
31226
  { headers: jsonHeaders() }
31129
31227
  );
@@ -31139,9 +31237,10 @@ async function estimateRelayFeeInToken(valueWei, fromAmountWei, fromAmountUSD, c
31139
31237
  return 0n;
31140
31238
  }
31141
31239
  try {
31142
- const resp = await fetch(`${apiBase()}/v1/price/native?chainId=${cid}`, {
31143
- headers: jsonHeaders()
31144
- });
31240
+ const resp = await rateLimitedFetch(
31241
+ `${apiBase()}/v1/price/native?chainId=${cid}`,
31242
+ { headers: jsonHeaders() }
31243
+ );
31145
31244
  if (!resp.ok) throw new Error(`native price HTTP ${resp.status}`);
31146
31245
  const json = await resp.json();
31147
31246
  const ethPriceUSD = parseFloat(json.data?.priceUSD ?? "0");
@@ -31648,7 +31747,7 @@ function useTransactionActionModel({
31648
31747
  }) {
31649
31748
  const feeDataCacheRef = (0, import_react37.useRef)({});
31650
31749
  const { isSubmitting, submitTransaction } = useTransactionSubmit();
31651
- const { trackEvent } = useGTM(GTM_ID);
31750
+ const { trackEvent } = useGTMTracker();
31652
31751
  const destinationConfig = (() => {
31653
31752
  try {
31654
31753
  return Trustware.getConfig();
@@ -32084,7 +32183,6 @@ var init_useTransactionActionModel = __esm({
32084
32183
  init_chainHelpers();
32085
32184
  init_utils2();
32086
32185
  init_hooks();
32087
- init_constants();
32088
32186
  }
32089
32187
  });
32090
32188
 
@@ -33437,6 +33535,16 @@ async function waitForApprovalConfirmation(chainId, txHash) {
33437
33535
  }
33438
33536
  throw new Error("Timed out waiting for approval confirmation");
33439
33537
  }
33538
+ async function requireActiveAddress(wallet, plannedFor) {
33539
+ if (!wallet) throw new Error("Wallet not connected. Please reconnect.");
33540
+ const active = await wallet.getAddress();
33541
+ if (active?.toLowerCase() !== plannedFor?.toLowerCase()) {
33542
+ throw new Error(
33543
+ "Wallet account changed. Please review and confirm the swap again."
33544
+ );
33545
+ }
33546
+ return active;
33547
+ }
33440
33548
  function useSwapExecution(fromChain) {
33441
33549
  const [state, setState] = (0, import_react45.useState)({
33442
33550
  txStatus: "idle",
@@ -33493,7 +33601,15 @@ function useSwapExecution(fromChain) {
33493
33601
  if (tx.status === "bridging") {
33494
33602
  setState((p) => ({ ...p, txStatus: "bridging" }));
33495
33603
  }
33496
- } catch {
33604
+ } catch (err) {
33605
+ if (abortRef.current) return;
33606
+ if (isNotFoundError(err)) {
33607
+ clearPolling();
33608
+ const msg = "Transaction session expired. Please try again.";
33609
+ setState((p) => ({ ...p, txStatus: "error", errorMessage: msg }));
33610
+ onError(msg);
33611
+ return;
33612
+ }
33497
33613
  }
33498
33614
  if (abortRef.current) return;
33499
33615
  pollCountRef.current += 1;
@@ -33519,24 +33635,55 @@ function useSwapExecution(fromChain) {
33519
33635
  }
33520
33636
  const txReq = routeResult.txReq;
33521
33637
  const chainIdStr = String(txReq?.chainId ?? fromChain?.chainId ?? "");
33522
- const spender = txReq?.to ?? txReq?.target;
33523
33638
  const amountWei = BigInt(routeResult.route?.estimate?.fromAmount ?? "0");
33524
- if (!spender || !chainIdStr || amountWei === 0n) {
33639
+ const plannedApprovals = routeResult.route?.execution?.approvals ?? [];
33640
+ const toCheck = plannedApprovals.length ? plannedApprovals.flatMap((a) => {
33641
+ const token = a.tokenAddress ?? fromTokenAddress;
33642
+ const required = requiredApprovalAmount(
33643
+ BigInt(a.amount || "0"),
33644
+ amountWei
33645
+ );
33646
+ if (!token || !a.spender) return [];
33647
+ return [
33648
+ {
33649
+ chainId: a.chainId || chainIdStr,
33650
+ token,
33651
+ spender: a.spender,
33652
+ required
33653
+ }
33654
+ ];
33655
+ }) : (() => {
33656
+ const spender = txReq?.to ?? txReq?.target;
33657
+ if (!spender) return [];
33658
+ return [
33659
+ {
33660
+ chainId: chainIdStr,
33661
+ token: fromTokenAddress,
33662
+ spender,
33663
+ required: amountWei
33664
+ }
33665
+ ];
33666
+ })();
33667
+ if (toCheck.length === 0 || !chainIdStr || amountWei === 0n) {
33525
33668
  setState((p) => ({ ...p, allowanceStatus: "unknown" }));
33526
33669
  return;
33527
33670
  }
33528
33671
  setState((p) => ({ ...p, allowanceStatus: "checking" }));
33529
33672
  try {
33530
- const { allowance } = await getEVMAllowance({
33531
- chainId: chainIdStr,
33532
- tokenAddress: fromTokenAddress,
33533
- ownerAddress: walletAddress,
33534
- spenderAddress: spender
33535
- });
33536
- const allowanceWei = BigInt(allowance || "0");
33673
+ const results = await Promise.all(
33674
+ toCheck.map(async (c) => {
33675
+ const { allowance } = await getEVMAllowance({
33676
+ chainId: c.chainId,
33677
+ tokenAddress: c.token,
33678
+ ownerAddress: walletAddress,
33679
+ spenderAddress: c.spender
33680
+ });
33681
+ return approvalSatisfied(BigInt(allowance || "0"), c.required);
33682
+ })
33683
+ );
33537
33684
  setState((p) => ({
33538
33685
  ...p,
33539
- allowanceStatus: allowanceWei >= amountWei ? "sufficient" : "needed"
33686
+ allowanceStatus: results.every(Boolean) ? "sufficient" : "needed"
33540
33687
  }));
33541
33688
  } catch {
33542
33689
  setState((p) => ({ ...p, allowanceStatus: "needed" }));
@@ -33640,19 +33787,49 @@ function useSwapExecution(fromChain) {
33640
33787
  }
33641
33788
  }
33642
33789
  try {
33643
- if (!isNative && walletAddress && spender && chainIdStr && fromTokenAddress) {
33790
+ const plannedApprovals = routeResult.route?.execution?.approvals ?? [];
33791
+ const requiredApprovals = plannedApprovals.length ? plannedApprovals.flatMap((a, index) => {
33792
+ const token = a.tokenAddress ?? fromTokenAddress;
33793
+ const approvalSpender = a.spender;
33794
+ if (!token || !approvalSpender) return [];
33795
+ const amountWei = requiredApprovalAmount(
33796
+ BigInt(a.amount || "0"),
33797
+ fromAmountWei
33798
+ );
33799
+ return [
33800
+ {
33801
+ stepIndex: index,
33802
+ token,
33803
+ spender: approvalSpender,
33804
+ amountWei,
33805
+ chainId: a.chainId || chainIdStr
33806
+ }
33807
+ ];
33808
+ }) : spender && fromTokenAddress ? [
33809
+ {
33810
+ stepIndex: null,
33811
+ token: fromTokenAddress,
33812
+ spender,
33813
+ amountWei: fromAmountWei,
33814
+ chainId: chainIdStr
33815
+ }
33816
+ ] : [];
33817
+ const ownsApprovals = !isNative && !!walletAddress && !!chainIdStr;
33818
+ const activeAddress = ownsApprovals ? await requireActiveAddress(wallet, walletAddress) : void 0;
33819
+ for (const required of requiredApprovals) {
33820
+ if (!ownsApprovals) break;
33644
33821
  let allowanceWei = 0n;
33645
33822
  try {
33646
33823
  const { allowance } = await getEVMAllowance({
33647
- chainId: chainIdStr,
33648
- tokenAddress: fromTokenAddress,
33649
- ownerAddress: walletAddress,
33650
- spenderAddress: spender
33824
+ chainId: required.chainId,
33825
+ tokenAddress: required.token,
33826
+ ownerAddress: activeAddress,
33827
+ spenderAddress: required.spender
33651
33828
  });
33652
33829
  allowanceWei = BigInt(allowance || "0");
33653
33830
  } catch {
33654
33831
  }
33655
- if (allowanceWei < fromAmountWei) {
33832
+ if (!approvalSatisfied(allowanceWei, required.amountWei)) {
33656
33833
  setState((p) => ({ ...p, txStatus: "approving" }));
33657
33834
  if (!wallet || wallet.ecosystem !== "evm") {
33658
33835
  throw new Error("EVM wallet required for token approval");
@@ -33660,7 +33837,7 @@ function useSwapExecution(fromChain) {
33660
33837
  if (!chainIdStr || chainIdStr === "0") {
33661
33838
  throw new Error("Invalid chain ID for token approval");
33662
33839
  }
33663
- const targetChain = Number(txReq.chainId ?? fromChain?.chainId);
33840
+ const targetChain = Number(required.chainId ?? txReq.chainId);
33664
33841
  if (Number.isFinite(targetChain)) {
33665
33842
  const current = await wallet.getChainId();
33666
33843
  if (current !== targetChain) {
@@ -33672,18 +33849,18 @@ function useSwapExecution(fromChain) {
33672
33849
  }
33673
33850
  const approveAmount = maxApproval ? BigInt(
33674
33851
  "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
33675
- ) : fromAmountWei;
33852
+ ) : required.amountWei;
33676
33853
  const data = (0, import_viem43.encodeFunctionData)({
33677
33854
  abi: import_viem43.erc20Abi,
33678
33855
  functionName: "approve",
33679
- args: [spender, approveAmount]
33856
+ args: [required.spender, approveAmount]
33680
33857
  });
33681
33858
  let approvalHash;
33682
33859
  if (wallet.type === "eip1193") {
33683
33860
  const from = await wallet.getAddress();
33684
33861
  const params = {
33685
33862
  from,
33686
- to: fromTokenAddress,
33863
+ to: required.token,
33687
33864
  data,
33688
33865
  value: "0x0"
33689
33866
  };
@@ -33696,14 +33873,22 @@ function useSwapExecution(fromChain) {
33696
33873
  });
33697
33874
  } else {
33698
33875
  const response = await wallet.sendTransaction({
33699
- to: fromTokenAddress,
33876
+ to: required.token,
33700
33877
  data,
33701
33878
  value: 0n,
33702
33879
  chainId: Number.isFinite(targetChain) ? targetChain : void 0
33703
33880
  });
33704
33881
  approvalHash = response.hash;
33705
33882
  }
33706
- await waitForApprovalConfirmation(chainIdStr, approvalHash);
33883
+ if (required.stepIndex !== null) {
33884
+ void submitStepReceipt(
33885
+ routeResult.intentId,
33886
+ required.stepIndex,
33887
+ approvalHash
33888
+ ).catch(() => {
33889
+ });
33890
+ }
33891
+ await waitForApprovalConfirmation(required.chainId, approvalHash);
33707
33892
  setState((p) => ({
33708
33893
  ...p,
33709
33894
  txStatus: "confirming",
@@ -33711,9 +33896,13 @@ function useSwapExecution(fromChain) {
33711
33896
  }));
33712
33897
  }
33713
33898
  }
33899
+ if (ownsApprovals) {
33900
+ await requireActiveAddress(wallet, walletAddress);
33901
+ }
33714
33902
  const hash = await Trustware.sendRouteTransaction(
33715
33903
  routeResult,
33716
- numericChainId
33904
+ numericChainId,
33905
+ { approvalsEnsured: ownsApprovals }
33717
33906
  );
33718
33907
  try {
33719
33908
  await submitReceipt(routeResult.intentId, hash);
@@ -33783,6 +33972,8 @@ var init_useSwapExecution = __esm({
33783
33972
  import_viem43 = require("viem");
33784
33973
  init_core2();
33785
33974
  init_routes();
33975
+ init_http();
33976
+ init_tx();
33786
33977
  init_sdkRpc();
33787
33978
  init_chainHelpers();
33788
33979
  FAST_POLL_MS = 1500;
@@ -33794,9 +33985,14 @@ var init_useSwapExecution = __esm({
33794
33985
 
33795
33986
  // src/core/forex.ts
33796
33987
  async function fetchForexRates(base2 = "USD") {
33797
- const r = await fetch(`${apiBase()}/v1/forex?base=${base2}`, {
33798
- headers: jsonHeaders()
33799
- });
33988
+ let r;
33989
+ try {
33990
+ r = await rateLimitedFetch(`${apiBase()}/v1/forex?base=${base2}`, {
33991
+ headers: jsonHeaders()
33992
+ });
33993
+ } catch {
33994
+ return {};
33995
+ }
33800
33996
  if (!r.ok) return {};
33801
33997
  const j = await r.json();
33802
33998
  const rates = j?.data?.rates ?? j?.rates ?? j?.data ?? {};
@@ -39732,6 +39928,7 @@ function WidgetInner({
39732
39928
  const { resolvedTheme } = useDepositUi();
39733
39929
  const { status, revalidate, errors } = useTrustware();
39734
39930
  const [showConfirmDialog, setShowConfirmDialog] = (0, import_react51.useState)(false);
39931
+ useGTM(GTM_ID);
39735
39932
  const handleCloseRequest = (0, import_react51.useCallback)(() => {
39736
39933
  if (ACTIVE_TRANSACTION_STATUSES.includes(transactionStatus)) {
39737
39934
  setShowConfirmDialog(true);
@@ -39806,6 +40003,8 @@ var init_TrustwareWidgetV2 = __esm({
39806
40003
  init_provider();
39807
40004
  init_manager();
39808
40005
  init_useTrustwareConfig();
40006
+ init_hooks();
40007
+ init_constants();
39809
40008
  init_swap();
39810
40009
  import_jsx_runtime60 = require("react/jsx-runtime");
39811
40010
  widgetContentContainerStyle = {