@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.mjs CHANGED
@@ -535,16 +535,14 @@ var init_defaults = __esm({
535
535
  });
536
536
 
537
537
  // src/types/config.ts
538
- var DEFAULT_RETRY_CONFIG, DEFAULT_FEATURE_FLAGS;
538
+ var DEFAULT_RETRY_CONFIG, RATE_LIMIT_WAIT_BUDGET_MS, DEFAULT_FEATURE_FLAGS;
539
539
  var init_config = __esm({
540
540
  "src/types/config.ts"() {
541
541
  "use strict";
542
542
  DEFAULT_RETRY_CONFIG = {
543
- autoRetry: true,
544
- maxRetries: 3,
545
- baseDelayMs: 1e3,
546
543
  approachingThreshold: 5
547
544
  };
545
+ RATE_LIMIT_WAIT_BUDGET_MS = 1e4;
548
546
  DEFAULT_FEATURE_FLAGS = {
549
547
  tokensPagination: true,
550
548
  balanceStreaming: false,
@@ -563,7 +561,7 @@ var init_constants = __esm({
563
561
  "src/constants.ts"() {
564
562
  "use strict";
565
563
  SDK_NAME = "@trustware/sdk";
566
- SDK_VERSION = "1.1.10-staging.0";
564
+ SDK_VERSION = "1.1.11-staging.2";
567
565
  API_ROOT = "https://bv-staging-api.trustware.io";
568
566
  GTM_ID = "GTM-TZDGNCXB";
569
567
  API_PREFIX = "/api";
@@ -607,6 +605,9 @@ async function assertOK(r) {
607
605
  }
608
606
  throw new Error(`HTTP ${r.status}: ${msg}`);
609
607
  }
608
+ function isNotFoundError(err) {
609
+ return err instanceof Error && err.message.startsWith("HTTP 404");
610
+ }
610
611
  async function validateSdkAccess() {
611
612
  const r = await fetch(`${apiBase()}/sdk/validate`, {
612
613
  method: "GET",
@@ -647,60 +648,66 @@ function notifyRateLimitCallbacks(info, isRateLimited, retryCount) {
647
648
  retry.onRateLimitApproaching(info, retry.approachingThreshold);
648
649
  }
649
650
  }
650
- function calculateBackoffDelay(baseDelayMs, retryCount, retryAfter) {
651
- if (retryAfter && retryAfter > 0) {
652
- return retryAfter * 1e3;
651
+ function serverDirectedWaitMs(info) {
652
+ if (!info) return null;
653
+ if (typeof info.retryAfter === "number" && info.retryAfter > 0) {
654
+ return info.retryAfter * 1e3;
655
+ }
656
+ if (typeof info.reset === "number" && info.reset > 0) {
657
+ const untilReset = info.reset * 1e3 - Date.now();
658
+ if (untilReset > 0) return untilReset;
653
659
  }
654
- return baseDelayMs * Math.pow(2, retryCount);
660
+ return null;
655
661
  }
656
662
  function sleep(ms) {
657
663
  return new Promise((resolve) => setTimeout(resolve, ms));
658
664
  }
659
- async function rateLimitedFetch(url, options = {}) {
660
- const { skipRateLimit, ...fetchOptions } = options;
661
- const cfg = TrustwareConfigStore.get();
662
- if (!cfg.retry.autoRetry || skipRateLimit) {
663
- return fetch(url, fetchOptions);
664
- }
665
- const { maxRetries, baseDelayMs } = cfg.retry;
665
+ async function withRateLimitRetry(attempt) {
666
+ let waitedMs = 0;
666
667
  let retryCount = 0;
667
668
  while (true) {
668
- const response = await fetch(url, fetchOptions);
669
+ const response = await attempt();
669
670
  const rateLimitInfo = parseRateLimitHeaders(response);
670
- if (response.status === 429) {
671
+ if (response.status !== 429) {
671
672
  if (rateLimitInfo) {
672
- notifyRateLimitCallbacks(rateLimitInfo, true, retryCount);
673
+ notifyRateLimitCallbacks(rateLimitInfo, false, 0);
673
674
  }
674
- if (retryCount >= maxRetries) {
675
- throw new RateLimitError(
676
- rateLimitInfo || { limit: 0, remaining: 0, reset: 0 },
677
- true
678
- );
679
- }
680
- const delay = calculateBackoffDelay(
681
- baseDelayMs,
682
- retryCount,
683
- rateLimitInfo?.retryAfter
684
- );
685
- await sleep(delay);
686
- retryCount++;
687
- continue;
675
+ return response;
688
676
  }
689
677
  if (rateLimitInfo) {
690
- notifyRateLimitCallbacks(rateLimitInfo, false, 0);
678
+ notifyRateLimitCallbacks(rateLimitInfo, true, retryCount);
691
679
  }
692
- return response;
680
+ const waitMs = serverDirectedWaitMs(rateLimitInfo);
681
+ const info = rateLimitInfo || { limit: 0, remaining: 0, reset: 0 };
682
+ if (waitMs === null) {
683
+ throw new RateLimitError(info, true);
684
+ }
685
+ if (waitedMs + waitMs > RATE_LIMIT_WAIT_BUDGET_MS) {
686
+ throw new RateLimitError(info, false);
687
+ }
688
+ await sleep(waitMs);
689
+ waitedMs += waitMs;
690
+ retryCount++;
693
691
  }
694
692
  }
693
+ async function rateLimitedFetch(url, options = {}) {
694
+ const { skipRateLimit, ...fetchOptions } = options;
695
+ if (skipRateLimit) {
696
+ return fetch(url, fetchOptions);
697
+ }
698
+ return withRateLimitRetry(() => fetch(url, fetchOptions));
699
+ }
695
700
  var RateLimitError;
696
701
  var init_http = __esm({
697
702
  "src/core/http.ts"() {
698
703
  "use strict";
699
704
  init_constants();
700
705
  init_config2();
706
+ init_config();
701
707
  RateLimitError = class extends Error {
702
708
  constructor(info, retriesExhausted) {
703
- 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.`;
709
+ const seconds = info.retryAfter ?? (info.reset > 0 ? Math.max(0, Math.ceil((info.reset * 1e3 - Date.now()) / 1e3)) : null);
710
+ const message = seconds === null ? "Rate limit exceeded. The server did not say when to retry." : `Rate limit exceeded. Try again in ${seconds} seconds.`;
704
711
  super(message);
705
712
  this.name = "RateLimitError";
706
713
  this.rateLimitInfo = info;
@@ -2357,7 +2364,7 @@ async function getBalances(chainRef, address, opts) {
2357
2364
  if (cached) return cached;
2358
2365
  }
2359
2366
  const url = `${apiBase()}/v1/data/wallets/${encodeURIComponent(chainKey)}/${trimmedAddress}/balances`;
2360
- const response = await fetch(url, {
2367
+ const response = await rateLimitedFetch(url, {
2361
2368
  method: "GET",
2362
2369
  credentials: "omit",
2363
2370
  headers: jsonHeaders()
@@ -2579,6 +2586,19 @@ async function submitReceipt(intentId, txHash, sponsorshipRequestId, eoaAddress)
2579
2586
  const j = await r.json();
2580
2587
  return j.data;
2581
2588
  }
2589
+ async function submitStepReceipt(intentId, stepIndex, txHash) {
2590
+ const r = await rateLimitedFetch(
2591
+ `${apiBase()}/v1/route-intent/${intentId}/steps/${stepIndex}/receipt`,
2592
+ {
2593
+ method: "POST",
2594
+ headers: jsonHeaders({ "Idempotency-Key": txHash }),
2595
+ body: JSON.stringify({ txHash })
2596
+ }
2597
+ );
2598
+ await assertOK(r);
2599
+ const j = await r.json();
2600
+ return j.data;
2601
+ }
2582
2602
  async function getStatus(intentId) {
2583
2603
  const r = await rateLimitedFetch(
2584
2604
  `${apiBase()}/v1/route-intent/${intentId}/status`,
@@ -2588,7 +2608,18 @@ async function getStatus(intentId) {
2588
2608
  );
2589
2609
  await assertOK(r);
2590
2610
  const j = await r.json();
2591
- return j.data;
2611
+ return normalizeStatusPayload(j.data);
2612
+ }
2613
+ function normalizeStatusPayload(raw) {
2614
+ if (!raw || typeof raw !== "object") return raw;
2615
+ const wire = raw;
2616
+ const out = { ...wire };
2617
+ for (const [snake, camel] of Object.entries(STATUS_WIRE_TO_CAMEL)) {
2618
+ if (out[camel] !== void 0) continue;
2619
+ if (!(snake in wire)) continue;
2620
+ out[camel] = wire[snake];
2621
+ }
2622
+ return out;
2592
2623
  }
2593
2624
  async function pollStatus(intentId, { intervalMs = 2e3, timeoutMs = 5 * 6e4 } = {}) {
2594
2625
  const t0 = Date.now();
@@ -2599,12 +2630,39 @@ async function pollStatus(intentId, { intervalMs = 2e3, timeoutMs = 5 * 6e4 } =
2599
2630
  await new Promise((r) => setTimeout(r, intervalMs));
2600
2631
  }
2601
2632
  }
2633
+ var STATUS_WIRE_TO_CAMEL;
2602
2634
  var init_routes = __esm({
2603
2635
  "src/core/routes.ts"() {
2604
2636
  "use strict";
2605
2637
  init_http();
2606
2638
  init_store();
2607
2639
  init_address();
2640
+ STATUS_WIRE_TO_CAMEL = {
2641
+ intent_id: "intentId",
2642
+ from_address: "fromAddress",
2643
+ to_address: "toAddress",
2644
+ from_chain_id: "fromChainId",
2645
+ to_chain_id: "toChainId",
2646
+ source_tx_hash: "sourceTxHash",
2647
+ dest_tx_hash: "destTxHash",
2648
+ request_id: "requestId",
2649
+ provider_request_id: "providerRequestId",
2650
+ transaction_request: "transactionRequest",
2651
+ status_raw: "statusRaw",
2652
+ route_path: "routePath",
2653
+ route_status: "routeStatus",
2654
+ to_amount_wei: "toAmountWei",
2655
+ from_chain_block: "fromChainBlock",
2656
+ to_chain_block: "toChainBlock",
2657
+ from_chain_tx_url: "fromChainTxUrl",
2658
+ to_chain_tx_url: "toChainTxUrl",
2659
+ gas_status: "gasStatus",
2660
+ is_gmp_transaction: "isGMPTransaction",
2661
+ axelar_transaction_url: "axelarTransactionUrl",
2662
+ create_date: "createdDate",
2663
+ update_date: "updatedDate",
2664
+ time_spent_ms: "timeSpentMs"
2665
+ };
2608
2666
  }
2609
2667
  });
2610
2668
 
@@ -2895,12 +2953,9 @@ function useTrustwareConfig() {
2895
2953
  title: "Trustware SDK",
2896
2954
  description: "Seamlessly bridge assets across chains with Trustware."
2897
2955
  },
2898
- retry: {
2899
- autoRetry: true,
2900
- maxRetries: 3,
2901
- baseDelayMs: 1e3,
2902
- approachingThreshold: 5
2903
- },
2956
+ // Shared with the resolver rather than restated, so this placeholder
2957
+ // can't drift from the real defaults.
2958
+ retry: { ...DEFAULT_RETRY_CONFIG },
2904
2959
  features: {
2905
2960
  tokensPagination: true,
2906
2961
  balanceStreaming: false,
@@ -2925,6 +2980,7 @@ var init_useTrustwareConfig = __esm({
2925
2980
  "src/hooks/useTrustwareConfig.ts"() {
2926
2981
  "use strict";
2927
2982
  "use client";
2983
+ init_config();
2928
2984
  init_store();
2929
2985
  }
2930
2986
  });
@@ -5300,75 +5356,17 @@ var init_useTransactionSubmit = __esm({
5300
5356
 
5301
5357
  // src/hooks/useGTM.ts
5302
5358
  import { useEffect as useEffect5, useRef as useRef2, useCallback as useCallback2 } from "react";
5303
- function useGTM(gtmId) {
5304
- const isInitialized = useRef2(false);
5305
- const scriptRef = useRef2(null);
5306
- const isGA4Allowed = useCallback2(() => {
5307
- try {
5308
- return Trustware.getConfig().features.shouldAllowGA4;
5309
- } catch {
5310
- return false;
5311
- }
5312
- }, []);
5313
- useEffect5(() => {
5314
- if (!isGA4Allowed()) {
5315
- console.warn(
5316
- "useGTM: GA4 tracking is disabled or Trustware config not initialized."
5317
- );
5318
- return;
5319
- }
5320
- if (!gtmId || typeof gtmId !== "string") {
5321
- console.error(
5322
- "useGTM: A valid GTM Container ID is required (format: GTM-XXXXX)."
5323
- );
5324
- return;
5325
- }
5326
- if (isInitialized.current) return;
5327
- const alreadyLoaded = document.querySelector(
5328
- `script[src*="googletagmanager.com/gtm.js?id=${gtmId}"]`
5329
- );
5330
- if (alreadyLoaded) {
5331
- window.dataLayer = window.dataLayer || [];
5332
- isInitialized.current = true;
5333
- return;
5334
- }
5335
- window.dataLayer = window.dataLayer || [];
5336
- window.dataLayer.push({
5337
- "gtm.start": (/* @__PURE__ */ new Date()).getTime(),
5338
- event: "gtm.js"
5339
- });
5340
- const firstScript = document.getElementsByTagName("script")[0];
5341
- const script = document.createElement("script");
5342
- script.async = true;
5343
- script.src = `https://www.googletagmanager.com/gtm.js?id=${gtmId}`;
5344
- firstScript.parentNode?.insertBefore(script, firstScript);
5345
- scriptRef.current = script;
5346
- isInitialized.current = true;
5347
- return () => {
5348
- if (scriptRef.current) {
5349
- scriptRef.current.parentNode?.removeChild(scriptRef.current);
5350
- scriptRef.current = null;
5351
- }
5352
- isInitialized.current = false;
5353
- };
5354
- }, [gtmId, isGA4Allowed]);
5355
- const addNoscriptIframe = useCallback2(() => {
5356
- if (!isGA4Allowed()) return;
5357
- if (document.querySelector('iframe[src*="googletagmanager.com/ns.html"]'))
5358
- return;
5359
- const noscript = document.createElement("noscript");
5360
- const iframe = document.createElement("iframe");
5361
- iframe.src = `https://www.googletagmanager.com/ns.html?id=${gtmId}`;
5362
- iframe.height = "0";
5363
- iframe.width = "0";
5364
- iframe.style.display = "none";
5365
- iframe.style.visibility = "hidden";
5366
- noscript.appendChild(iframe);
5367
- document.body.insertBefore(noscript, document.body.firstChild);
5368
- }, [gtmId, isGA4Allowed]);
5359
+ function isGA4Allowed() {
5360
+ try {
5361
+ return Trustware.getConfig().features.shouldAllowGA4;
5362
+ } catch {
5363
+ return false;
5364
+ }
5365
+ }
5366
+ function useGTMTracker() {
5369
5367
  const trackEvent = useCallback2(
5370
5368
  (eventName, eventParams = {}) => {
5371
- if (!isInitialized.current) {
5369
+ if (!isContainerInitialized) {
5372
5370
  console.warn(
5373
5371
  "useGTM: Not initialized. Ensure a valid GTM Container ID was provided."
5374
5372
  );
@@ -5395,7 +5393,7 @@ function useGTM(gtmId) {
5395
5393
  );
5396
5394
  const setUserProperty = useCallback2(
5397
5395
  (propertyName, value) => {
5398
- if (!isInitialized.current) {
5396
+ if (!isContainerInitialized) {
5399
5397
  console.warn(
5400
5398
  "useGTM: Not initialized. Ensure a valid GTM Container ID was provided."
5401
5399
  );
@@ -5421,20 +5419,77 @@ function useGTM(gtmId) {
5421
5419
  }
5422
5420
  window.gtag(command, ...args);
5423
5421
  },
5424
- [isGA4Allowed]
5422
+ []
5425
5423
  );
5426
- return {
5427
- trackEvent,
5428
- trackPageView,
5429
- setUserProperty,
5430
- directGtag,
5431
- addNoscriptIframe
5432
- };
5424
+ return { trackEvent, trackPageView, setUserProperty, directGtag };
5425
+ }
5426
+ function useGTM(gtmId) {
5427
+ const scriptRef = useRef2(null);
5428
+ const tracker = useGTMTracker();
5429
+ useEffect5(() => {
5430
+ if (!isGA4Allowed()) {
5431
+ console.warn(
5432
+ "useGTM: GA4 tracking is disabled or Trustware config not initialized."
5433
+ );
5434
+ return;
5435
+ }
5436
+ if (!gtmId || typeof gtmId !== "string") {
5437
+ console.error(
5438
+ "useGTM: A valid GTM Container ID is required (format: GTM-XXXXX)."
5439
+ );
5440
+ return;
5441
+ }
5442
+ if (isContainerInitialized) return;
5443
+ const alreadyLoaded = document.querySelector(
5444
+ `script[src*="googletagmanager.com/gtm.js?id=${gtmId}"]`
5445
+ );
5446
+ if (alreadyLoaded) {
5447
+ window.dataLayer = window.dataLayer || [];
5448
+ isContainerInitialized = true;
5449
+ return;
5450
+ }
5451
+ window.dataLayer = window.dataLayer || [];
5452
+ window.dataLayer.push({
5453
+ "gtm.start": (/* @__PURE__ */ new Date()).getTime(),
5454
+ event: "gtm.js"
5455
+ });
5456
+ const firstScript = document.getElementsByTagName("script")[0];
5457
+ const script = document.createElement("script");
5458
+ script.async = true;
5459
+ script.src = `https://www.googletagmanager.com/gtm.js?id=${gtmId}`;
5460
+ firstScript.parentNode?.insertBefore(script, firstScript);
5461
+ scriptRef.current = script;
5462
+ isContainerInitialized = true;
5463
+ return () => {
5464
+ if (scriptRef.current) {
5465
+ scriptRef.current.parentNode?.removeChild(scriptRef.current);
5466
+ scriptRef.current = null;
5467
+ }
5468
+ isContainerInitialized = false;
5469
+ };
5470
+ }, [gtmId]);
5471
+ const addNoscriptIframe = useCallback2(() => {
5472
+ if (!isGA4Allowed()) return;
5473
+ if (document.querySelector('iframe[src*="googletagmanager.com/ns.html"]'))
5474
+ return;
5475
+ const noscript = document.createElement("noscript");
5476
+ const iframe = document.createElement("iframe");
5477
+ iframe.src = `https://www.googletagmanager.com/ns.html?id=${gtmId}`;
5478
+ iframe.height = "0";
5479
+ iframe.width = "0";
5480
+ iframe.style.display = "none";
5481
+ iframe.style.visibility = "hidden";
5482
+ noscript.appendChild(iframe);
5483
+ document.body.insertBefore(noscript, document.body.firstChild);
5484
+ }, [gtmId]);
5485
+ return { ...tracker, addNoscriptIframe };
5433
5486
  }
5487
+ var isContainerInitialized;
5434
5488
  var init_useGTM = __esm({
5435
5489
  "src/hooks/useGTM.ts"() {
5436
5490
  "use strict";
5437
5491
  init_core2();
5492
+ isContainerInitialized = false;
5438
5493
  }
5439
5494
  });
5440
5495
 
@@ -5461,7 +5516,7 @@ function useTransactionPolling() {
5461
5516
  return void 0;
5462
5517
  }
5463
5518
  })();
5464
- const { trackEvent } = useGTM(GTM_ID);
5519
+ const { trackEvent } = useGTMTracker();
5465
5520
  const [state, setState] = useState6({
5466
5521
  isPolling: false,
5467
5522
  apiStatus: null,
@@ -5561,8 +5616,21 @@ function useTransactionPolling() {
5561
5616
  pollCountRef.current += 1;
5562
5617
  const pollInterval = pollCountRef.current <= 10 ? FAST_POLL_INTERVAL_MS : NORMAL_POLL_INTERVAL_MS;
5563
5618
  pollingRef.current = setTimeout(poll, pollInterval);
5564
- } catch {
5619
+ } catch (err) {
5565
5620
  if (abortRef.current) return;
5621
+ if (isNotFoundError(err)) {
5622
+ clearPolling();
5623
+ const notFoundError = "Transaction session expired. Please try again.";
5624
+ setState((prev) => ({
5625
+ ...prev,
5626
+ isPolling: false,
5627
+ error: notFoundError
5628
+ }));
5629
+ setErrorMessage(notFoundError);
5630
+ setTransactionStatus("error");
5631
+ setCurrentStep("error");
5632
+ return;
5633
+ }
5566
5634
  const elapsed = Date.now() - startTimeRef.current;
5567
5635
  if (elapsed > POLL_TIMEOUT_MS) {
5568
5636
  clearPolling();
@@ -5698,10 +5766,10 @@ var init_useTransactionPolling = __esm({
5698
5766
  "use strict";
5699
5767
  "use client";
5700
5768
  init_routes();
5769
+ init_http();
5701
5770
  init_DepositContext();
5702
5771
  init_provider();
5703
5772
  init_core2();
5704
- init_constants();
5705
5773
  init_hooks();
5706
5774
  FAST_POLL_INTERVAL_MS = 1500;
5707
5775
  NORMAL_POLL_INTERVAL_MS = 2500;
@@ -5783,6 +5851,20 @@ var init_eipWallets = __esm({
5783
5851
  "src/wallets/eipWallets.ts"() {
5784
5852
  "use strict";
5785
5853
  CHAIN_PARAMS = {
5854
+ 1: {
5855
+ chainIdHex: "0x1",
5856
+ chainName: "Ethereum",
5857
+ rpcUrls: ["https://eth.llamarpc.com"],
5858
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
5859
+ blockExplorerUrls: ["https://etherscan.io"]
5860
+ },
5861
+ 137: {
5862
+ chainIdHex: "0x89",
5863
+ chainName: "Polygon",
5864
+ rpcUrls: ["https://polygon-rpc.com"],
5865
+ nativeCurrency: { name: "POL", symbol: "POL", decimals: 18 },
5866
+ blockExplorerUrls: ["https://polygonscan.com"]
5867
+ },
5786
5868
  8453: {
5787
5869
  chainIdHex: "0x2105",
5788
5870
  chainName: "Base",
@@ -8887,9 +8969,6 @@ function resolveConfig(input) {
8887
8969
  const theme = input.theme ?? "system";
8888
8970
  const messages = deepMerge(DEFAULT_MESSAGES, input.messages);
8889
8971
  const retry = {
8890
- autoRetry: input.retry?.autoRetry ?? DEFAULT_RETRY_CONFIG.autoRetry,
8891
- maxRetries: input.retry?.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,
8892
- baseDelayMs: input.retry?.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,
8893
8972
  approachingThreshold: input.retry?.approachingThreshold ?? DEFAULT_RETRY_CONFIG.approachingThreshold,
8894
8973
  onRateLimitInfo: input.retry?.onRateLimitInfo,
8895
8974
  onRateLimited: input.retry?.onRateLimited,
@@ -9063,18 +9142,27 @@ async function waitForTxConfirmation(chainId, txHash) {
9063
9142
  }
9064
9143
  throw new Error("Timed out waiting for approval confirmation");
9065
9144
  }
9066
- async function ensureApprovals(w, approvals, fallbackChainId) {
9145
+ function requiredApprovalAmount(planAmountWei, routeAmountWei) {
9146
+ if (planAmountWei >= UNLIMITED_APPROVAL_FLOOR && routeAmountWei !== void 0 && routeAmountWei > 0n) {
9147
+ return routeAmountWei;
9148
+ }
9149
+ return planAmountWei;
9150
+ }
9151
+ function approvalSatisfied(currentAllowance, requiredWei) {
9152
+ if (requiredWei === 0n) return currentAllowance === 0n;
9153
+ return currentAllowance >= requiredWei;
9154
+ }
9155
+ async function ensureApprovals(w, approvals, fallbackChainId, intentId, routeAmountWei) {
9067
9156
  if (!approvals || approvals.length === 0) return;
9068
9157
  const owner = await w.getAddress();
9069
- for (const approval of approvals) {
9158
+ for (const [stepIndex, approval] of approvals.entries()) {
9070
9159
  const spender = approval.spender;
9071
9160
  const tokenAddress = approval.tokenAddress;
9072
9161
  const amount = approval.amount;
9073
9162
  if (!spender || !tokenAddress || !amount) continue;
9074
9163
  const chainId = approval.chainId || String(fallbackChainId ?? "");
9075
9164
  if (!chainId) continue;
9076
- const amountWei = BigInt(amount);
9077
- if (amountWei === 0n) continue;
9165
+ const amountWei = requiredApprovalAmount(BigInt(amount), routeAmountWei);
9078
9166
  let currentAllowance = 0n;
9079
9167
  try {
9080
9168
  const { allowance } = await getEVMAllowance({
@@ -9086,7 +9174,7 @@ async function ensureApprovals(w, approvals, fallbackChainId) {
9086
9174
  currentAllowance = BigInt(allowance || "0");
9087
9175
  } catch {
9088
9176
  }
9089
- if (currentAllowance >= amountWei) continue;
9177
+ if (approvalSatisfied(currentAllowance, amountWei)) continue;
9090
9178
  const data = encodeFunctionData({
9091
9179
  abi: erc20ApproveAbi,
9092
9180
  functionName: "approve",
@@ -9098,6 +9186,10 @@ async function ensureApprovals(w, approvals, fallbackChainId) {
9098
9186
  value: 0n,
9099
9187
  chainId: Number.isFinite(Number(chainId)) ? Number(chainId) : void 0
9100
9188
  });
9189
+ if (intentId) {
9190
+ void submitStepReceipt(intentId, stepIndex, hash).catch(() => {
9191
+ });
9192
+ }
9101
9193
  await waitForTxConfirmation(chainId, hash);
9102
9194
  }
9103
9195
  }
@@ -9107,7 +9199,7 @@ function isUserRejected(e2) {
9107
9199
  const msg = String(e2?.message || e2)?.toLowerCase?.() || "";
9108
9200
  return msg.includes("user rejected") || msg.includes("user denied");
9109
9201
  }
9110
- async function sendRouteTransaction(b, fallbackChainId) {
9202
+ async function sendRouteTransaction(b, fallbackChainId, options) {
9111
9203
  const w = walletManager.wallet;
9112
9204
  if (!w) throw new Error("Trustware.wallet not configured");
9113
9205
  const txReq = b.txReq;
@@ -9134,11 +9226,13 @@ async function sendRouteTransaction(b, fallbackChainId) {
9134
9226
  }
9135
9227
  }
9136
9228
  }
9137
- if (!validatedSponsorship) {
9229
+ if (!validatedSponsorship && !options?.approvalsEnsured) {
9138
9230
  await ensureApprovals(
9139
9231
  w,
9140
9232
  b.route?.execution?.approvals,
9141
- Number.isFinite(target) ? target : void 0
9233
+ Number.isFinite(target) ? target : void 0,
9234
+ b.intentId,
9235
+ BigInt(b.route?.estimate?.fromAmount ?? "0")
9142
9236
  );
9143
9237
  }
9144
9238
  if (w.type === "eip1193") {
@@ -9237,7 +9331,7 @@ async function runTopUp(params) {
9237
9331
  }
9238
9332
  }
9239
9333
  }
9240
- var erc20ApproveAbi;
9334
+ var erc20ApproveAbi, UNLIMITED_APPROVAL_FLOOR;
9241
9335
  var init_tx = __esm({
9242
9336
  "src/core/tx.ts"() {
9243
9337
  "use strict";
@@ -9247,6 +9341,7 @@ var init_tx = __esm({
9247
9341
  erc20ApproveAbi = parseAbi([
9248
9342
  "function approve(address spender, uint256 amount) returns (bool)"
9249
9343
  ]);
9344
+ UNLIMITED_APPROVAL_FLOOR = 1n << 255n;
9250
9345
  }
9251
9346
  });
9252
9347
 
@@ -9392,6 +9487,7 @@ var init_core = __esm({
9392
9487
  buildRoute,
9393
9488
  buildDepositAddress,
9394
9489
  submitReceipt,
9490
+ submitStepReceipt,
9395
9491
  getStatus,
9396
9492
  pollStatus,
9397
9493
  getBalances,
@@ -30548,7 +30644,7 @@ import {
30548
30644
  keccak256 as keccak2566,
30549
30645
  stringToHex
30550
30646
  } from "viem";
30551
- async function fetchWithTimeout(url, init) {
30647
+ async function fetchOnceWithTimeout(url, init) {
30552
30648
  const controller = new AbortController();
30553
30649
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
30554
30650
  try {
@@ -30569,6 +30665,9 @@ async function fetchWithTimeout(url, init) {
30569
30665
  clearTimeout(timer);
30570
30666
  }
30571
30667
  }
30668
+ async function fetchWithTimeout(url, init) {
30669
+ return withRateLimitRetry(() => fetchOnceWithTimeout(url, init));
30670
+ }
30572
30671
  function extractRevertHex(err) {
30573
30672
  if (typeof err.data === "string" && err.data.startsWith("0x"))
30574
30673
  return err.data;
@@ -31068,7 +31167,7 @@ async function estimateRelayFeeInToken(valueWei, fromAmountWei, fromAmountUSD, c
31068
31167
  tokenUnitsPerDollar = Number(fromAmountWei) / fromUSD;
31069
31168
  } else if (fromToken && fromDecimals !== void 0) {
31070
31169
  try {
31071
- const resp = await fetch(
31170
+ const resp = await rateLimitedFetch(
31072
31171
  `${apiBase()}/v1/price/token?chainId=${cid}&address=${fromToken}`,
31073
31172
  { headers: jsonHeaders() }
31074
31173
  );
@@ -31084,9 +31183,10 @@ async function estimateRelayFeeInToken(valueWei, fromAmountWei, fromAmountUSD, c
31084
31183
  return 0n;
31085
31184
  }
31086
31185
  try {
31087
- const resp = await fetch(`${apiBase()}/v1/price/native?chainId=${cid}`, {
31088
- headers: jsonHeaders()
31089
- });
31186
+ const resp = await rateLimitedFetch(
31187
+ `${apiBase()}/v1/price/native?chainId=${cid}`,
31188
+ { headers: jsonHeaders() }
31189
+ );
31090
31190
  if (!resp.ok) throw new Error(`native price HTTP ${resp.status}`);
31091
31191
  const json = await resp.json();
31092
31192
  const ethPriceUSD = parseFloat(json.data?.priceUSD ?? "0");
@@ -31594,7 +31694,7 @@ function useTransactionActionModel({
31594
31694
  }) {
31595
31695
  const feeDataCacheRef = useRef13({});
31596
31696
  const { isSubmitting, submitTransaction } = useTransactionSubmit();
31597
- const { trackEvent } = useGTM(GTM_ID);
31697
+ const { trackEvent } = useGTMTracker();
31598
31698
  const destinationConfig = (() => {
31599
31699
  try {
31600
31700
  return Trustware.getConfig();
@@ -32027,7 +32127,6 @@ var init_useTransactionActionModel = __esm({
32027
32127
  init_chainHelpers();
32028
32128
  init_utils2();
32029
32129
  init_hooks();
32030
- init_constants();
32031
32130
  }
32032
32131
  });
32033
32132
 
@@ -33379,6 +33478,16 @@ async function waitForApprovalConfirmation(chainId, txHash) {
33379
33478
  }
33380
33479
  throw new Error("Timed out waiting for approval confirmation");
33381
33480
  }
33481
+ async function requireActiveAddress(wallet, plannedFor) {
33482
+ if (!wallet) throw new Error("Wallet not connected. Please reconnect.");
33483
+ const active = await wallet.getAddress();
33484
+ if (active?.toLowerCase() !== plannedFor?.toLowerCase()) {
33485
+ throw new Error(
33486
+ "Wallet account changed. Please review and confirm the swap again."
33487
+ );
33488
+ }
33489
+ return active;
33490
+ }
33382
33491
  function useSwapExecution(fromChain) {
33383
33492
  const [state, setState] = useState29({
33384
33493
  txStatus: "idle",
@@ -33435,7 +33544,15 @@ function useSwapExecution(fromChain) {
33435
33544
  if (tx.status === "bridging") {
33436
33545
  setState((p) => ({ ...p, txStatus: "bridging" }));
33437
33546
  }
33438
- } catch {
33547
+ } catch (err) {
33548
+ if (abortRef.current) return;
33549
+ if (isNotFoundError(err)) {
33550
+ clearPolling();
33551
+ const msg = "Transaction session expired. Please try again.";
33552
+ setState((p) => ({ ...p, txStatus: "error", errorMessage: msg }));
33553
+ onError(msg);
33554
+ return;
33555
+ }
33439
33556
  }
33440
33557
  if (abortRef.current) return;
33441
33558
  pollCountRef.current += 1;
@@ -33461,24 +33578,55 @@ function useSwapExecution(fromChain) {
33461
33578
  }
33462
33579
  const txReq = routeResult.txReq;
33463
33580
  const chainIdStr = String(txReq?.chainId ?? fromChain?.chainId ?? "");
33464
- const spender = txReq?.to ?? txReq?.target;
33465
33581
  const amountWei = BigInt(routeResult.route?.estimate?.fromAmount ?? "0");
33466
- if (!spender || !chainIdStr || amountWei === 0n) {
33582
+ const plannedApprovals = routeResult.route?.execution?.approvals ?? [];
33583
+ const toCheck = plannedApprovals.length ? plannedApprovals.flatMap((a) => {
33584
+ const token = a.tokenAddress ?? fromTokenAddress;
33585
+ const required = requiredApprovalAmount(
33586
+ BigInt(a.amount || "0"),
33587
+ amountWei
33588
+ );
33589
+ if (!token || !a.spender) return [];
33590
+ return [
33591
+ {
33592
+ chainId: a.chainId || chainIdStr,
33593
+ token,
33594
+ spender: a.spender,
33595
+ required
33596
+ }
33597
+ ];
33598
+ }) : (() => {
33599
+ const spender = txReq?.to ?? txReq?.target;
33600
+ if (!spender) return [];
33601
+ return [
33602
+ {
33603
+ chainId: chainIdStr,
33604
+ token: fromTokenAddress,
33605
+ spender,
33606
+ required: amountWei
33607
+ }
33608
+ ];
33609
+ })();
33610
+ if (toCheck.length === 0 || !chainIdStr || amountWei === 0n) {
33467
33611
  setState((p) => ({ ...p, allowanceStatus: "unknown" }));
33468
33612
  return;
33469
33613
  }
33470
33614
  setState((p) => ({ ...p, allowanceStatus: "checking" }));
33471
33615
  try {
33472
- const { allowance } = await getEVMAllowance({
33473
- chainId: chainIdStr,
33474
- tokenAddress: fromTokenAddress,
33475
- ownerAddress: walletAddress,
33476
- spenderAddress: spender
33477
- });
33478
- const allowanceWei = BigInt(allowance || "0");
33616
+ const results = await Promise.all(
33617
+ toCheck.map(async (c) => {
33618
+ const { allowance } = await getEVMAllowance({
33619
+ chainId: c.chainId,
33620
+ tokenAddress: c.token,
33621
+ ownerAddress: walletAddress,
33622
+ spenderAddress: c.spender
33623
+ });
33624
+ return approvalSatisfied(BigInt(allowance || "0"), c.required);
33625
+ })
33626
+ );
33479
33627
  setState((p) => ({
33480
33628
  ...p,
33481
- allowanceStatus: allowanceWei >= amountWei ? "sufficient" : "needed"
33629
+ allowanceStatus: results.every(Boolean) ? "sufficient" : "needed"
33482
33630
  }));
33483
33631
  } catch {
33484
33632
  setState((p) => ({ ...p, allowanceStatus: "needed" }));
@@ -33582,19 +33730,49 @@ function useSwapExecution(fromChain) {
33582
33730
  }
33583
33731
  }
33584
33732
  try {
33585
- if (!isNative && walletAddress && spender && chainIdStr && fromTokenAddress) {
33733
+ const plannedApprovals = routeResult.route?.execution?.approvals ?? [];
33734
+ const requiredApprovals = plannedApprovals.length ? plannedApprovals.flatMap((a, index) => {
33735
+ const token = a.tokenAddress ?? fromTokenAddress;
33736
+ const approvalSpender = a.spender;
33737
+ if (!token || !approvalSpender) return [];
33738
+ const amountWei = requiredApprovalAmount(
33739
+ BigInt(a.amount || "0"),
33740
+ fromAmountWei
33741
+ );
33742
+ return [
33743
+ {
33744
+ stepIndex: index,
33745
+ token,
33746
+ spender: approvalSpender,
33747
+ amountWei,
33748
+ chainId: a.chainId || chainIdStr
33749
+ }
33750
+ ];
33751
+ }) : spender && fromTokenAddress ? [
33752
+ {
33753
+ stepIndex: null,
33754
+ token: fromTokenAddress,
33755
+ spender,
33756
+ amountWei: fromAmountWei,
33757
+ chainId: chainIdStr
33758
+ }
33759
+ ] : [];
33760
+ const ownsApprovals = !isNative && !!walletAddress && !!chainIdStr;
33761
+ const activeAddress = ownsApprovals ? await requireActiveAddress(wallet, walletAddress) : void 0;
33762
+ for (const required of requiredApprovals) {
33763
+ if (!ownsApprovals) break;
33586
33764
  let allowanceWei = 0n;
33587
33765
  try {
33588
33766
  const { allowance } = await getEVMAllowance({
33589
- chainId: chainIdStr,
33590
- tokenAddress: fromTokenAddress,
33591
- ownerAddress: walletAddress,
33592
- spenderAddress: spender
33767
+ chainId: required.chainId,
33768
+ tokenAddress: required.token,
33769
+ ownerAddress: activeAddress,
33770
+ spenderAddress: required.spender
33593
33771
  });
33594
33772
  allowanceWei = BigInt(allowance || "0");
33595
33773
  } catch {
33596
33774
  }
33597
- if (allowanceWei < fromAmountWei) {
33775
+ if (!approvalSatisfied(allowanceWei, required.amountWei)) {
33598
33776
  setState((p) => ({ ...p, txStatus: "approving" }));
33599
33777
  if (!wallet || wallet.ecosystem !== "evm") {
33600
33778
  throw new Error("EVM wallet required for token approval");
@@ -33602,7 +33780,7 @@ function useSwapExecution(fromChain) {
33602
33780
  if (!chainIdStr || chainIdStr === "0") {
33603
33781
  throw new Error("Invalid chain ID for token approval");
33604
33782
  }
33605
- const targetChain = Number(txReq.chainId ?? fromChain?.chainId);
33783
+ const targetChain = Number(required.chainId ?? txReq.chainId);
33606
33784
  if (Number.isFinite(targetChain)) {
33607
33785
  const current = await wallet.getChainId();
33608
33786
  if (current !== targetChain) {
@@ -33614,18 +33792,18 @@ function useSwapExecution(fromChain) {
33614
33792
  }
33615
33793
  const approveAmount = maxApproval ? BigInt(
33616
33794
  "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
33617
- ) : fromAmountWei;
33795
+ ) : required.amountWei;
33618
33796
  const data = encodeFunctionData10({
33619
33797
  abi: erc20Abi2,
33620
33798
  functionName: "approve",
33621
- args: [spender, approveAmount]
33799
+ args: [required.spender, approveAmount]
33622
33800
  });
33623
33801
  let approvalHash;
33624
33802
  if (wallet.type === "eip1193") {
33625
33803
  const from = await wallet.getAddress();
33626
33804
  const params = {
33627
33805
  from,
33628
- to: fromTokenAddress,
33806
+ to: required.token,
33629
33807
  data,
33630
33808
  value: "0x0"
33631
33809
  };
@@ -33638,14 +33816,22 @@ function useSwapExecution(fromChain) {
33638
33816
  });
33639
33817
  } else {
33640
33818
  const response = await wallet.sendTransaction({
33641
- to: fromTokenAddress,
33819
+ to: required.token,
33642
33820
  data,
33643
33821
  value: 0n,
33644
33822
  chainId: Number.isFinite(targetChain) ? targetChain : void 0
33645
33823
  });
33646
33824
  approvalHash = response.hash;
33647
33825
  }
33648
- await waitForApprovalConfirmation(chainIdStr, approvalHash);
33826
+ if (required.stepIndex !== null) {
33827
+ void submitStepReceipt(
33828
+ routeResult.intentId,
33829
+ required.stepIndex,
33830
+ approvalHash
33831
+ ).catch(() => {
33832
+ });
33833
+ }
33834
+ await waitForApprovalConfirmation(required.chainId, approvalHash);
33649
33835
  setState((p) => ({
33650
33836
  ...p,
33651
33837
  txStatus: "confirming",
@@ -33653,9 +33839,13 @@ function useSwapExecution(fromChain) {
33653
33839
  }));
33654
33840
  }
33655
33841
  }
33842
+ if (ownsApprovals) {
33843
+ await requireActiveAddress(wallet, walletAddress);
33844
+ }
33656
33845
  const hash = await Trustware.sendRouteTransaction(
33657
33846
  routeResult,
33658
- numericChainId
33847
+ numericChainId,
33848
+ { approvalsEnsured: ownsApprovals }
33659
33849
  );
33660
33850
  try {
33661
33851
  await submitReceipt(routeResult.intentId, hash);
@@ -33723,6 +33913,8 @@ var init_useSwapExecution = __esm({
33723
33913
  "use client";
33724
33914
  init_core2();
33725
33915
  init_routes();
33916
+ init_http();
33917
+ init_tx();
33726
33918
  init_sdkRpc();
33727
33919
  init_chainHelpers();
33728
33920
  FAST_POLL_MS = 1500;
@@ -33734,9 +33926,14 @@ var init_useSwapExecution = __esm({
33734
33926
 
33735
33927
  // src/core/forex.ts
33736
33928
  async function fetchForexRates(base2 = "USD") {
33737
- const r = await fetch(`${apiBase()}/v1/forex?base=${base2}`, {
33738
- headers: jsonHeaders()
33739
- });
33929
+ let r;
33930
+ try {
33931
+ r = await rateLimitedFetch(`${apiBase()}/v1/forex?base=${base2}`, {
33932
+ headers: jsonHeaders()
33933
+ });
33934
+ } catch {
33935
+ return {};
33936
+ }
33740
33937
  if (!r.ok) return {};
33741
33938
  const j = await r.json();
33742
33939
  const rates = j?.data?.rates ?? j?.rates ?? j?.data ?? {};
@@ -39687,6 +39884,7 @@ function WidgetInner({
39687
39884
  const { resolvedTheme } = useDepositUi();
39688
39885
  const { status, revalidate, errors } = useTrustware();
39689
39886
  const [showConfirmDialog, setShowConfirmDialog] = useState35(false);
39887
+ useGTM(GTM_ID);
39690
39888
  const handleCloseRequest = useCallback23(() => {
39691
39889
  if (ACTIVE_TRANSACTION_STATUSES.includes(transactionStatus)) {
39692
39890
  setShowConfirmDialog(true);
@@ -39760,6 +39958,8 @@ var init_TrustwareWidgetV2 = __esm({
39760
39958
  init_provider();
39761
39959
  init_manager();
39762
39960
  init_useTrustwareConfig();
39961
+ init_hooks();
39962
+ init_constants();
39763
39963
  init_swap();
39764
39964
  widgetContentContainerStyle = {
39765
39965
  position: "relative",