@rhea-finance/cross-chain-aggregation-dex 2.0.5 → 2.0.7

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 (42) hide show
  1. package/README.md +148 -71
  2. package/dist/executors/aptos.d.mts +1 -1
  3. package/dist/executors/aptos.d.ts +1 -1
  4. package/dist/executors/aptos.js.map +1 -1
  5. package/dist/executors/aptos.mjs.map +1 -1
  6. package/dist/executors/bitcoin.d.mts +1 -1
  7. package/dist/executors/bitcoin.d.ts +1 -1
  8. package/dist/executors/bitcoin.js.map +1 -1
  9. package/dist/executors/bitcoin.mjs.map +1 -1
  10. package/dist/executors/evm.d.mts +1 -1
  11. package/dist/executors/evm.d.ts +1 -1
  12. package/dist/executors/evm.js.map +1 -1
  13. package/dist/executors/evm.mjs.map +1 -1
  14. package/dist/executors/near.d.mts +1 -1
  15. package/dist/executors/near.d.ts +1 -1
  16. package/dist/executors/near.js.map +1 -1
  17. package/dist/executors/near.mjs.map +1 -1
  18. package/dist/executors/solana.d.mts +1 -1
  19. package/dist/executors/solana.d.ts +1 -1
  20. package/dist/executors/solana.js.map +1 -1
  21. package/dist/executors/solana.mjs.map +1 -1
  22. package/dist/executors/sui.d.mts +1 -1
  23. package/dist/executors/sui.d.ts +1 -1
  24. package/dist/executors/sui.js.map +1 -1
  25. package/dist/executors/sui.mjs.map +1 -1
  26. package/dist/executors/tron.d.mts +1 -1
  27. package/dist/executors/tron.d.ts +1 -1
  28. package/dist/executors/tron.js.map +1 -1
  29. package/dist/executors/tron.mjs.map +1 -1
  30. package/dist/executors/zcash.d.mts +1 -1
  31. package/dist/executors/zcash.d.ts +1 -1
  32. package/dist/executors/zcash.js.map +1 -1
  33. package/dist/executors/zcash.mjs.map +1 -1
  34. package/dist/index.d.mts +40 -6
  35. package/dist/index.d.ts +40 -6
  36. package/dist/index.js +266 -47
  37. package/dist/index.js.map +1 -1
  38. package/dist/index.mjs +265 -48
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/{shared-DmMJXknE.d.mts → shared-COkGiqaz.d.mts} +30 -2
  41. package/dist/{shared-DmMJXknE.d.ts → shared-COkGiqaz.d.ts} +30 -2
  42. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ var ApiClient = class {
50
50
  this.fetchImpl = fetchImpl.bind(globalThis);
51
51
  }
52
52
  quote(body, options = {}) {
53
- return this.request("/api/swap/quote", "quote", {
53
+ return this.request("/api/v2/swap/quote", "quote", {
54
54
  ...options,
55
55
  method: "POST",
56
56
  body,
@@ -58,7 +58,7 @@ var ApiClient = class {
58
58
  });
59
59
  }
60
60
  build(body, options = {}) {
61
- return this.request("/api/swap/swap", "build", {
61
+ return this.request("/api/v2/swap/swap", "build", {
62
62
  ...options,
63
63
  method: "POST",
64
64
  body
@@ -118,6 +118,22 @@ var ApiClient = class {
118
118
  body
119
119
  });
120
120
  }
121
+ getFromTokenRows(chainId, options = {}) {
122
+ return this.request("/get_chain_prices", "tokens", {
123
+ ...options,
124
+ method: "GET",
125
+ retryableOperation: true,
126
+ query: { chain: chainId }
127
+ });
128
+ }
129
+ getCrossChainToTokenRows(chainId, options = {}) {
130
+ return this.request("/api/swap/supported_to_tokens", "tokens", {
131
+ ...options,
132
+ method: "GET",
133
+ retryableOperation: true,
134
+ query: { chain: chainId }
135
+ });
136
+ }
121
137
  async request(path, stage, options) {
122
138
  const url = this.buildUrl(path, options.query);
123
139
  const headers = await this.buildHeaders(
@@ -657,7 +673,7 @@ function normalizeBuild(raw, executionId = createExecutionId(), request) {
657
673
  const lane = laneFromChainType(common.chainType, fromChain);
658
674
  assertLaneMatchesChain(lane, fromChain);
659
675
  const execution = normalizeExecution(raw, lane, fromChain);
660
- const order = normalizeOrder(raw, execution);
676
+ const order = normalizeOrder(raw, execution, request);
661
677
  const deposit = normalizeDeposit(raw.deposit);
662
678
  return {
663
679
  executionId,
@@ -888,11 +904,12 @@ function assertKind(actual, allowed) {
888
904
  );
889
905
  }
890
906
  }
891
- function normalizeOrder(raw, execution) {
907
+ function normalizeOrder(raw, execution, request) {
892
908
  const depositOrderId = readRecordString(raw.deposit, "orderId");
893
- const orderId = raw.orderId ?? depositOrderId;
894
- if (!orderId) return void 0;
895
909
  const router = raw.statusRouter ?? (execution.kind === "evm-signature" ? execution.request.router : void 0) ?? raw.router;
910
+ const confidentialNearIntentsStatusKey = request?.confidentiality === "basic" && router.toLowerCase().includes("nearintents") ? readRecordString(raw.deposit, "depositAddress") : void 0;
911
+ const orderId = raw.orderId ?? depositOrderId ?? confidentialNearIntentsStatusKey;
912
+ if (!orderId) return void 0;
896
913
  const chainId = execution.kind === "evm-signature" ? String(execution.request.chainId) : execution.kind === "evm-transaction" ? String(execution.tx.chainId) : void 0;
897
914
  return { orderId, router, ...chainId ? { chainId } : {} };
898
915
  }
@@ -952,6 +969,8 @@ function assertBaseUnitAmount(value) {
952
969
 
953
970
  // src/normalizers/quote.ts
954
971
  var DEFAULT_QUOTE_WAITING_TIME_MS = 3e3;
972
+ var DEFAULT_SAME_CHAIN_TIMEOUT_MS = 500;
973
+ var DEFAULT_CROSS_CHAIN_TIMEOUT_MS = 3e3;
955
974
  function serializeQuoteRequest(request) {
956
975
  validateQuoteRequest(request);
957
976
  return {
@@ -963,6 +982,8 @@ function serializeQuoteRequest(request) {
963
982
  amountIn: request.amountIn,
964
983
  slippage: request.slippageBps,
965
984
  quoteWaitingTimeMs: request.quoteWaitingTimeMs ?? DEFAULT_QUOTE_WAITING_TIME_MS,
985
+ sameChainTimeoutMs: request.sameChainTimeoutMs ?? DEFAULT_SAME_CHAIN_TIMEOUT_MS,
986
+ crossChainTimeoutMs: request.crossChainTimeoutMs ?? DEFAULT_CROSS_CHAIN_TIMEOUT_MS,
966
987
  ...request.confidentiality ? { confidentiality: request.confidentiality } : {},
967
988
  sender: request.sender.trim(),
968
989
  ...request.recipient?.trim() ? { recipient: request.recipient.trim() } : {}
@@ -974,7 +995,11 @@ function normalizeQuote(request, raw, receivedAt = Date.now()) {
974
995
  throw invalidQuote("Quote response does not contain a valid best route");
975
996
  }
976
997
  const quoteId = readOptionalString(raw.bestQuote.quoteId);
977
- const apiRequest = Object.freeze({ ...serializeQuoteRequest(request) });
998
+ const apiRequest = { ...serializeQuoteRequest(request) };
999
+ delete apiRequest.quoteWaitingTimeMs;
1000
+ delete apiRequest.sameChainTimeoutMs;
1001
+ delete apiRequest.crossChainTimeoutMs;
1002
+ Object.freeze(apiRequest);
978
1003
  const buildContext = Object.freeze({
979
1004
  request: apiRequest,
980
1005
  router: bestRoute.router,
@@ -1042,6 +1067,14 @@ function validateQuoteRequest(request) {
1042
1067
  "quoteWaitingTimeMs must be a non-negative integer"
1043
1068
  );
1044
1069
  }
1070
+ validateQuoteTimingParameter(
1071
+ request.sameChainTimeoutMs,
1072
+ "sameChainTimeoutMs"
1073
+ );
1074
+ validateQuoteTimingParameter(
1075
+ request.crossChainTimeoutMs,
1076
+ "crossChainTimeoutMs"
1077
+ );
1045
1078
  if (!request.sender.trim()) {
1046
1079
  throw new SwapSdkError(
1047
1080
  "INVALID_REQUEST",
@@ -1064,6 +1097,15 @@ function validateQuoteRequest(request) {
1064
1097
  );
1065
1098
  }
1066
1099
  }
1100
+ function validateQuoteTimingParameter(value, field) {
1101
+ if (value !== void 0 && (!Number.isFinite(value) || !Number.isInteger(value) || value < 0)) {
1102
+ throw new SwapSdkError(
1103
+ "INVALID_REQUEST",
1104
+ "quote",
1105
+ `${field} must be a non-negative integer`
1106
+ );
1107
+ }
1108
+ }
1067
1109
  function normalizeRoute(raw, required) {
1068
1110
  const router = readOptionalString(raw.router);
1069
1111
  const amountOutValue = raw.amountOut ?? raw.estimatedOut;
@@ -1236,6 +1278,137 @@ function normalizeTimestamp(value) {
1236
1278
  return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
1237
1279
  }
1238
1280
 
1281
+ // src/normalizers/tokens.ts
1282
+ var TOKEN_LIST_CHAIN_METADATA = /* @__PURE__ */ new Map([
1283
+ [1, { chain: "1", blockchain: "eth" }],
1284
+ [10, { chain: "10", blockchain: "op" }],
1285
+ [43114, { chain: "43114", blockchain: "avax" }],
1286
+ [4663, { chain: "4663", blockchain: "robinhood" }],
1287
+ [747474, { chain: "747474", blockchain: "katana" }],
1288
+ [146, { chain: "146", blockchain: "sonic" }],
1289
+ [130, { chain: "130", blockchain: "unichain" }],
1290
+ [1672, { chain: "1672", blockchain: "pharos" }],
1291
+ [4217, { chain: "4217", blockchain: "tempo" }],
1292
+ [56, { chain: "56", blockchain: "bsc" }],
1293
+ [100, { chain: "100", blockchain: "gnosis" }],
1294
+ [137, { chain: "137", blockchain: "pol" }],
1295
+ [143, { chain: "143", blockchain: "monad" }],
1296
+ [196, { chain: "196", blockchain: "xlayer" }],
1297
+ [8453, { chain: "8453", blockchain: "base" }],
1298
+ [9745, { chain: "9745", blockchain: "plasma" }],
1299
+ [42161, { chain: "42161", blockchain: "arb" }],
1300
+ [80094, { chain: "80094", blockchain: "bera" }],
1301
+ [195, { chain: "tron", blockchain: "tron" }],
1302
+ [501, { chain: "solana", blockchain: "sol" }],
1303
+ [784, { chain: "sui", blockchain: "sui" }],
1304
+ [900001, { chain: "near", blockchain: "near" }],
1305
+ [900002, { chain: "btc", blockchain: "btc" }],
1306
+ [900010, { chain: "zcash", blockchain: "zec" }],
1307
+ [900012, { chain: "aptos", blockchain: "aptos" }]
1308
+ ]);
1309
+ function normalizeFromTokenList(raw, chainId) {
1310
+ if (!isRecord2(raw) || Array.isArray(raw)) {
1311
+ throw invalidTokenList("From-token response data must be an object");
1312
+ }
1313
+ return normalizeRows(Object.values(raw), chainId);
1314
+ }
1315
+ function normalizeCrossChainToTokenList(raw, chainId) {
1316
+ if (!isRecord2(raw) || !Array.isArray(raw.tokens)) {
1317
+ throw invalidTokenList(
1318
+ "Cross-chain to-token response data must contain a tokens array"
1319
+ );
1320
+ }
1321
+ return normalizeRows(raw.tokens, chainId);
1322
+ }
1323
+ function normalizeRows(rows, chainId) {
1324
+ const tokens = rows.map((row) => normalizeRow(row, chainId)).filter((token) => token !== void 0);
1325
+ if (rows.length > 0 && tokens.length === 0) {
1326
+ throw invalidTokenList("Token-list response contains no valid token rows");
1327
+ }
1328
+ return tokens;
1329
+ }
1330
+ function normalizeRow(value, chainId) {
1331
+ if (!isRecord2(value)) return void 0;
1332
+ const row = value;
1333
+ const assetId = readString(row.assetId) ?? readString(row.address) ?? readString(row.contractAddress) ?? readString(row.coinType);
1334
+ const symbol = readString(row.symbol);
1335
+ const decimals = readDecimals(row.decimals);
1336
+ if (!assetId || !symbol || decimals === void 0) return void 0;
1337
+ const metadata = TOKEN_LIST_CHAIN_METADATA.get(chainId);
1338
+ const chain = metadata?.chain ?? String(chainId);
1339
+ const blockchain = readString(row.blockchain)?.toLowerCase() ?? metadata?.blockchain ?? String(chainId);
1340
+ const rawAddress = readString(row.contractAddress) ?? readString(row.address) ?? readString(row.coinType);
1341
+ const isNative = isNativeToken({
1342
+ row,
1343
+ chain,
1344
+ blockchain,
1345
+ assetId,
1346
+ rawAddress,
1347
+ symbol
1348
+ });
1349
+ const sources = Array.isArray(row.sources) ? row.sources.filter(
1350
+ (source) => typeof source === "string" && source.trim().length > 0
1351
+ ) : readString(row.platform) ? [readString(row.platform)] : [];
1352
+ const price = typeof row.price === "string" || typeof row.price === "number" ? row.price : null;
1353
+ const priceUpdatedAt = typeof row.updated_at === "number" && Number.isFinite(row.updated_at) ? row.updated_at : null;
1354
+ return {
1355
+ chain,
1356
+ address: assetId,
1357
+ symbol,
1358
+ decimals,
1359
+ isNative,
1360
+ tokenListChainId: chainId,
1361
+ blockchain,
1362
+ assetId,
1363
+ contractAddress: isNative ? null : rawAddress ?? null,
1364
+ coinType: readString(row.coinType) ?? null,
1365
+ name: readString(row.name) ?? null,
1366
+ logoURI: readString(row.logoURI) ?? null,
1367
+ price,
1368
+ priceUpdatedAt,
1369
+ sources,
1370
+ raw: { ...row }
1371
+ };
1372
+ }
1373
+ function isNativeToken(input) {
1374
+ if (input.row.isNative === true) return true;
1375
+ const address = (input.rawAddress ?? "").toLowerCase();
1376
+ const assetId = input.assetId.toLowerCase();
1377
+ const coinType = readString(input.row.coinType)?.toLowerCase() ?? "";
1378
+ if (/^[1-9]\d*$/.test(input.chain)) {
1379
+ return address === "0x0000000000000000000000000000000000000000" || address === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" || (input.blockchain === "pol" || input.blockchain === "polygon") && address === "0x0000000000000000000000000000000000001010";
1380
+ }
1381
+ if (input.chain === "solana") {
1382
+ return address === "11111111111111111111111111111111" || assetId === "nep141:sol.omft.near";
1383
+ }
1384
+ if (input.chain === "tron") {
1385
+ return input.symbol.toUpperCase() === "TRX";
1386
+ }
1387
+ if (input.chain === "sui") {
1388
+ return address.endsWith("::sui::sui");
1389
+ }
1390
+ if (input.chain === "aptos") {
1391
+ if (coinType === "0x1::aptos_coin::aptoscoin") return true;
1392
+ if (!/^0x[0-9a-f]+$/.test(address)) return false;
1393
+ const compactAddress = address.slice(2).replace(/^0+/, "") || "0";
1394
+ return compactAddress === "a";
1395
+ }
1396
+ return false;
1397
+ }
1398
+ function readString(value) {
1399
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
1400
+ }
1401
+ function readDecimals(value) {
1402
+ const decimals = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
1403
+ return Number.isInteger(decimals) && decimals >= 0 ? decimals : void 0;
1404
+ }
1405
+ function isRecord2(value) {
1406
+ return typeof value === "object" && value !== null;
1407
+ }
1408
+ function invalidTokenList(message) {
1409
+ return new SwapSdkError("INVALID_API_RESPONSE", "tokens", message);
1410
+ }
1411
+
1239
1412
  // src/mca/collateral.ts
1240
1413
  function resolveMcaWithdrawPolicy(input) {
1241
1414
  const decreaseCollateral = resolveMcaRequiredCollateralDecrease({
@@ -1399,7 +1572,7 @@ function normalizeMcaQuote(request, signer, quote) {
1399
1572
  mca: Object.freeze({ ...mca })
1400
1573
  };
1401
1574
  if (request.flow === "deposit") {
1402
- if (readString(quote.raw.nearDepositTxError)) {
1575
+ if (readString2(quote.raw.nearDepositTxError)) {
1403
1576
  throw invalidResponse(String(quote.raw.nearDepositTxError));
1404
1577
  }
1405
1578
  return {
@@ -1410,7 +1583,7 @@ function normalizeMcaQuote(request, signer, quote) {
1410
1583
  ...quote.raw.nearDepositTx === void 0 ? {} : { nearDepositTx: quote.raw.nearDepositTx }
1411
1584
  };
1412
1585
  }
1413
- if (readString(quote.raw.nearMcaWithdrawTxError)) {
1586
+ if (readString2(quote.raw.nearMcaWithdrawTxError)) {
1414
1587
  throw invalidResponse(String(quote.raw.nearMcaWithdrawTxError));
1415
1588
  }
1416
1589
  const mode = resolveWithdrawMode(request);
@@ -1438,7 +1611,7 @@ function resolveWithdrawMode(request) {
1438
1611
  function isPlainObject(value) {
1439
1612
  return typeof value === "object" && value !== null && !Array.isArray(value);
1440
1613
  }
1441
- function readString(value) {
1614
+ function readString2(value) {
1442
1615
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
1443
1616
  }
1444
1617
  function invalidRequest(message) {
@@ -1740,7 +1913,6 @@ var McaSwapService = class {
1740
1913
  this.client = client;
1741
1914
  this.relayerReports = /* @__PURE__ */ new Map();
1742
1915
  this.now = config.now ?? Date.now;
1743
- this.maxQuoteAgeMs = config.maxQuoteAgeMs === void 0 ? 3e4 : config.maxQuoteAgeMs;
1744
1916
  this.reportMode = config.reportMode ?? "auto";
1745
1917
  this.onEvent = config.onEvent;
1746
1918
  this.resolveSignerIdentity = config.resolveSignerIdentity;
@@ -1762,7 +1934,6 @@ var McaSwapService = class {
1762
1934
  return normalizeMcaQuote(request, signer, quote);
1763
1935
  }
1764
1936
  async build(input) {
1765
- this.assertQuoteFresh(input.quote);
1766
1937
  if (input.quote.executionMode === "withdraw-relayer") {
1767
1938
  throw new SwapSdkError(
1768
1939
  "INVALID_REQUEST",
@@ -1783,7 +1954,6 @@ var McaSwapService = class {
1783
1954
  };
1784
1955
  }
1785
1956
  async swap(input) {
1786
- this.assertQuoteFresh(input.quote);
1787
1957
  if (input.quote.executionMode === "withdraw-relayer") {
1788
1958
  return this.executeRelayerWithdraw({ ...input, quote: input.quote });
1789
1959
  }
@@ -1926,7 +2096,7 @@ var McaSwapService = class {
1926
2096
  "MCA relayer preview is missing business"
1927
2097
  );
1928
2098
  }
1929
- const message = readString2(input.quote.preview.messageToSign);
2099
+ const message = readString3(input.quote.preview.messageToSign);
1930
2100
  if (!message) {
1931
2101
  throw new SwapSdkError(
1932
2102
  "INVALID_API_RESPONSE",
@@ -1935,7 +2105,7 @@ var McaSwapService = class {
1935
2105
  );
1936
2106
  }
1937
2107
  const depositAddress = extractMcaWithdrawDepositAddress({
1938
- snapshotDepositAddress: readString2(input.quote.raw.depositAddress),
2108
+ snapshotDepositAddress: readString3(input.quote.raw.depositAddress),
1939
2109
  preview: input.quote.preview,
1940
2110
  bestQuote: input.quote.raw.bestQuote
1941
2111
  });
@@ -2000,8 +2170,8 @@ var McaSwapService = class {
2000
2170
  signal: input.signal,
2001
2171
  idempotencyKey: input.idempotencyKey
2002
2172
  });
2003
- const orderId = readString2(raw.orderId) ?? readRecordString2(raw.deposit, "orderId");
2004
- const router = readString2(raw.router) ?? input.quote.route.router;
2173
+ const orderId = readString3(raw.orderId) ?? readRecordString2(raw.deposit, "orderId");
2174
+ const router = readString3(raw.router) ?? input.quote.route.router;
2005
2175
  const submittedDepositAddress = readRecordString2(raw.deposit, "depositAddress") ?? depositAddress;
2006
2176
  if (!orderId || !router) {
2007
2177
  throw new SwapSdkError(
@@ -2099,18 +2269,6 @@ var McaSwapService = class {
2099
2269
  this.onEvent?.(event);
2100
2270
  if (local !== this.onEvent) local?.(event);
2101
2271
  }
2102
- assertQuoteFresh(quote) {
2103
- const now = this.now();
2104
- const expiredByApi = quote.expiresAt !== void 0 && now > quote.expiresAt;
2105
- const expiredByAge = this.maxQuoteAgeMs !== null && now - quote.receivedAt > Math.max(0, this.maxQuoteAgeMs);
2106
- if (expiredByApi || expiredByAge) {
2107
- throw new SwapSdkError(
2108
- "QUOTE_EXPIRED",
2109
- "build",
2110
- "Quote has expired; request a fresh quote"
2111
- );
2112
- }
2113
- }
2114
2272
  };
2115
2273
  function buildRequestFromQuote(quote) {
2116
2274
  const context = quote.buildContext;
@@ -2134,14 +2292,14 @@ function assertSignerMatchesQuote(signer, expected) {
2134
2292
  );
2135
2293
  }
2136
2294
  }
2137
- function readString2(value) {
2295
+ function readString3(value) {
2138
2296
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
2139
2297
  }
2140
2298
  function readRecordString2(value, field) {
2141
2299
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2142
2300
  return void 0;
2143
2301
  }
2144
- return readString2(value[field]);
2302
+ return readString3(value[field]);
2145
2303
  }
2146
2304
 
2147
2305
  // src/client/SwapClient.ts
@@ -2149,6 +2307,8 @@ var SwapClient = class {
2149
2307
  constructor(config) {
2150
2308
  this.inFlight = /* @__PURE__ */ new Set();
2151
2309
  this.reportRequests = /* @__PURE__ */ new Map();
2310
+ this.tokenListCache = /* @__PURE__ */ new Map();
2311
+ this.tokenListInflight = /* @__PURE__ */ new Map();
2152
2312
  this.config = config;
2153
2313
  this.api = new ApiClient(config);
2154
2314
  this.registry = new ExecutorRegistry(config.executors ?? []);
@@ -2156,7 +2316,6 @@ var SwapClient = class {
2156
2316
  this.now = config.now ?? Date.now;
2157
2317
  this.managedSwapFlow = new McaSwapService(this, {
2158
2318
  now: this.now,
2159
- maxQuoteAgeMs: config.maxQuoteAgeMs,
2160
2319
  reportMode: this.reportMode,
2161
2320
  resolveSignerIdentity: (chain) => this.registry.getSigner(chain),
2162
2321
  resolveMessageSigner: (chain) => this.registry.getSigner(chain, true),
@@ -2181,6 +2340,55 @@ var SwapClient = class {
2181
2340
  buildRaw(request, options = {}) {
2182
2341
  return this.api.build(request, options);
2183
2342
  }
2343
+ async getFromTokens(request, options = {}) {
2344
+ return this.loadTokenList("from", request, options, async () => {
2345
+ const raw = await this.api.getFromTokenRows(request.chainId, options);
2346
+ return normalizeFromTokenList(raw, request.chainId);
2347
+ });
2348
+ }
2349
+ async getCrossChainToTokens(request, options = {}) {
2350
+ return this.loadTokenList("cross-chain-to", request, options, async () => {
2351
+ const raw = await this.api.getCrossChainToTokenRows(
2352
+ request.chainId,
2353
+ options
2354
+ );
2355
+ return normalizeCrossChainToTokenList(raw, request.chainId);
2356
+ });
2357
+ }
2358
+ async loadTokenList(direction, request, options, load) {
2359
+ if (!Number.isSafeInteger(request.chainId) || request.chainId <= 0) {
2360
+ throw new SwapSdkError(
2361
+ "INVALID_REQUEST",
2362
+ "tokens",
2363
+ `Token-list chainId must be a positive safe integer: ${String(
2364
+ request.chainId
2365
+ )}`
2366
+ );
2367
+ }
2368
+ const ttlMs = Math.max(0, this.config.tokenListCacheTtlMs ?? 6e5);
2369
+ const key = `${direction}:${request.chainId}`;
2370
+ const cached = this.tokenListCache.get(key);
2371
+ if (ttlMs > 0 && cached && cached.expiresAt > this.now()) {
2372
+ return cloneTokenList(cached.tokens);
2373
+ }
2374
+ const existing = options.signal ? void 0 : this.tokenListInflight.get(key);
2375
+ if (existing) return cloneTokenList(await existing);
2376
+ const pending = load().then((tokens) => {
2377
+ if (ttlMs > 0) {
2378
+ this.tokenListCache.set(key, {
2379
+ expiresAt: this.now() + ttlMs,
2380
+ tokens: cloneTokenList(tokens)
2381
+ });
2382
+ }
2383
+ return tokens;
2384
+ }).finally(() => {
2385
+ if (this.tokenListInflight.get(key) === pending) {
2386
+ this.tokenListInflight.delete(key);
2387
+ }
2388
+ });
2389
+ if (!options.signal) this.tokenListInflight.set(key, pending);
2390
+ return cloneTokenList(await pending);
2391
+ }
2184
2392
  async buildSwap(input) {
2185
2393
  if (isMcaQuote(input.quote)) {
2186
2394
  return this.managedSwapFlow.build({
@@ -2192,7 +2400,6 @@ var SwapClient = class {
2192
2400
  return this.buildStandardSwap(input);
2193
2401
  }
2194
2402
  async buildStandardSwap(input) {
2195
- this.assertQuoteFresh(input.quote);
2196
2403
  const executionId = createExecutionId();
2197
2404
  this.emit({ type: "build-started", executionId });
2198
2405
  const context = input.quote.buildContext;
@@ -2311,7 +2518,23 @@ var SwapClient = class {
2311
2518
  emit({ type: "warning", executionId: build.executionId, warning });
2312
2519
  }
2313
2520
  }
2314
- if ((input.waitFor ?? "submitted") === "completed" && orderId) {
2521
+ const waitsForCompletion = (input.waitFor ?? "submitted") === "completed";
2522
+ const requiresOrderStatus = build.isCrossChain || build.request?.confidentiality === "basic";
2523
+ if (waitsForCompletion && requiresOrderStatus && !orderId) {
2524
+ throw new SwapSdkError(
2525
+ "INVALID_API_RESPONSE",
2526
+ "status",
2527
+ "Swap submitted, but the API did not provide a status key",
2528
+ {
2529
+ details: {
2530
+ executionId: build.executionId,
2531
+ ...result.txHash ? { txHash: result.txHash } : {},
2532
+ router: orderRouter ?? build.router
2533
+ }
2534
+ }
2535
+ );
2536
+ }
2537
+ if (waitsForCompletion && orderId) {
2315
2538
  const status = await this.waitForOrder({
2316
2539
  orderId,
2317
2540
  router: orderRouter ?? build.router,
@@ -2464,19 +2687,6 @@ var SwapClient = class {
2464
2687
  emit(event) {
2465
2688
  this.config.onEvent?.(event);
2466
2689
  }
2467
- assertQuoteFresh(quote) {
2468
- const now = this.now();
2469
- const maxAge = this.config.maxQuoteAgeMs === void 0 ? 3e4 : this.config.maxQuoteAgeMs;
2470
- const expiredByApi = quote.expiresAt !== void 0 && now > quote.expiresAt;
2471
- const expiredByAge = maxAge !== null && now - quote.receivedAt > Math.max(0, maxAge);
2472
- if (expiredByApi || expiredByAge) {
2473
- throw new SwapSdkError(
2474
- "QUOTE_EXPIRED",
2475
- "build",
2476
- "Quote has expired; request a fresh quote"
2477
- );
2478
- }
2479
- }
2480
2690
  createReportRequest(build, result) {
2481
2691
  const request = build.request;
2482
2692
  const reportContext = build.reportContext;
@@ -2546,6 +2756,13 @@ function isMcaQuote(quote) {
2546
2756
  function readNonEmptyString(value) {
2547
2757
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
2548
2758
  }
2759
+ function cloneTokenList(tokens) {
2760
+ return tokens.map((token) => ({
2761
+ ...token,
2762
+ sources: [...token.sources],
2763
+ raw: { ...token.raw }
2764
+ }));
2765
+ }
2549
2766
  function delay(ms, signal) {
2550
2767
  return new Promise((resolve, reject) => {
2551
2768
  if (signal?.aborted) {
@@ -2594,6 +2811,8 @@ exports.formatUnits = formatUnits;
2594
2811
  exports.fromApiChain = fromApiChain;
2595
2812
  exports.isSameMcaSignerIdentity = isSameMcaSignerIdentity;
2596
2813
  exports.normalizeBuild = normalizeBuild;
2814
+ exports.normalizeCrossChainToTokenList = normalizeCrossChainToTokenList;
2815
+ exports.normalizeFromTokenList = normalizeFromTokenList;
2597
2816
  exports.normalizeHistory = normalizeHistory;
2598
2817
  exports.normalizeHistoryStatus = normalizeHistoryStatus;
2599
2818
  exports.normalizeMcaQuote = normalizeMcaQuote;