@trustware/sdk-staging 1.1.9-staging.7 → 1.1.11-staging.1

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.
package/dist/index.cjs CHANGED
@@ -464,6 +464,20 @@ var init_eipWallets = __esm({
464
464
  "src/wallets/eipWallets.ts"() {
465
465
  "use strict";
466
466
  CHAIN_PARAMS = {
467
+ 1: {
468
+ chainIdHex: "0x1",
469
+ chainName: "Ethereum",
470
+ rpcUrls: ["https://eth.llamarpc.com"],
471
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
472
+ blockExplorerUrls: ["https://etherscan.io"]
473
+ },
474
+ 137: {
475
+ chainIdHex: "0x89",
476
+ chainName: "Polygon",
477
+ rpcUrls: ["https://polygon-rpc.com"],
478
+ nativeCurrency: { name: "POL", symbol: "POL", decimals: 18 },
479
+ blockExplorerUrls: ["https://polygonscan.com"]
480
+ },
467
481
  8453: {
468
482
  chainIdHex: "0x2105",
469
483
  chainName: "Base",
@@ -495,7 +509,7 @@ var init_constants = __esm({
495
509
  "src/constants.ts"() {
496
510
  "use strict";
497
511
  SDK_NAME = "@trustware/sdk";
498
- SDK_VERSION = "1.1.9-staging.7";
512
+ SDK_VERSION = "1.1.11-staging.1";
499
513
  API_ROOT = "https://bv-staging-api.trustware.io";
500
514
  GTM_ID = "GTM-TZDGNCXB";
501
515
  API_PREFIX = "/api";
@@ -539,6 +553,9 @@ async function assertOK(r) {
539
553
  }
540
554
  throw new Error(`HTTP ${r.status}: ${msg}`);
541
555
  }
556
+ function isNotFoundError(err) {
557
+ return err instanceof Error && err.message.startsWith("HTTP 404");
558
+ }
542
559
  async function validateSdkAccess() {
543
560
  const r = await fetch(`${apiBase()}/sdk/validate`, {
544
561
  method: "GET",
@@ -1136,12 +1153,33 @@ var init_deepLink = __esm({
1136
1153
  });
1137
1154
 
1138
1155
  // src/wallets/metadata.ts
1139
- var WALLETS;
1156
+ var POPULAR_ORDER, WALLETS;
1140
1157
  var init_metadata = __esm({
1141
1158
  "src/wallets/metadata.ts"() {
1142
1159
  "use strict";
1143
1160
  init_deepLink();
1144
1161
  init_constants();
1162
+ POPULAR_ORDER = [
1163
+ "rabby",
1164
+ "metamask",
1165
+ "walletconnect",
1166
+ "rainbow",
1167
+ "phantom-evm",
1168
+ "phantom-solana",
1169
+ "metamask-solana",
1170
+ "solflare",
1171
+ "okx",
1172
+ "brave",
1173
+ "coinbase",
1174
+ "zerion",
1175
+ "taho",
1176
+ "trust",
1177
+ "bitget",
1178
+ "backpack",
1179
+ "imtoken",
1180
+ "safe",
1181
+ "kucoin"
1182
+ ];
1145
1183
  WALLETS = [
1146
1184
  {
1147
1185
  id: "metamask",
@@ -4378,6 +4416,27 @@ var init_wallets = __esm({
4378
4416
  });
4379
4417
 
4380
4418
  // src/core/routes.ts
4419
+ function assertValidPostHook(hooks) {
4420
+ const postHook = hooks?.postHook;
4421
+ if (!postHook) return;
4422
+ if (!postHook.target?.trim()) {
4423
+ throw new Error("hooks.postHook.target is required.");
4424
+ }
4425
+ if (!postHook.callData?.trim()) {
4426
+ throw new Error("hooks.postHook.callData is required.");
4427
+ }
4428
+ if (postHook.fullAmount) {
4429
+ if (postHook.amountInputPos === void 0) {
4430
+ throw new Error(
4431
+ "hooks.postHook.amountInputPos is required when fullAmount is true."
4432
+ );
4433
+ }
4434
+ } else if (!postHook.fundAmount?.trim()) {
4435
+ throw new Error(
4436
+ "hooks.postHook.fundAmount is required unless fullAmount is set."
4437
+ );
4438
+ }
4439
+ }
4381
4440
  function isEvmTxRequest(txReq) {
4382
4441
  return Boolean(txReq?.data && (txReq.to || txReq.target));
4383
4442
  }
@@ -4396,6 +4455,7 @@ async function buildRoute(body, signal) {
4396
4455
  if (!addressValidation.isValid) {
4397
4456
  throw new Error(addressValidation.error || "Invalid route addresses.");
4398
4457
  }
4458
+ assertValidPostHook(body.hooks);
4399
4459
  const cfg = TrustwareConfigStore.get();
4400
4460
  const url = `${apiBase()}/v1/routes/route`;
4401
4461
  const payload = {
@@ -4434,6 +4494,7 @@ async function buildRoute(body, signal) {
4434
4494
  return { intentId, txReq, actions, finalExchangeRate, route, sponsorship };
4435
4495
  }
4436
4496
  async function buildDepositAddress(body, signal) {
4497
+ assertValidPostHook(body.hooks);
4437
4498
  const cfg = TrustwareConfigStore.get();
4438
4499
  const url = `${apiBase()}/v1/routes/deposit-address`;
4439
4500
  const payload = {
@@ -4492,6 +4553,19 @@ async function submitReceipt(intentId, txHash, sponsorshipRequestId, eoaAddress)
4492
4553
  const j = await r.json();
4493
4554
  return j.data;
4494
4555
  }
4556
+ async function submitStepReceipt(intentId, stepIndex, txHash) {
4557
+ const r = await rateLimitedFetch(
4558
+ `${apiBase()}/v1/route-intent/${intentId}/steps/${stepIndex}/receipt`,
4559
+ {
4560
+ method: "POST",
4561
+ headers: jsonHeaders({ "Idempotency-Key": txHash }),
4562
+ body: JSON.stringify({ txHash })
4563
+ }
4564
+ );
4565
+ await assertOK(r);
4566
+ const j = await r.json();
4567
+ return j.data;
4568
+ }
4495
4569
  async function getStatus(intentId) {
4496
4570
  const r = await rateLimitedFetch(
4497
4571
  `${apiBase()}/v1/route-intent/${intentId}/status`,
@@ -5186,13 +5260,107 @@ function backendChainId(chain, fallback) {
5186
5260
  const preferred = chain?.networkIdentifier ?? chain?.chainId ?? chain?.id;
5187
5261
  return String(preferred ?? fallback ?? "");
5188
5262
  }
5263
+ function sleep2(ms) {
5264
+ return new Promise((resolve) => setTimeout(resolve, ms));
5265
+ }
5266
+ async function sendEvmTx(w, tx) {
5267
+ if (w.type === "eip1193") {
5268
+ const from = await w.getAddress();
5269
+ const params = {
5270
+ from,
5271
+ to: tx.to,
5272
+ data: tx.data,
5273
+ value: tx.value ? `0x${tx.value.toString(16)}` : "0x0"
5274
+ };
5275
+ if (tx.chainId !== void 0) {
5276
+ params.chainId = `0x${tx.chainId.toString(16)}`;
5277
+ }
5278
+ const hash = await w.request({
5279
+ method: "eth_sendTransaction",
5280
+ params: [params]
5281
+ });
5282
+ return hash;
5283
+ }
5284
+ const response = await w.sendTransaction({
5285
+ to: tx.to,
5286
+ data: tx.data,
5287
+ value: tx.value,
5288
+ chainId: tx.chainId
5289
+ });
5290
+ return response.hash;
5291
+ }
5292
+ async function waitForTxConfirmation(chainId, txHash) {
5293
+ const timeoutMs = 12e4;
5294
+ const intervalMs = 2e3;
5295
+ const started = Date.now();
5296
+ while (Date.now() - started < timeoutMs) {
5297
+ const status = await getEVMTxStatus({ chainId, txHash });
5298
+ if (status.status === "success") return;
5299
+ if (status.status === "reverted") {
5300
+ throw new Error("Approval transaction reverted");
5301
+ }
5302
+ await sleep2(intervalMs);
5303
+ }
5304
+ throw new Error("Timed out waiting for approval confirmation");
5305
+ }
5306
+ function requiredApprovalAmount(planAmountWei, routeAmountWei) {
5307
+ if (planAmountWei >= UNLIMITED_APPROVAL_FLOOR && routeAmountWei !== void 0 && routeAmountWei > 0n) {
5308
+ return routeAmountWei;
5309
+ }
5310
+ return planAmountWei;
5311
+ }
5312
+ function approvalSatisfied(currentAllowance, requiredWei) {
5313
+ if (requiredWei === 0n) return currentAllowance === 0n;
5314
+ return currentAllowance >= requiredWei;
5315
+ }
5316
+ async function ensureApprovals(w, approvals, fallbackChainId, intentId, routeAmountWei) {
5317
+ if (!approvals || approvals.length === 0) return;
5318
+ const owner = await w.getAddress();
5319
+ for (const [stepIndex, approval] of approvals.entries()) {
5320
+ const spender = approval.spender;
5321
+ const tokenAddress = approval.tokenAddress;
5322
+ const amount = approval.amount;
5323
+ if (!spender || !tokenAddress || !amount) continue;
5324
+ const chainId = approval.chainId || String(fallbackChainId ?? "");
5325
+ if (!chainId) continue;
5326
+ const amountWei = requiredApprovalAmount(BigInt(amount), routeAmountWei);
5327
+ let currentAllowance = 0n;
5328
+ try {
5329
+ const { allowance } = await getEVMAllowance({
5330
+ chainId,
5331
+ tokenAddress,
5332
+ ownerAddress: owner,
5333
+ spenderAddress: spender
5334
+ });
5335
+ currentAllowance = BigInt(allowance || "0");
5336
+ } catch {
5337
+ }
5338
+ if (approvalSatisfied(currentAllowance, amountWei)) continue;
5339
+ const data = (0, import_viem2.encodeFunctionData)({
5340
+ abi: erc20ApproveAbi,
5341
+ functionName: "approve",
5342
+ args: [spender, amountWei]
5343
+ });
5344
+ const hash = await sendEvmTx(w, {
5345
+ to: tokenAddress,
5346
+ data,
5347
+ value: 0n,
5348
+ chainId: Number.isFinite(Number(chainId)) ? Number(chainId) : void 0
5349
+ });
5350
+ if (intentId) {
5351
+ void submitStepReceipt(intentId, stepIndex, hash).catch(() => {
5352
+ });
5353
+ }
5354
+ await waitForTxConfirmation(chainId, hash);
5355
+ }
5356
+ }
5189
5357
  function isUserRejected(e2) {
5190
5358
  const code = e2?.code ?? e2?.data?.code;
5191
5359
  if (code === 4001) return true;
5192
5360
  const msg = String(e2?.message || e2)?.toLowerCase?.() || "";
5193
5361
  return msg.includes("user rejected") || msg.includes("user denied");
5194
5362
  }
5195
- async function sendRouteTransaction(b, fallbackChainId) {
5363
+ async function sendRouteTransaction(b, fallbackChainId, options) {
5196
5364
  const w = walletManager.wallet;
5197
5365
  if (!w) throw new Error("Trustware.wallet not configured");
5198
5366
  const txReq = b.txReq;
@@ -5219,6 +5387,15 @@ async function sendRouteTransaction(b, fallbackChainId) {
5219
5387
  }
5220
5388
  }
5221
5389
  }
5390
+ if (!validatedSponsorship && !options?.approvalsEnsured) {
5391
+ await ensureApprovals(
5392
+ w,
5393
+ b.route?.execution?.approvals,
5394
+ Number.isFinite(target) ? target : void 0,
5395
+ b.intentId,
5396
+ BigInt(b.route?.estimate?.fromAmount ?? "0")
5397
+ );
5398
+ }
5222
5399
  if (w.type === "eip1193") {
5223
5400
  const from = await w.getAddress();
5224
5401
  const hexValue = value ? `0x${value.toString(16)}` : "0x0";
@@ -5315,13 +5492,18 @@ async function runTopUp(params) {
5315
5492
  }
5316
5493
  }
5317
5494
  }
5318
- var import_viem2;
5495
+ var import_viem2, erc20ApproveAbi, UNLIMITED_APPROVAL_FLOOR;
5319
5496
  var init_tx = __esm({
5320
5497
  "src/core/tx.ts"() {
5321
5498
  "use strict";
5322
5499
  init_wallets();
5323
5500
  init_routes();
5501
+ init_sdkRpc();
5324
5502
  import_viem2 = require("viem");
5503
+ erc20ApproveAbi = (0, import_viem2.parseAbi)([
5504
+ "function approve(address spender, uint256 amount) returns (bool)"
5505
+ ]);
5506
+ UNLIMITED_APPROVAL_FLOOR = 1n << 255n;
5325
5507
  }
5326
5508
  });
5327
5509
 
@@ -7595,6 +7777,7 @@ var init_core = __esm({
7595
7777
  buildRoute,
7596
7778
  buildDepositAddress,
7597
7779
  submitReceipt,
7780
+ submitStepReceipt,
7598
7781
  getStatus,
7599
7782
  pollStatus,
7600
7783
  getBalances,
@@ -8823,75 +9006,17 @@ var init_useTransactionSubmit = __esm({
8823
9006
  });
8824
9007
 
8825
9008
  // src/hooks/useGTM.ts
8826
- function useGTM(gtmId) {
8827
- const isInitialized = (0, import_react15.useRef)(false);
8828
- const scriptRef = (0, import_react15.useRef)(null);
8829
- const isGA4Allowed = (0, import_react15.useCallback)(() => {
8830
- try {
8831
- return Trustware.getConfig().features.shouldAllowGA4;
8832
- } catch {
8833
- return false;
8834
- }
8835
- }, []);
8836
- (0, import_react15.useEffect)(() => {
8837
- if (!isGA4Allowed()) {
8838
- console.warn(
8839
- "useGTM: GA4 tracking is disabled or Trustware config not initialized."
8840
- );
8841
- return;
8842
- }
8843
- if (!gtmId || typeof gtmId !== "string") {
8844
- console.error(
8845
- "useGTM: A valid GTM Container ID is required (format: GTM-XXXXX)."
8846
- );
8847
- return;
8848
- }
8849
- if (isInitialized.current) return;
8850
- const alreadyLoaded = document.querySelector(
8851
- `script[src*="googletagmanager.com/gtm.js?id=${gtmId}"]`
8852
- );
8853
- if (alreadyLoaded) {
8854
- window.dataLayer = window.dataLayer || [];
8855
- isInitialized.current = true;
8856
- return;
8857
- }
8858
- window.dataLayer = window.dataLayer || [];
8859
- window.dataLayer.push({
8860
- "gtm.start": (/* @__PURE__ */ new Date()).getTime(),
8861
- event: "gtm.js"
8862
- });
8863
- const firstScript = document.getElementsByTagName("script")[0];
8864
- const script = document.createElement("script");
8865
- script.async = true;
8866
- script.src = `https://www.googletagmanager.com/gtm.js?id=${gtmId}`;
8867
- firstScript.parentNode?.insertBefore(script, firstScript);
8868
- scriptRef.current = script;
8869
- isInitialized.current = true;
8870
- return () => {
8871
- if (scriptRef.current) {
8872
- scriptRef.current.parentNode?.removeChild(scriptRef.current);
8873
- scriptRef.current = null;
8874
- }
8875
- isInitialized.current = false;
8876
- };
8877
- }, [gtmId, isGA4Allowed]);
8878
- const addNoscriptIframe = (0, import_react15.useCallback)(() => {
8879
- if (!isGA4Allowed()) return;
8880
- if (document.querySelector('iframe[src*="googletagmanager.com/ns.html"]'))
8881
- return;
8882
- const noscript = document.createElement("noscript");
8883
- const iframe = document.createElement("iframe");
8884
- iframe.src = `https://www.googletagmanager.com/ns.html?id=${gtmId}`;
8885
- iframe.height = "0";
8886
- iframe.width = "0";
8887
- iframe.style.display = "none";
8888
- iframe.style.visibility = "hidden";
8889
- noscript.appendChild(iframe);
8890
- document.body.insertBefore(noscript, document.body.firstChild);
8891
- }, [gtmId, isGA4Allowed]);
9009
+ function isGA4Allowed() {
9010
+ try {
9011
+ return Trustware.getConfig().features.shouldAllowGA4;
9012
+ } catch {
9013
+ return false;
9014
+ }
9015
+ }
9016
+ function useGTMTracker() {
8892
9017
  const trackEvent = (0, import_react15.useCallback)(
8893
9018
  (eventName, eventParams = {}) => {
8894
- if (!isInitialized.current) {
9019
+ if (!isContainerInitialized) {
8895
9020
  console.warn(
8896
9021
  "useGTM: Not initialized. Ensure a valid GTM Container ID was provided."
8897
9022
  );
@@ -8918,7 +9043,7 @@ function useGTM(gtmId) {
8918
9043
  );
8919
9044
  const setUserProperty = (0, import_react15.useCallback)(
8920
9045
  (propertyName, value) => {
8921
- if (!isInitialized.current) {
9046
+ if (!isContainerInitialized) {
8922
9047
  console.warn(
8923
9048
  "useGTM: Not initialized. Ensure a valid GTM Container ID was provided."
8924
9049
  );
@@ -8944,22 +9069,78 @@ function useGTM(gtmId) {
8944
9069
  }
8945
9070
  window.gtag(command, ...args);
8946
9071
  },
8947
- [isGA4Allowed]
9072
+ []
8948
9073
  );
8949
- return {
8950
- trackEvent,
8951
- trackPageView,
8952
- setUserProperty,
8953
- directGtag,
8954
- addNoscriptIframe
8955
- };
9074
+ return { trackEvent, trackPageView, setUserProperty, directGtag };
9075
+ }
9076
+ function useGTM(gtmId) {
9077
+ const scriptRef = (0, import_react15.useRef)(null);
9078
+ const tracker = useGTMTracker();
9079
+ (0, import_react15.useEffect)(() => {
9080
+ if (!isGA4Allowed()) {
9081
+ console.warn(
9082
+ "useGTM: GA4 tracking is disabled or Trustware config not initialized."
9083
+ );
9084
+ return;
9085
+ }
9086
+ if (!gtmId || typeof gtmId !== "string") {
9087
+ console.error(
9088
+ "useGTM: A valid GTM Container ID is required (format: GTM-XXXXX)."
9089
+ );
9090
+ return;
9091
+ }
9092
+ if (isContainerInitialized) return;
9093
+ const alreadyLoaded = document.querySelector(
9094
+ `script[src*="googletagmanager.com/gtm.js?id=${gtmId}"]`
9095
+ );
9096
+ if (alreadyLoaded) {
9097
+ window.dataLayer = window.dataLayer || [];
9098
+ isContainerInitialized = true;
9099
+ return;
9100
+ }
9101
+ window.dataLayer = window.dataLayer || [];
9102
+ window.dataLayer.push({
9103
+ "gtm.start": (/* @__PURE__ */ new Date()).getTime(),
9104
+ event: "gtm.js"
9105
+ });
9106
+ const firstScript = document.getElementsByTagName("script")[0];
9107
+ const script = document.createElement("script");
9108
+ script.async = true;
9109
+ script.src = `https://www.googletagmanager.com/gtm.js?id=${gtmId}`;
9110
+ firstScript.parentNode?.insertBefore(script, firstScript);
9111
+ scriptRef.current = script;
9112
+ isContainerInitialized = true;
9113
+ return () => {
9114
+ if (scriptRef.current) {
9115
+ scriptRef.current.parentNode?.removeChild(scriptRef.current);
9116
+ scriptRef.current = null;
9117
+ }
9118
+ isContainerInitialized = false;
9119
+ };
9120
+ }, [gtmId]);
9121
+ const addNoscriptIframe = (0, import_react15.useCallback)(() => {
9122
+ if (!isGA4Allowed()) return;
9123
+ if (document.querySelector('iframe[src*="googletagmanager.com/ns.html"]'))
9124
+ return;
9125
+ const noscript = document.createElement("noscript");
9126
+ const iframe = document.createElement("iframe");
9127
+ iframe.src = `https://www.googletagmanager.com/ns.html?id=${gtmId}`;
9128
+ iframe.height = "0";
9129
+ iframe.width = "0";
9130
+ iframe.style.display = "none";
9131
+ iframe.style.visibility = "hidden";
9132
+ noscript.appendChild(iframe);
9133
+ document.body.insertBefore(noscript, document.body.firstChild);
9134
+ }, [gtmId]);
9135
+ return { ...tracker, addNoscriptIframe };
8956
9136
  }
8957
- var import_react15;
9137
+ var import_react15, isContainerInitialized;
8958
9138
  var init_useGTM = __esm({
8959
9139
  "src/hooks/useGTM.ts"() {
8960
9140
  "use strict";
8961
9141
  import_react15 = require("react");
8962
9142
  init_core2();
9143
+ isContainerInitialized = false;
8963
9144
  }
8964
9145
  });
8965
9146
 
@@ -8985,7 +9166,7 @@ function useTransactionPolling() {
8985
9166
  return void 0;
8986
9167
  }
8987
9168
  })();
8988
- const { trackEvent } = useGTM(GTM_ID);
9169
+ const { trackEvent } = useGTMTracker();
8989
9170
  const [state, setState] = (0, import_react16.useState)({
8990
9171
  isPolling: false,
8991
9172
  apiStatus: null,
@@ -9085,8 +9266,21 @@ function useTransactionPolling() {
9085
9266
  pollCountRef.current += 1;
9086
9267
  const pollInterval = pollCountRef.current <= 10 ? FAST_POLL_INTERVAL_MS : NORMAL_POLL_INTERVAL_MS;
9087
9268
  pollingRef.current = setTimeout(poll, pollInterval);
9088
- } catch {
9269
+ } catch (err) {
9089
9270
  if (abortRef.current) return;
9271
+ if (isNotFoundError(err)) {
9272
+ clearPolling();
9273
+ const notFoundError = "Transaction session expired. Please try again.";
9274
+ setState((prev) => ({
9275
+ ...prev,
9276
+ isPolling: false,
9277
+ error: notFoundError
9278
+ }));
9279
+ setErrorMessage(notFoundError);
9280
+ setTransactionStatus("error");
9281
+ setCurrentStep("error");
9282
+ return;
9283
+ }
9090
9284
  const elapsed = Date.now() - startTimeRef.current;
9091
9285
  if (elapsed > POLL_TIMEOUT_MS) {
9092
9286
  clearPolling();
@@ -9223,10 +9417,10 @@ var init_useTransactionPolling = __esm({
9223
9417
  "use client";
9224
9418
  import_react16 = require("react");
9225
9419
  init_routes();
9420
+ init_http();
9226
9421
  init_DepositContext();
9227
9422
  init_provider();
9228
9423
  init_core2();
9229
- init_constants();
9230
9424
  init_hooks();
9231
9425
  FAST_POLL_INTERVAL_MS = 1500;
9232
9426
  NORMAL_POLL_INTERVAL_MS = 2500;
@@ -31658,7 +31852,7 @@ function normalizeTokenAddressForCompare(chain, addr) {
31658
31852
  if (chainType === "solana") return value;
31659
31853
  return value.toLowerCase();
31660
31854
  }
31661
- function sleep2(ms) {
31855
+ function sleep3(ms) {
31662
31856
  return new Promise((resolve) => setTimeout(resolve, ms));
31663
31857
  }
31664
31858
  function useTransactionActionModel({
@@ -31674,7 +31868,7 @@ function useTransactionActionModel({
31674
31868
  }) {
31675
31869
  const feeDataCacheRef = (0, import_react37.useRef)({});
31676
31870
  const { isSubmitting, submitTransaction } = useTransactionSubmit();
31677
- const { trackEvent } = useGTM(GTM_ID);
31871
+ const { trackEvent } = useGTMTracker();
31678
31872
  const destinationConfig = (() => {
31679
31873
  try {
31680
31874
  return Trustware.getConfig();
@@ -31776,7 +31970,7 @@ function useTransactionActionModel({
31776
31970
  if (status.status === "reverted") {
31777
31971
  throw new Error("Approval transaction reverted");
31778
31972
  }
31779
- await sleep2(intervalMs);
31973
+ await sleep3(intervalMs);
31780
31974
  }
31781
31975
  throw new Error("Timed out waiting for approval confirmation");
31782
31976
  },
@@ -32110,7 +32304,6 @@ var init_useTransactionActionModel = __esm({
32110
32304
  init_chainHelpers();
32111
32305
  init_utils();
32112
32306
  init_hooks();
32113
- init_constants();
32114
32307
  }
32115
32308
  });
32116
32309
 
@@ -33441,7 +33634,7 @@ function normalizeTx(raw) {
33441
33634
  toChainTxUrl: r.toChainTxUrl || r.to_chain_tx_url || void 0
33442
33635
  };
33443
33636
  }
33444
- function sleep3(ms) {
33637
+ function sleep4(ms) {
33445
33638
  return new Promise((resolve) => setTimeout(resolve, ms));
33446
33639
  }
33447
33640
  function isUserRejection(err) {
@@ -33459,10 +33652,20 @@ async function waitForApprovalConfirmation(chainId, txHash) {
33459
33652
  if (status.status === "success") return;
33460
33653
  if (status.status === "reverted")
33461
33654
  throw new Error("Approval transaction reverted");
33462
- await sleep3(2e3);
33655
+ await sleep4(2e3);
33463
33656
  }
33464
33657
  throw new Error("Timed out waiting for approval confirmation");
33465
33658
  }
33659
+ async function requireActiveAddress(wallet, plannedFor) {
33660
+ if (!wallet) throw new Error("Wallet not connected. Please reconnect.");
33661
+ const active = await wallet.getAddress();
33662
+ if (active?.toLowerCase() !== plannedFor?.toLowerCase()) {
33663
+ throw new Error(
33664
+ "Wallet account changed. Please review and confirm the swap again."
33665
+ );
33666
+ }
33667
+ return active;
33668
+ }
33466
33669
  function useSwapExecution(fromChain) {
33467
33670
  const [state, setState] = (0, import_react45.useState)({
33468
33671
  txStatus: "idle",
@@ -33519,7 +33722,15 @@ function useSwapExecution(fromChain) {
33519
33722
  if (tx.status === "bridging") {
33520
33723
  setState((p) => ({ ...p, txStatus: "bridging" }));
33521
33724
  }
33522
- } catch {
33725
+ } catch (err) {
33726
+ if (abortRef.current) return;
33727
+ if (isNotFoundError(err)) {
33728
+ clearPolling();
33729
+ const msg = "Transaction session expired. Please try again.";
33730
+ setState((p) => ({ ...p, txStatus: "error", errorMessage: msg }));
33731
+ onError(msg);
33732
+ return;
33733
+ }
33523
33734
  }
33524
33735
  if (abortRef.current) return;
33525
33736
  pollCountRef.current += 1;
@@ -33545,24 +33756,55 @@ function useSwapExecution(fromChain) {
33545
33756
  }
33546
33757
  const txReq = routeResult.txReq;
33547
33758
  const chainIdStr = String(txReq?.chainId ?? fromChain?.chainId ?? "");
33548
- const spender = txReq?.to ?? txReq?.target;
33549
33759
  const amountWei = BigInt(routeResult.route?.estimate?.fromAmount ?? "0");
33550
- if (!spender || !chainIdStr || amountWei === 0n) {
33760
+ const plannedApprovals = routeResult.route?.execution?.approvals ?? [];
33761
+ const toCheck = plannedApprovals.length ? plannedApprovals.flatMap((a) => {
33762
+ const token = a.tokenAddress ?? fromTokenAddress;
33763
+ const required = requiredApprovalAmount(
33764
+ BigInt(a.amount || "0"),
33765
+ amountWei
33766
+ );
33767
+ if (!token || !a.spender) return [];
33768
+ return [
33769
+ {
33770
+ chainId: a.chainId || chainIdStr,
33771
+ token,
33772
+ spender: a.spender,
33773
+ required
33774
+ }
33775
+ ];
33776
+ }) : (() => {
33777
+ const spender = txReq?.to ?? txReq?.target;
33778
+ if (!spender) return [];
33779
+ return [
33780
+ {
33781
+ chainId: chainIdStr,
33782
+ token: fromTokenAddress,
33783
+ spender,
33784
+ required: amountWei
33785
+ }
33786
+ ];
33787
+ })();
33788
+ if (toCheck.length === 0 || !chainIdStr || amountWei === 0n) {
33551
33789
  setState((p) => ({ ...p, allowanceStatus: "unknown" }));
33552
33790
  return;
33553
33791
  }
33554
33792
  setState((p) => ({ ...p, allowanceStatus: "checking" }));
33555
33793
  try {
33556
- const { allowance } = await getEVMAllowance({
33557
- chainId: chainIdStr,
33558
- tokenAddress: fromTokenAddress,
33559
- ownerAddress: walletAddress,
33560
- spenderAddress: spender
33561
- });
33562
- const allowanceWei = BigInt(allowance || "0");
33794
+ const results = await Promise.all(
33795
+ toCheck.map(async (c) => {
33796
+ const { allowance } = await getEVMAllowance({
33797
+ chainId: c.chainId,
33798
+ tokenAddress: c.token,
33799
+ ownerAddress: walletAddress,
33800
+ spenderAddress: c.spender
33801
+ });
33802
+ return approvalSatisfied(BigInt(allowance || "0"), c.required);
33803
+ })
33804
+ );
33563
33805
  setState((p) => ({
33564
33806
  ...p,
33565
- allowanceStatus: allowanceWei >= amountWei ? "sufficient" : "needed"
33807
+ allowanceStatus: results.every(Boolean) ? "sufficient" : "needed"
33566
33808
  }));
33567
33809
  } catch {
33568
33810
  setState((p) => ({ ...p, allowanceStatus: "needed" }));
@@ -33666,19 +33908,49 @@ function useSwapExecution(fromChain) {
33666
33908
  }
33667
33909
  }
33668
33910
  try {
33669
- if (!isNative && walletAddress && spender && chainIdStr && fromTokenAddress) {
33911
+ const plannedApprovals = routeResult.route?.execution?.approvals ?? [];
33912
+ const requiredApprovals = plannedApprovals.length ? plannedApprovals.flatMap((a, index) => {
33913
+ const token = a.tokenAddress ?? fromTokenAddress;
33914
+ const approvalSpender = a.spender;
33915
+ if (!token || !approvalSpender) return [];
33916
+ const amountWei = requiredApprovalAmount(
33917
+ BigInt(a.amount || "0"),
33918
+ fromAmountWei
33919
+ );
33920
+ return [
33921
+ {
33922
+ stepIndex: index,
33923
+ token,
33924
+ spender: approvalSpender,
33925
+ amountWei,
33926
+ chainId: a.chainId || chainIdStr
33927
+ }
33928
+ ];
33929
+ }) : spender && fromTokenAddress ? [
33930
+ {
33931
+ stepIndex: null,
33932
+ token: fromTokenAddress,
33933
+ spender,
33934
+ amountWei: fromAmountWei,
33935
+ chainId: chainIdStr
33936
+ }
33937
+ ] : [];
33938
+ const ownsApprovals = !isNative && !!walletAddress && !!chainIdStr;
33939
+ const activeAddress = ownsApprovals ? await requireActiveAddress(wallet, walletAddress) : void 0;
33940
+ for (const required of requiredApprovals) {
33941
+ if (!ownsApprovals) break;
33670
33942
  let allowanceWei = 0n;
33671
33943
  try {
33672
33944
  const { allowance } = await getEVMAllowance({
33673
- chainId: chainIdStr,
33674
- tokenAddress: fromTokenAddress,
33675
- ownerAddress: walletAddress,
33676
- spenderAddress: spender
33945
+ chainId: required.chainId,
33946
+ tokenAddress: required.token,
33947
+ ownerAddress: activeAddress,
33948
+ spenderAddress: required.spender
33677
33949
  });
33678
33950
  allowanceWei = BigInt(allowance || "0");
33679
33951
  } catch {
33680
33952
  }
33681
- if (allowanceWei < fromAmountWei) {
33953
+ if (!approvalSatisfied(allowanceWei, required.amountWei)) {
33682
33954
  setState((p) => ({ ...p, txStatus: "approving" }));
33683
33955
  if (!wallet || wallet.ecosystem !== "evm") {
33684
33956
  throw new Error("EVM wallet required for token approval");
@@ -33686,7 +33958,7 @@ function useSwapExecution(fromChain) {
33686
33958
  if (!chainIdStr || chainIdStr === "0") {
33687
33959
  throw new Error("Invalid chain ID for token approval");
33688
33960
  }
33689
- const targetChain = Number(txReq.chainId ?? fromChain?.chainId);
33961
+ const targetChain = Number(required.chainId ?? txReq.chainId);
33690
33962
  if (Number.isFinite(targetChain)) {
33691
33963
  const current = await wallet.getChainId();
33692
33964
  if (current !== targetChain) {
@@ -33698,18 +33970,18 @@ function useSwapExecution(fromChain) {
33698
33970
  }
33699
33971
  const approveAmount = maxApproval ? BigInt(
33700
33972
  "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
33701
- ) : fromAmountWei;
33973
+ ) : required.amountWei;
33702
33974
  const data = (0, import_viem43.encodeFunctionData)({
33703
33975
  abi: import_viem43.erc20Abi,
33704
33976
  functionName: "approve",
33705
- args: [spender, approveAmount]
33977
+ args: [required.spender, approveAmount]
33706
33978
  });
33707
33979
  let approvalHash;
33708
33980
  if (wallet.type === "eip1193") {
33709
33981
  const from = await wallet.getAddress();
33710
33982
  const params = {
33711
33983
  from,
33712
- to: fromTokenAddress,
33984
+ to: required.token,
33713
33985
  data,
33714
33986
  value: "0x0"
33715
33987
  };
@@ -33722,14 +33994,22 @@ function useSwapExecution(fromChain) {
33722
33994
  });
33723
33995
  } else {
33724
33996
  const response = await wallet.sendTransaction({
33725
- to: fromTokenAddress,
33997
+ to: required.token,
33726
33998
  data,
33727
33999
  value: 0n,
33728
34000
  chainId: Number.isFinite(targetChain) ? targetChain : void 0
33729
34001
  });
33730
34002
  approvalHash = response.hash;
33731
34003
  }
33732
- await waitForApprovalConfirmation(chainIdStr, approvalHash);
34004
+ if (required.stepIndex !== null) {
34005
+ void submitStepReceipt(
34006
+ routeResult.intentId,
34007
+ required.stepIndex,
34008
+ approvalHash
34009
+ ).catch(() => {
34010
+ });
34011
+ }
34012
+ await waitForApprovalConfirmation(required.chainId, approvalHash);
33733
34013
  setState((p) => ({
33734
34014
  ...p,
33735
34015
  txStatus: "confirming",
@@ -33737,9 +34017,13 @@ function useSwapExecution(fromChain) {
33737
34017
  }));
33738
34018
  }
33739
34019
  }
34020
+ if (ownsApprovals) {
34021
+ await requireActiveAddress(wallet, walletAddress);
34022
+ }
33740
34023
  const hash = await Trustware.sendRouteTransaction(
33741
34024
  routeResult,
33742
- numericChainId
34025
+ numericChainId,
34026
+ { approvalsEnsured: ownsApprovals }
33743
34027
  );
33744
34028
  try {
33745
34029
  await submitReceipt(routeResult.intentId, hash);
@@ -33809,6 +34093,8 @@ var init_useSwapExecution = __esm({
33809
34093
  import_viem43 = require("viem");
33810
34094
  init_core2();
33811
34095
  init_routes();
34096
+ init_http();
34097
+ init_tx();
33812
34098
  init_sdkRpc();
33813
34099
  init_chainHelpers();
33814
34100
  FAST_POLL_MS = 1500;
@@ -39758,6 +40044,7 @@ function WidgetInner({
39758
40044
  const { resolvedTheme } = useDepositUi();
39759
40045
  const { status, revalidate, errors } = useTrustware();
39760
40046
  const [showConfirmDialog, setShowConfirmDialog] = (0, import_react51.useState)(false);
40047
+ useGTM(GTM_ID);
39761
40048
  const handleCloseRequest = (0, import_react51.useCallback)(() => {
39762
40049
  if (ACTIVE_TRANSACTION_STATUSES.includes(transactionStatus)) {
39763
40050
  setShowConfirmDialog(true);
@@ -39832,6 +40119,8 @@ var init_TrustwareWidgetV2 = __esm({
39832
40119
  init_provider();
39833
40120
  init_manager();
39834
40121
  init_useTrustwareConfig();
40122
+ init_hooks();
40123
+ init_constants();
39835
40124
  init_swap();
39836
40125
  import_jsx_runtime60 = require("react/jsx-runtime");
39837
40126
  widgetContentContainerStyle = {
@@ -39978,6 +40267,7 @@ __export(index_exports, {
39978
40267
  DEFAULT_SLIPPAGE: () => DEFAULT_SLIPPAGE2,
39979
40268
  GTM_ID: () => GTM_ID,
39980
40269
  IdentityStore: () => IdentityStore,
40270
+ POPULAR_ORDER: () => POPULAR_ORDER,
39981
40271
  RateLimitError: () => RateLimitError,
39982
40272
  SDK_NAME: () => SDK_NAME,
39983
40273
  SDK_VERSION: () => SDK_VERSION,
@@ -39986,6 +40276,7 @@ __export(index_exports, {
39986
40276
  TrustwareProvider: () => TrustwareProvider,
39987
40277
  TrustwareWidget: () => TrustwareWidgetV2,
39988
40278
  WALLETCONNECT_PROJECT_ID: () => WALLETCONNECT_PROJECT_ID,
40279
+ WALLETS: () => WALLETS,
39989
40280
  buildWalletIdentityAddress: () => buildWalletIdentityAddress,
39990
40281
  connectDetectedWallet: () => connectDetectedWallet,
39991
40282
  createWalletIdentity: () => createWalletIdentity,
@@ -40038,6 +40329,7 @@ init_index();
40038
40329
  DEFAULT_SLIPPAGE,
40039
40330
  GTM_ID,
40040
40331
  IdentityStore,
40332
+ POPULAR_ORDER,
40041
40333
  RateLimitError,
40042
40334
  SDK_NAME,
40043
40335
  SDK_VERSION,
@@ -40046,6 +40338,7 @@ init_index();
40046
40338
  TrustwareProvider,
40047
40339
  TrustwareWidget,
40048
40340
  WALLETCONNECT_PROJECT_ID,
40341
+ WALLETS,
40049
40342
  buildWalletIdentityAddress,
40050
40343
  connectDetectedWallet,
40051
40344
  createWalletIdentity,