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

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 +147 -69
  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 -4
  35. package/dist/index.d.ts +40 -4
  36. package/dist/index.js +264 -15
  37. package/dist/index.js.map +1 -1
  38. package/dist/index.mjs +263 -16
  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
@@ -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) {
@@ -1926,7 +2099,7 @@ var McaSwapService = class {
1926
2099
  "MCA relayer preview is missing business"
1927
2100
  );
1928
2101
  }
1929
- const message = readString2(input.quote.preview.messageToSign);
2102
+ const message = readString3(input.quote.preview.messageToSign);
1930
2103
  if (!message) {
1931
2104
  throw new SwapSdkError(
1932
2105
  "INVALID_API_RESPONSE",
@@ -1935,7 +2108,7 @@ var McaSwapService = class {
1935
2108
  );
1936
2109
  }
1937
2110
  const depositAddress = extractMcaWithdrawDepositAddress({
1938
- snapshotDepositAddress: readString2(input.quote.raw.depositAddress),
2111
+ snapshotDepositAddress: readString3(input.quote.raw.depositAddress),
1939
2112
  preview: input.quote.preview,
1940
2113
  bestQuote: input.quote.raw.bestQuote
1941
2114
  });
@@ -2000,8 +2173,8 @@ var McaSwapService = class {
2000
2173
  signal: input.signal,
2001
2174
  idempotencyKey: input.idempotencyKey
2002
2175
  });
2003
- const orderId = readString2(raw.orderId) ?? readRecordString2(raw.deposit, "orderId");
2004
- const router = readString2(raw.router) ?? input.quote.route.router;
2176
+ const orderId = readString3(raw.orderId) ?? readRecordString2(raw.deposit, "orderId");
2177
+ const router = readString3(raw.router) ?? input.quote.route.router;
2005
2178
  const submittedDepositAddress = readRecordString2(raw.deposit, "depositAddress") ?? depositAddress;
2006
2179
  if (!orderId || !router) {
2007
2180
  throw new SwapSdkError(
@@ -2134,14 +2307,14 @@ function assertSignerMatchesQuote(signer, expected) {
2134
2307
  );
2135
2308
  }
2136
2309
  }
2137
- function readString2(value) {
2310
+ function readString3(value) {
2138
2311
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
2139
2312
  }
2140
2313
  function readRecordString2(value, field) {
2141
2314
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
2142
2315
  return void 0;
2143
2316
  }
2144
- return readString2(value[field]);
2317
+ return readString3(value[field]);
2145
2318
  }
2146
2319
 
2147
2320
  // src/client/SwapClient.ts
@@ -2149,6 +2322,8 @@ var SwapClient = class {
2149
2322
  constructor(config) {
2150
2323
  this.inFlight = /* @__PURE__ */ new Set();
2151
2324
  this.reportRequests = /* @__PURE__ */ new Map();
2325
+ this.tokenListCache = /* @__PURE__ */ new Map();
2326
+ this.tokenListInflight = /* @__PURE__ */ new Map();
2152
2327
  this.config = config;
2153
2328
  this.api = new ApiClient(config);
2154
2329
  this.registry = new ExecutorRegistry(config.executors ?? []);
@@ -2181,6 +2356,55 @@ var SwapClient = class {
2181
2356
  buildRaw(request, options = {}) {
2182
2357
  return this.api.build(request, options);
2183
2358
  }
2359
+ async getFromTokens(request, options = {}) {
2360
+ return this.loadTokenList("from", request, options, async () => {
2361
+ const raw = await this.api.getFromTokenRows(request.chainId, options);
2362
+ return normalizeFromTokenList(raw, request.chainId);
2363
+ });
2364
+ }
2365
+ async getCrossChainToTokens(request, options = {}) {
2366
+ return this.loadTokenList("cross-chain-to", request, options, async () => {
2367
+ const raw = await this.api.getCrossChainToTokenRows(
2368
+ request.chainId,
2369
+ options
2370
+ );
2371
+ return normalizeCrossChainToTokenList(raw, request.chainId);
2372
+ });
2373
+ }
2374
+ async loadTokenList(direction, request, options, load) {
2375
+ if (!Number.isSafeInteger(request.chainId) || request.chainId <= 0) {
2376
+ throw new SwapSdkError(
2377
+ "INVALID_REQUEST",
2378
+ "tokens",
2379
+ `Token-list chainId must be a positive safe integer: ${String(
2380
+ request.chainId
2381
+ )}`
2382
+ );
2383
+ }
2384
+ const ttlMs = Math.max(0, this.config.tokenListCacheTtlMs ?? 6e5);
2385
+ const key = `${direction}:${request.chainId}`;
2386
+ const cached = this.tokenListCache.get(key);
2387
+ if (ttlMs > 0 && cached && cached.expiresAt > this.now()) {
2388
+ return cloneTokenList(cached.tokens);
2389
+ }
2390
+ const existing = options.signal ? void 0 : this.tokenListInflight.get(key);
2391
+ if (existing) return cloneTokenList(await existing);
2392
+ const pending = load().then((tokens) => {
2393
+ if (ttlMs > 0) {
2394
+ this.tokenListCache.set(key, {
2395
+ expiresAt: this.now() + ttlMs,
2396
+ tokens: cloneTokenList(tokens)
2397
+ });
2398
+ }
2399
+ return tokens;
2400
+ }).finally(() => {
2401
+ if (this.tokenListInflight.get(key) === pending) {
2402
+ this.tokenListInflight.delete(key);
2403
+ }
2404
+ });
2405
+ if (!options.signal) this.tokenListInflight.set(key, pending);
2406
+ return cloneTokenList(await pending);
2407
+ }
2184
2408
  async buildSwap(input) {
2185
2409
  if (isMcaQuote(input.quote)) {
2186
2410
  return this.managedSwapFlow.build({
@@ -2311,7 +2535,23 @@ var SwapClient = class {
2311
2535
  emit({ type: "warning", executionId: build.executionId, warning });
2312
2536
  }
2313
2537
  }
2314
- if ((input.waitFor ?? "submitted") === "completed" && orderId) {
2538
+ const waitsForCompletion = (input.waitFor ?? "submitted") === "completed";
2539
+ const requiresOrderStatus = build.isCrossChain || build.request?.confidentiality === "basic";
2540
+ if (waitsForCompletion && requiresOrderStatus && !orderId) {
2541
+ throw new SwapSdkError(
2542
+ "INVALID_API_RESPONSE",
2543
+ "status",
2544
+ "Swap submitted, but the API did not provide a status key",
2545
+ {
2546
+ details: {
2547
+ executionId: build.executionId,
2548
+ ...result.txHash ? { txHash: result.txHash } : {},
2549
+ router: orderRouter ?? build.router
2550
+ }
2551
+ }
2552
+ );
2553
+ }
2554
+ if (waitsForCompletion && orderId) {
2315
2555
  const status = await this.waitForOrder({
2316
2556
  orderId,
2317
2557
  router: orderRouter ?? build.router,
@@ -2546,6 +2786,13 @@ function isMcaQuote(quote) {
2546
2786
  function readNonEmptyString(value) {
2547
2787
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
2548
2788
  }
2789
+ function cloneTokenList(tokens) {
2790
+ return tokens.map((token) => ({
2791
+ ...token,
2792
+ sources: [...token.sources],
2793
+ raw: { ...token.raw }
2794
+ }));
2795
+ }
2549
2796
  function delay(ms, signal) {
2550
2797
  return new Promise((resolve, reject) => {
2551
2798
  if (signal?.aborted) {
@@ -2594,6 +2841,8 @@ exports.formatUnits = formatUnits;
2594
2841
  exports.fromApiChain = fromApiChain;
2595
2842
  exports.isSameMcaSignerIdentity = isSameMcaSignerIdentity;
2596
2843
  exports.normalizeBuild = normalizeBuild;
2844
+ exports.normalizeCrossChainToTokenList = normalizeCrossChainToTokenList;
2845
+ exports.normalizeFromTokenList = normalizeFromTokenList;
2597
2846
  exports.normalizeHistory = normalizeHistory;
2598
2847
  exports.normalizeHistoryStatus = normalizeHistoryStatus;
2599
2848
  exports.normalizeMcaQuote = normalizeMcaQuote;