@rhea-finance/cross-chain-aggregation-dex 0.2.1 → 0.2.3

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.mjs CHANGED
@@ -43,14 +43,51 @@ function processErrorMessage(errorMessage) {
43
43
  }
44
44
  return TRANSACTION_EXECUTION_ERROR_MESSAGE;
45
45
  }
46
- function normalizeError(error) {
47
- if (!error) return ErrorMessages.EXECUTE_FAILED;
46
+ function normalizeErrorForIntents(error) {
47
+ if (!error) return ErrorMessages.QUOTE_FAILED;
48
48
  const errorLower = error.toLowerCase();
49
- if (errorLower.includes("quote") || errorLower.includes("route") || errorLower.includes("missing") || errorLower.includes("required") || errorLower.includes("invalid") || errorLower.includes("balance") || errorLower.includes("liquidity") || errorLower.includes("slippage") || errorLower.includes("gas") || errorLower.includes("network")) {
49
+ if (errorLower.includes("quote") || errorLower.includes("route") || errorLower.includes("missing") || errorLower.includes("required") || errorLower.includes("invalid") || errorLower.includes("balance") || errorLower.includes("liquidity") || errorLower.includes("gas") || errorLower.includes("network")) {
50
50
  return ErrorMessages.QUOTE_FAILED;
51
51
  }
52
52
  return processErrorMessage(error);
53
53
  }
54
+ function normalizeError(error) {
55
+ if (!error) return ErrorMessages.EXECUTE_FAILED;
56
+ return error;
57
+ }
58
+ function formatErrorMessage({
59
+ error,
60
+ fallbackMessage,
61
+ originAsset,
62
+ friendly
63
+ }) {
64
+ let messageStr = "";
65
+ if (error) {
66
+ if (typeof error === "string") {
67
+ messageStr = error;
68
+ } else if (typeof error === "object") {
69
+ messageStr = error?.message || error?.error || JSON.stringify(error);
70
+ } else {
71
+ messageStr = String(error);
72
+ }
73
+ }
74
+ if (!messageStr || messageStr === "null" || messageStr === "undefined") {
75
+ messageStr = fallbackMessage || "Transaction execution error occurred. Please contact support";
76
+ }
77
+ if (messageStr?.includes("low") && originAsset) {
78
+ return "Amount is too low for bridge.";
79
+ } else if (messageStr?.includes("(")) {
80
+ const index = messageStr.indexOf("(");
81
+ return friendly ? processErrorMessage(messageStr.slice(0, index)) : messageStr.slice(0, index);
82
+ } else if (messageStr?.includes("timed out") || messageStr?.includes("not allowed")) {
83
+ return "transaction has been cancelled";
84
+ } else if ((messageStr?.toLowerCase().includes("tokenin") || messageStr?.toLowerCase().includes("tokenout")) && messageStr?.toLowerCase().includes("not valid")) {
85
+ return "Failed to get quote";
86
+ } else if (messageStr?.toLowerCase().includes("liquidity") && messageStr?.toLowerCase().includes("available")) {
87
+ return "Failed to get quote";
88
+ }
89
+ return friendly ? processErrorMessage(messageStr) : messageStr;
90
+ }
54
91
 
55
92
  // src/utils/logger.ts
56
93
  var LOG_LEVELS = {
@@ -414,7 +451,7 @@ var NearSmartRouter = class {
414
451
  amountOut: "0",
415
452
  minAmountOut: "0",
416
453
  routes: [],
417
- error: normalizeError(response?.result_msg || response?.result_message) || ErrorMessages.QUOTE_FAILED
454
+ error: response?.result_msg || response?.result_message || ErrorMessages.QUOTE_FAILED
418
455
  };
419
456
  }
420
457
  const { routes: serverRoutes, amount_out } = response.result_data;
@@ -453,7 +490,7 @@ var NearSmartRouter = class {
453
490
  amountOut: "0",
454
491
  minAmountOut: "0",
455
492
  routes: [],
456
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
493
+ error: error?.message || ErrorMessages.QUOTE_FAILED
457
494
  };
458
495
  }
459
496
  }
@@ -515,7 +552,6 @@ var NearSmartRouter = class {
515
552
  },
516
553
  gas: "50000000000000",
517
554
  expandDeposit: "1250000000000000000000"
518
- // 0.00125 NEAR
519
555
  });
520
556
  }
521
557
  transactions.push({
@@ -571,7 +607,6 @@ var NearSmartRouter = class {
571
607
  msg: JSON.stringify(swapMsg)
572
608
  },
573
609
  gas: "250",
574
- // NEP-141 requires attaching 1 yoctoNEAR for certain calls.
575
610
  expandDeposit: "1"
576
611
  });
577
612
  const result = await this.nearChainAdapter.call({
@@ -758,7 +793,7 @@ var AggregateDexRouter = class {
758
793
  amountOut: "0",
759
794
  minAmountOut: "0",
760
795
  routes: [],
761
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
796
+ error: error?.message || ErrorMessages.QUOTE_FAILED
762
797
  };
763
798
  }
764
799
  }
@@ -890,7 +925,7 @@ var AggregateDexRouter = class {
890
925
  } catch (error) {
891
926
  return {
892
927
  success: false,
893
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
928
+ error: error?.message || ErrorMessages.QUOTE_FAILED
894
929
  };
895
930
  }
896
931
  const routerMsg = finalQuote.routerMsg;
@@ -1093,7 +1128,8 @@ var AggregateDexRouter = class {
1093
1128
  } catch (error) {
1094
1129
  return {
1095
1130
  success: false,
1096
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
1131
+ // 预交易阶段:保留原始错误信息,不简化
1132
+ error: error?.message || ErrorMessages.QUOTE_FAILED
1097
1133
  };
1098
1134
  }
1099
1135
  const finalAmountToTransfer = finalQuoteForExecution.amountIn;
@@ -1261,17 +1297,9 @@ async function estimateGasLimit(gas, to, transactionData, value, sender, chainId
1261
1297
  reason: estimateError?.reason,
1262
1298
  message: estimateError?.message || String(estimateError)
1263
1299
  };
1264
- console.warn("RPC gas estimate failed:", {
1265
- error: estimateError?.message || String(estimateError),
1266
- code: estimateError?.code,
1267
- reason: estimateError?.reason
1268
- });
1269
1300
  }
1270
1301
  }
1271
1302
  } catch (error) {
1272
- console.warn("Gas estimation error:", {
1273
- error: error?.message || String(error)
1274
- });
1275
1303
  }
1276
1304
  if (estimatedGasLimit && estimatedGasLimit.gt(0) && hasReliableEstimate) {
1277
1305
  return [estimatedGasLimit, true, rpcEstimateError];
@@ -1357,7 +1385,7 @@ var BitgetRouter = class {
1357
1385
  amountOut: "0",
1358
1386
  minAmountOut: "0",
1359
1387
  routes: [],
1360
- error: ErrorMessages.QUOTE_FAILED
1388
+ error: "Bitget quote failed: Router requires recipient address"
1361
1389
  };
1362
1390
  }
1363
1391
  const { tokenIn, tokenOut, amountIn, slippage, sender, recipient } = params;
@@ -1370,7 +1398,7 @@ var BitgetRouter = class {
1370
1398
  amountOut: "0",
1371
1399
  minAmountOut: "0",
1372
1400
  routes: [],
1373
- error: ErrorMessages.QUOTE_FAILED
1401
+ error: "Bitget quote failed: Sender and recipient addresses are required"
1374
1402
  };
1375
1403
  }
1376
1404
  if (tokenIn?.address === void 0 || tokenOut?.address === void 0) {
@@ -1382,7 +1410,7 @@ var BitgetRouter = class {
1382
1410
  amountOut: "0",
1383
1411
  minAmountOut: "0",
1384
1412
  routes: [],
1385
- error: ErrorMessages.QUOTE_FAILED
1413
+ error: "Bitget quote failed: Token addresses are required"
1386
1414
  };
1387
1415
  }
1388
1416
  const normalizedTokenIn = tokenIn.address === "" ? "" : this.normalizeEvmAddress(tokenIn.address);
@@ -1404,7 +1432,7 @@ var BitgetRouter = class {
1404
1432
  if (!isBitgetResponseSuccess(response) || !response.data) {
1405
1433
  return createQuoteError(
1406
1434
  params,
1407
- normalizeError(response.msg) || ErrorMessages.QUOTE_FAILED
1435
+ response.msg || "Bitget API error: Failed to get quote"
1408
1436
  );
1409
1437
  }
1410
1438
  const {
@@ -1470,21 +1498,23 @@ var BitgetRouter = class {
1470
1498
  } catch (error) {
1471
1499
  return createQuoteError(
1472
1500
  params,
1473
- normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
1501
+ error?.message || "Bitget quote failed: Unknown error"
1474
1502
  );
1475
1503
  }
1476
1504
  }
1477
1505
  async executeSwap(params) {
1478
1506
  try {
1479
1507
  if (!requiresRecipientInExecute(params)) {
1480
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1508
+ return createExecuteError("Bitget swap failed: Router requires recipient address");
1481
1509
  }
1482
1510
  const { quote, sender, receiveUser } = params;
1483
1511
  if (!quote.success) {
1484
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1512
+ return createExecuteError(
1513
+ quote.error || "Bitget swap failed: Invalid quote"
1514
+ );
1485
1515
  }
1486
1516
  if (!receiveUser || receiveUser.trim() === "") {
1487
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1517
+ return createExecuteError("Bitget swap failed: Recipient address is required");
1488
1518
  }
1489
1519
  let market;
1490
1520
  try {
@@ -1492,13 +1522,13 @@ var BitgetRouter = class {
1492
1522
  const routerMsg = JSON.parse(quote.routerMsg);
1493
1523
  market = routerMsg.market || "";
1494
1524
  } else {
1495
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1525
+ return createExecuteError("Bitget swap failed: Missing router message data");
1496
1526
  }
1497
1527
  } catch (error) {
1498
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1528
+ return createExecuteError("Bitget swap failed: Invalid router message format");
1499
1529
  }
1500
1530
  if (!market) {
1501
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1531
+ return createExecuteError("Bitget swap failed: Market information is required");
1502
1532
  }
1503
1533
  const normalizedTokenIn = this.normalizeEvmAddress(
1504
1534
  quote.tokenIn.address
@@ -1521,9 +1551,8 @@ var BitgetRouter = class {
1521
1551
  tokenOutDecimals: quote.tokenOut.decimals
1522
1552
  });
1523
1553
  if (!isBitgetResponseSuccess(reQuoteResponse) || !reQuoteResponse.data) {
1524
- return createExecuteError(
1525
- normalizeError(reQuoteResponse.msg) || ErrorMessages.QUOTE_FAILED
1526
- );
1554
+ const errorMsg = reQuoteResponse.msg || "Bitget re-quote failed: No data returned";
1555
+ return createExecuteError(errorMsg);
1527
1556
  }
1528
1557
  market = reQuoteResponse.data?.market || market;
1529
1558
  const swapResponse = await this.bitgetAdapter.swap({
@@ -1542,11 +1571,13 @@ var BitgetRouter = class {
1542
1571
  });
1543
1572
  if (!isBitgetResponseSuccess(swapResponse) || !swapResponse.data) {
1544
1573
  return createExecuteError(
1545
- normalizeError(swapResponse.msg) || ErrorMessages.QUOTE_FAILED
1574
+ swapResponse.msg || "Bitget swap failed: No transaction data"
1546
1575
  );
1547
1576
  }
1548
1577
  if (swapResponse.data?.estimateRevert === true) {
1549
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1578
+ return createExecuteError(
1579
+ swapResponse.msg || "Bitget swap failed: Transaction would revert (slippage or price impact too high)"
1580
+ );
1550
1581
  }
1551
1582
  let transactionData = swapResponse.data?.calldata || swapResponse.data?.data;
1552
1583
  const to = swapResponse.data?.contract || swapResponse.data?.to;
@@ -1560,14 +1591,18 @@ var BitgetRouter = class {
1560
1591
  }
1561
1592
  const gas = swapResponse.data?.gas || (swapResponse.data?.computeUnits !== void 0 ? String(swapResponse.data.computeUnits) : void 0);
1562
1593
  if (!to || !transactionData) {
1563
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1594
+ return createExecuteError(
1595
+ swapResponse.msg || "Bitget swap failed: Missing transaction data or recipient address"
1596
+ );
1564
1597
  }
1565
1598
  if (transactionData.length < 10 || !/^0x[0-9a-fA-F]+$/.test(transactionData)) {
1566
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1599
+ return createExecuteError(
1600
+ swapResponse.msg || "Bitget swap failed: Invalid transaction data format"
1601
+ );
1567
1602
  }
1568
1603
  if (normalizedTokenIn && normalizedTokenIn !== "") {
1569
1604
  if (!this.evmChainAdapter.getBalance) {
1570
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1605
+ return createExecuteError("Bitget swap failed: Balance check not supported");
1571
1606
  }
1572
1607
  const tokenBalanceFormatted = await this.evmChainAdapter.getBalance({
1573
1608
  address: sender,
@@ -1577,7 +1612,7 @@ var BitgetRouter = class {
1577
1612
  const balanceBN = ethers.utils.parseUnits(tokenBalanceFormatted || "0", tokenDecimals);
1578
1613
  const amountInBN = ethers.BigNumber.from(quote.amountIn);
1579
1614
  if (balanceBN.lt(amountInBN)) {
1580
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1615
+ return createExecuteError("Bitget swap failed: Insufficient token balance");
1581
1616
  }
1582
1617
  const currentAllowance = await this.evmChainAdapter.getAllowance({
1583
1618
  tokenAddress: normalizedTokenIn,
@@ -1610,17 +1645,17 @@ var BitgetRouter = class {
1610
1645
  retryCount++;
1611
1646
  }
1612
1647
  if (newAllowanceBN.lt(amountInBN)) {
1613
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1648
+ return createExecuteError("Bitget swap failed: Token approval failed or insufficient");
1614
1649
  }
1615
1650
  } catch (error) {
1616
1651
  return createExecuteError(
1617
- normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
1652
+ error?.message || "Bitget approval check failed"
1618
1653
  );
1619
1654
  }
1620
1655
  }
1621
1656
  } else {
1622
1657
  if (!this.evmChainAdapter.getBalance) {
1623
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1658
+ return createExecuteError("Bitget swap failed: Balance check not supported");
1624
1659
  }
1625
1660
  const nativeBalanceFormatted = await this.evmChainAdapter.getBalance({
1626
1661
  address: sender,
@@ -1644,7 +1679,7 @@ var BitgetRouter = class {
1644
1679
  const gasCostEstimate = estimatedGasLimitForBalance.mul(gasPriceEstimate2);
1645
1680
  const totalRequired = amountInBN.add(gasCostEstimate);
1646
1681
  if (balanceBN.lt(totalRequired)) {
1647
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1682
+ return createExecuteError("Bitget swap failed: Insufficient balance (including gas fee)");
1648
1683
  }
1649
1684
  }
1650
1685
  if (normalizedTokenIn && normalizedTokenIn !== "") {
@@ -1656,7 +1691,7 @@ var BitgetRouter = class {
1656
1691
  const finalAllowanceBN = ethers.BigNumber.from(finalAllowanceCheck);
1657
1692
  const amountInBN = ethers.BigNumber.from(quote.amountIn);
1658
1693
  if (finalAllowanceBN.lt(amountInBN)) {
1659
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1694
+ return createExecuteError("Bitget swap failed: Insufficient token allowance");
1660
1695
  }
1661
1696
  }
1662
1697
  const [estimatedGasLimit, hasReliableEstimate, rpcEstimateError] = await estimateGasLimit(
@@ -1677,10 +1712,13 @@ var BitgetRouter = class {
1677
1712
  tokenOut: quote.tokenOut.symbol,
1678
1713
  rpcError: rpcEstimateError
1679
1714
  });
1680
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1715
+ const errorMsg = rpcEstimateError?.message || rpcEstimateError?.reason || "";
1716
+ return createExecuteError(
1717
+ errorMsg || "Bitget swap failed: Transaction would revert (slippage or price impact too high)"
1718
+ );
1681
1719
  }
1682
1720
  if (!gas || gas === "0") {
1683
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1721
+ return createExecuteError("Bitget swap failed: Invalid gas estimate from Bitget");
1684
1722
  }
1685
1723
  logger.warn("RPC gas estimation failed, using Bitget estimate", {
1686
1724
  chainId: this.chainId,
@@ -1713,7 +1751,7 @@ var BitgetRouter = class {
1713
1751
  if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas || feeData.maxFeePerGas.lte(0) || feeData.maxPriorityFeePerGas.lte(0)) {
1714
1752
  const cachedGasPrice = await getCachedGasPrice();
1715
1753
  if (!cachedGasPrice || cachedGasPrice.lte(0)) {
1716
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1754
+ return createExecuteError("Bitget swap failed: Unable to get valid gas price");
1717
1755
  }
1718
1756
  feeData.maxFeePerGas = cachedGasPrice;
1719
1757
  feeData.maxPriorityFeePerGas = cachedGasPrice.div(10);
@@ -1746,27 +1784,27 @@ var BitgetRouter = class {
1746
1784
  } catch (error) {
1747
1785
  const cachedGasPrice = await getCachedGasPrice();
1748
1786
  if (!cachedGasPrice || cachedGasPrice.lte(0)) {
1749
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1787
+ return createExecuteError("Bitget swap failed: Unable to get valid gas price");
1750
1788
  }
1751
1789
  feeData.gasPrice = cachedGasPrice;
1752
1790
  }
1753
1791
  }
1754
1792
  if (!to || !ethers.utils.isAddress(to)) {
1755
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1793
+ return createExecuteError("Bitget swap failed: Invalid recipient address");
1756
1794
  }
1757
1795
  if (!transactionData || transactionData.length < 10) {
1758
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1796
+ return createExecuteError("Bitget swap failed: Invalid transaction data");
1759
1797
  }
1760
1798
  if (!estimatedGasLimit || estimatedGasLimit.lte(0)) {
1761
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1799
+ return createExecuteError("Bitget swap failed: Invalid gas limit");
1762
1800
  }
1763
1801
  if (supportsEip1559) {
1764
1802
  if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas || feeData.maxFeePerGas.lte(0) || feeData.maxPriorityFeePerGas.lte(0)) {
1765
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1803
+ return createExecuteError("Bitget swap failed: Invalid gas fee data (EIP-1559)");
1766
1804
  }
1767
1805
  } else {
1768
1806
  if (!feeData.gasPrice || feeData.gasPrice.lte(0)) {
1769
- return createExecuteError(ErrorMessages.QUOTE_FAILED);
1807
+ return createExecuteError("Bitget swap failed: Invalid gas price");
1770
1808
  }
1771
1809
  }
1772
1810
  const txParams = {
@@ -1806,218 +1844,1093 @@ var BitgetRouter = class {
1806
1844
  }
1807
1845
  }
1808
1846
  };
1809
- async function completeQuote(params, config) {
1810
- const {
1811
- sourceToken,
1812
- targetToken,
1813
- sourceChain,
1814
- targetChain: _targetChain,
1815
- // Reserved for future use
1816
- amountIn,
1817
- slippage,
1818
- recipient,
1819
- refundTo,
1820
- customRecipientMsg,
1821
- appFees,
1822
- evmChainId
1823
- } = params;
1824
- const {
1825
- intentsQuotationAdapter,
1826
- dexRouters,
1827
- dexRouter,
1828
- bluechipTokens,
1829
- configAdapter,
1830
- currentUserAddress,
1831
- isIntentsSupportedToken: customIsIntentsSupportedToken
1832
- } = config;
1833
- const wrapNearContractId = configAdapter.getWrapNearContractId();
1834
- const routers = dexRouters || (dexRouter ? [dexRouter] : []);
1835
- const userAddress = currentUserAddress || recipient;
1836
- if (!userAddress) {
1837
- throw new Error(ErrorMessages.QUOTE_FAILED);
1847
+ var OkxRouter = class {
1848
+ constructor(config) {
1849
+ this.okxAdapter = config.okxAdapter;
1850
+ this.evmChainAdapter = config.evmChainAdapter;
1851
+ this.chainId = config.chainId;
1838
1852
  }
1839
- if (sourceToken?.address === void 0) {
1840
- throw new Error(ErrorMessages.QUOTE_FAILED);
1853
+ getCapabilities() {
1854
+ return {
1855
+ requiresRecipient: true,
1856
+ requiresFinalizeQuote: false,
1857
+ requiresComplexRegistration: false,
1858
+ supportedChain: "evm"
1859
+ };
1841
1860
  }
1842
- if (targetToken?.address === void 0) {
1843
- throw new Error(ErrorMessages.QUOTE_FAILED);
1861
+ getSupportedChain() {
1862
+ return "evm";
1844
1863
  }
1845
- const isEvmChain = evmChainId !== void 0 || sourceChain === "evm" || sourceChain === "ethereum" || sourceChain === "bsc" || sourceChain === "polygon" || sourceChain === "base" || sourceChain === "monad" || sourceToken.chain === "evm";
1846
- const isTokenIntentsSupported = customIsIntentsSupportedToken ? customIsIntentsSupportedToken(sourceToken) : isEvmChain ? isEvmIntentsSupportedToken(sourceToken, bluechipTokens) : isNearIntentsSupportedToken(sourceToken, bluechipTokens);
1847
- const bluechipToken = isEvmChain ? findBestEvmBluechipToken(
1848
- bluechipTokens,
1849
- configAdapter.getEvmNativeWrappedTokenAddress?.()
1850
- ) : findBestBluechipToken(bluechipTokens, wrapNearContractId);
1851
- if (!bluechipToken?.address) {
1852
- throw new Error(ErrorMessages.QUOTE_FAILED);
1864
+ getChainId() {
1865
+ return this.chainId;
1853
1866
  }
1854
- const quotePaths = [];
1855
- routers.forEach((router, index) => {
1856
- const supportedChain = router.getSupportedChain();
1857
- const isEvmRouter = supportedChain === "evm";
1858
- let routeType;
1859
- if (isEvmRouter) {
1860
- routeType = "evm";
1861
- } else {
1862
- routeType = index === 0 ? "v1" : "v2";
1863
- }
1864
- const capabilities = router.getCapabilities();
1865
- const quoteParams = capabilities.requiresRecipient ? {
1866
- tokenIn: sourceToken,
1867
- tokenOut: bluechipToken,
1868
- amountIn,
1869
- slippage,
1870
- swapType: "EXACT_INPUT",
1871
- sender: userAddress,
1872
- recipient: userAddress
1873
- } : {
1874
- tokenIn: sourceToken,
1875
- tokenOut: bluechipToken,
1876
- amountIn,
1877
- slippage,
1878
- swapType: "EXACT_INPUT"
1879
- };
1880
- quotePaths.push({
1881
- type: routeType,
1882
- router,
1883
- promise: (async () => {
1884
- const preSwapQuote = await router.quote(quoteParams);
1885
- if (!preSwapQuote.success) {
1886
- throw new Error(ErrorMessages.QUOTE_FAILED);
1887
- }
1888
- let normalizedSourceAsset;
1889
- if (isEvmChain) {
1890
- const bluechipKey = bluechipToken.symbol?.toUpperCase();
1891
- const bluechipTokenConfig = bluechipKey && bluechipTokens[bluechipKey] || void 0;
1892
- normalizedSourceAsset = bluechipTokenConfig?.assetId ? bluechipTokenConfig.assetId : `evm:${normalizeEvmAddress(bluechipToken.address)}`;
1893
- } else {
1894
- const bluechipKey = bluechipToken.symbol?.toUpperCase() === "WNEAR" ? "NEAR" : bluechipToken.symbol?.toUpperCase();
1895
- const bluechipTokenConfig = bluechipKey && bluechipTokens[bluechipKey] || void 0;
1896
- normalizedSourceAsset = bluechipTokenConfig?.assetId ? bluechipTokenConfig.assetId : `nep141:${bluechipToken.address}`;
1897
- }
1898
- let normalizedTargetAsset = targetToken.address;
1899
- if (normalizedTargetAsset?.startsWith("1cs_v1:")) ; else if (normalizedTargetAsset && !normalizedTargetAsset.startsWith("nep141:") && !normalizedTargetAsset.startsWith("nep245:") && normalizedTargetAsset.includes(".")) {
1900
- normalizedTargetAsset = `nep141:${normalizeTokenId(
1901
- normalizedTargetAsset,
1902
- wrapNearContractId
1903
- )}`;
1904
- }
1905
- if (!normalizedTargetAsset?.startsWith("1cs_v1:")) {
1906
- normalizedTargetAsset = normalizeDestinationAsset(normalizedTargetAsset, wrapNearContractId) || normalizedTargetAsset;
1907
- }
1908
- const slippageBps = convertSlippageToBasisPoints(slippage);
1909
- let formattedAmountOut = preSwapQuote.amountOut;
1910
- if (isEvmChain && bluechipToken.decimals !== void 0) {
1911
- try {
1912
- const amountBN = new Big3(preSwapQuote.amountOut);
1913
- if (amountBN.lte(0)) {
1914
- throw new Error(ErrorMessages.QUOTE_FAILED);
1915
- }
1916
- formattedAmountOut = amountBN.toFixed(0, Big3.roundDown);
1917
- } catch (error) {
1918
- throw new Error(ErrorMessages.QUOTE_FAILED);
1919
- }
1920
- }
1921
- try {
1922
- const intentsQuote = await intentsQuotationAdapter.quote({
1923
- originAsset: normalizedSourceAsset,
1924
- destinationAsset: normalizedTargetAsset,
1925
- amount: formattedAmountOut,
1926
- refundTo: refundTo || recipient,
1927
- recipient,
1928
- slippageTolerance: slippageBps,
1929
- swapType: "FLEX_INPUT",
1930
- ...customRecipientMsg ? { customRecipientMsg } : {},
1931
- ...appFees ? { appFees } : {}
1932
- });
1933
- if (intentsQuote.quoteStatus !== "success") {
1934
- throw new Error(ErrorMessages.QUOTE_FAILED);
1935
- }
1936
- return {
1937
- intentsQuote,
1938
- preSwapQuote,
1939
- router,
1940
- finalAmountOut: intentsQuote.quoteSuccessResult?.quote?.amountOut || "0"
1941
- };
1942
- } catch (error) {
1943
- throw error;
1944
- }
1945
- })()
1946
- });
1947
- });
1948
- if (isTokenIntentsSupported) {
1949
- quotePaths.push({
1950
- type: "intents",
1951
- promise: (async () => {
1952
- let normalizedSourceAsset;
1953
- if (isEvmChain) {
1954
- const sourceKey = sourceToken.symbol?.toUpperCase();
1955
- const sourceTokenConfig = sourceKey ? bluechipTokens[sourceKey] : void 0;
1956
- if (sourceTokenConfig?.assetId) {
1957
- normalizedSourceAsset = sourceTokenConfig.assetId;
1958
- } else if (sourceToken.address === "") {
1959
- normalizedSourceAsset = `evm-${sourceChain}-native`;
1960
- } else {
1961
- normalizedSourceAsset = `evm:${normalizeEvmAddress(sourceToken.address)}`;
1962
- }
1963
- } else {
1964
- const sourceKey = sourceToken.symbol?.toUpperCase();
1965
- const sourceTokenConfig = sourceKey ? bluechipTokens[sourceKey] : void 0;
1966
- if (sourceTokenConfig?.assetId) {
1967
- normalizedSourceAsset = sourceTokenConfig.assetId;
1968
- } else {
1969
- normalizedSourceAsset = normalizeTokenId(sourceToken.address, wrapNearContractId);
1970
- if (!normalizedSourceAsset.startsWith("nep141:")) {
1971
- normalizedSourceAsset = `nep141:${normalizedSourceAsset}`;
1972
- }
1973
- }
1974
- }
1975
- let normalizedTargetAsset = targetToken.address;
1976
- if (normalizedTargetAsset?.startsWith("1cs_v1:")) ; else if (normalizedTargetAsset && !normalizedTargetAsset.startsWith("nep141:") && !normalizedTargetAsset.startsWith("nep245:") && normalizedTargetAsset.includes(".")) {
1977
- normalizedTargetAsset = `nep141:${normalizeTokenId(
1978
- normalizedTargetAsset,
1979
- wrapNearContractId
1980
- )}`;
1981
- }
1982
- if (!normalizedTargetAsset?.startsWith("1cs_v1:")) {
1983
- normalizedTargetAsset = normalizeDestinationAsset(normalizedTargetAsset, wrapNearContractId) || normalizedTargetAsset;
1984
- }
1985
- const slippageBps = convertSlippageToBasisPoints(slippage);
1986
- const intentsQuote = await intentsQuotationAdapter.quote({
1987
- originAsset: normalizedSourceAsset,
1988
- destinationAsset: normalizedTargetAsset,
1989
- amount: amountIn,
1990
- refundTo: refundTo || recipient,
1991
- recipient,
1992
- slippageTolerance: slippageBps,
1993
- swapType: "EXACT_INPUT",
1994
- ...customRecipientMsg ? { customRecipientMsg } : {}
1995
- });
1996
- if (intentsQuote.quoteStatus !== "success") {
1997
- throw new Error(ErrorMessages.QUOTE_FAILED);
1998
- }
1867
+ async quote(params) {
1868
+ try {
1869
+ if (!requiresRecipient(params)) {
1870
+ return createQuoteError(params, "OKX quote failed: Router requires recipient address");
1871
+ }
1872
+ const { tokenIn, tokenOut, amountIn, slippage, sender, recipient } = params;
1873
+ if (!sender || !recipient) {
1874
+ return createQuoteError(params, "OKX quote failed: Sender and recipient addresses are required");
1875
+ }
1876
+ if (tokenIn?.address === void 0 || tokenOut?.address === void 0) {
1877
+ return createQuoteError(params, "OKX quote failed: Token addresses are required");
1878
+ }
1879
+ const normalizedTokenIn = tokenIn.address === "" ? "" : this.normalizeEvmAddress(tokenIn.address);
1880
+ const normalizedTokenOut = tokenOut.address === "" ? "" : this.normalizeEvmAddress(tokenOut.address);
1881
+ const slippageBps = convertSlippageToBasisPoints(slippage);
1882
+ const slippageDecimal = slippageBps / 1e4;
1883
+ const response = await this.okxAdapter.quote({
1884
+ chainId: this.chainId,
1885
+ tokenIn: normalizedTokenIn,
1886
+ tokenOut: normalizedTokenOut,
1887
+ amountIn: String(amountIn),
1888
+ slippage: slippageDecimal,
1889
+ userAddress: sender,
1890
+ tokenInSymbol: tokenIn.symbol,
1891
+ tokenInDecimals: tokenIn.decimals,
1892
+ tokenOutSymbol: tokenOut.symbol,
1893
+ tokenOutDecimals: tokenOut.decimals
1894
+ });
1895
+ const is429Error = response.code === "429" || response.code === 429 || response.msg && response.msg.toLowerCase().includes("rate limit");
1896
+ if (is429Error) {
1999
1897
  return {
2000
- intentsQuote,
2001
- finalAmountOut: intentsQuote.quoteSuccessResult?.quote?.amountOut || "0"
1898
+ success: false,
1899
+ tokenIn: params.tokenIn,
1900
+ tokenOut: params.tokenOut,
1901
+ amountIn: params.amountIn,
1902
+ amountOut: "0",
1903
+ minAmountOut: "0",
1904
+ routes: [],
1905
+ error: "Rate limit exceeded"
2002
1906
  };
2003
- })()
2004
- });
2005
- }
2006
- const pathResults = await Promise.allSettled(
2007
- quotePaths.map((p) => p.promise)
2008
- );
2009
- const validPaths = [];
2010
- pathResults.forEach((result, index) => {
2011
- const pathType = quotePaths[index].type;
2012
- if (result.status === "fulfilled") {
2013
- validPaths.push({
2014
- type: pathType,
2015
- ...result.value
1907
+ }
1908
+ if (response.code !== "0" && response.code !== 0 && response.code !== void 0) {
1909
+ const errorCode = response.code;
1910
+ const errorMsg = response.msg || "Unknown error";
1911
+ logger.error("OKX quote API error", {
1912
+ code: errorCode,
1913
+ msg: errorMsg,
1914
+ tokenIn: params.tokenIn.symbol,
1915
+ tokenOut: params.tokenOut.symbol,
1916
+ amountIn: params.amountIn,
1917
+ fullResponse: response
1918
+ });
1919
+ return createQuoteError(params, `OKX API error (${errorCode}): ${errorMsg}`);
1920
+ }
1921
+ if (!response.data || response.data.length === 0) {
1922
+ logger.error("OKX quote empty data", {
1923
+ code: response.code,
1924
+ msg: response.msg,
1925
+ hasData: !!response.data,
1926
+ dataLength: response.data?.length || 0,
1927
+ tokenIn: params.tokenIn.symbol,
1928
+ tokenOut: params.tokenOut.symbol,
1929
+ fullResponse: response
1930
+ });
1931
+ return createQuoteError(
1932
+ params,
1933
+ response.msg || "OKX API returned empty data"
1934
+ );
1935
+ }
1936
+ const quoteData = response.data[0];
1937
+ const fromTokenAmount = quoteData.fromTokenAmount || quoteData.fromAmount;
1938
+ const toTokenAmount = quoteData.toTokenAmount || quoteData.toAmount;
1939
+ const estimateGasFee = quoteData.estimateGasFee || quoteData.estimatedGas;
1940
+ let minToAmount;
1941
+ if (toTokenAmount) {
1942
+ const toAmountBN = ethers.BigNumber.from(toTokenAmount);
1943
+ const slippageAmount = toAmountBN.mul(Math.floor(slippageDecimal * 1e4)).div(1e4);
1944
+ minToAmount = toAmountBN.sub(slippageAmount).toString();
1945
+ } else {
1946
+ minToAmount = "0";
1947
+ }
1948
+ const formattedAmountOut = toTokenAmount ? ethers.BigNumber.from(toTokenAmount).toString() : "0";
1949
+ const formattedMinAmountOut = minToAmount || formattedAmountOut;
1950
+ return {
1951
+ success: true,
1952
+ tokenIn,
1953
+ tokenOut,
1954
+ amountIn: fromTokenAmount || String(amountIn),
1955
+ amountOut: formattedAmountOut,
1956
+ minAmountOut: formattedMinAmountOut,
1957
+ routes: [],
1958
+ gasEstimate: estimateGasFee,
1959
+ routerMsg: JSON.stringify({
1960
+ chainId: this.chainId,
1961
+ adapter: "okx"
1962
+ }),
1963
+ recipient,
1964
+ slippage
1965
+ };
1966
+ } catch (error) {
1967
+ const errorMessage = error?.message || String(error);
1968
+ const isRateLimit = errorMessage?.includes("429") || errorMessage?.toLowerCase().includes("rate limit") || errorMessage?.toLowerCase().includes("too many requests");
1969
+ logger.error("OKX quote exception", {
1970
+ error: errorMessage,
1971
+ isRateLimit,
1972
+ tokenIn: params.tokenIn.symbol,
1973
+ tokenOut: params.tokenOut.symbol,
1974
+ amountIn: params.amountIn,
1975
+ errorStack: error?.stack,
1976
+ fullError: error
2016
1977
  });
1978
+ const detailedError = isRateLimit ? `OKX API rate limit error: ${errorMessage}. Please try again later.` : `OKX quote failed: ${errorMessage}`;
1979
+ return createQuoteError(params, detailedError);
2017
1980
  }
2018
- });
2019
- if (validPaths.length === 0) {
2020
- throw new Error(ErrorMessages.QUOTE_FAILED);
1981
+ }
1982
+ async executeSwap(params) {
1983
+ try {
1984
+ if (!requiresRecipientInExecute(params)) {
1985
+ return createExecuteError("OKX swap failed: Router requires recipient address");
1986
+ }
1987
+ const { quote, sender, receiveUser } = params;
1988
+ if (!quote.success) {
1989
+ return createExecuteError(
1990
+ quote.error || "OKX swap failed: Invalid quote"
1991
+ );
1992
+ }
1993
+ if (!receiveUser || receiveUser.trim() === "") {
1994
+ return createExecuteError("OKX swap failed: Recipient address is required");
1995
+ }
1996
+ const normalizedTokenIn = this.normalizeEvmAddress(quote.tokenIn.address);
1997
+ const normalizedTokenOut = this.normalizeEvmAddress(
1998
+ quote.tokenOut.address
1999
+ );
2000
+ const slippageBps = convertSlippageToBasisPoints(quote.slippage || 5e-3);
2001
+ const executionSlippage = slippageBps / 1e4;
2002
+ const reQuoteResponse = await this.okxAdapter.quote({
2003
+ chainId: this.chainId,
2004
+ tokenIn: normalizedTokenIn,
2005
+ tokenOut: normalizedTokenOut,
2006
+ amountIn: quote.amountIn,
2007
+ slippage: executionSlippage,
2008
+ userAddress: receiveUser,
2009
+ tokenInSymbol: quote.tokenIn.symbol,
2010
+ tokenInDecimals: quote.tokenIn.decimals,
2011
+ tokenOutSymbol: quote.tokenOut.symbol,
2012
+ tokenOutDecimals: quote.tokenOut.decimals
2013
+ });
2014
+ if (reQuoteResponse.code !== "0" && reQuoteResponse.code !== 0 && reQuoteResponse.code !== void 0) {
2015
+ const errorMsg = reQuoteResponse.msg || `OKX re-quote failed: Error code ${reQuoteResponse.code}`;
2016
+ return createExecuteError(errorMsg);
2017
+ }
2018
+ if (!reQuoteResponse.data || reQuoteResponse.data.length === 0) {
2019
+ const errorMsg = reQuoteResponse.msg || "OKX re-quote failed: No data returned";
2020
+ return createExecuteError(errorMsg);
2021
+ }
2022
+ const reQuoteData = reQuoteResponse.data[0];
2023
+ const toTokenAmount = reQuoteData.toTokenAmount || reQuoteData.toAmount;
2024
+ const okxEstimateGasFee = reQuoteData.estimateGasFee || reQuoteData.estimatedGas;
2025
+ const isCrossChain = receiveUser.toLowerCase() !== sender.toLowerCase();
2026
+ let minAmountOut;
2027
+ if (reQuoteData.minToAmount && reQuoteData.minToAmount !== "0") {
2028
+ minAmountOut = reQuoteData.minToAmount;
2029
+ if (isCrossChain) {
2030
+ logger.info("OKX swap: Using API minToAmount for cross-chain swap", {
2031
+ minToAmount: minAmountOut,
2032
+ toTokenAmount
2033
+ });
2034
+ }
2035
+ } else if (toTokenAmount) {
2036
+ const toAmountBN = ethers.BigNumber.from(toTokenAmount);
2037
+ const effectiveSlippage = isCrossChain ? executionSlippage + 0.02 : executionSlippage;
2038
+ const slippageBps2 = ethers.BigNumber.from(Math.floor(effectiveSlippage * 1e4));
2039
+ const slippageAmount = toAmountBN.mul(slippageBps2).div(1e4);
2040
+ minAmountOut = toAmountBN.sub(slippageAmount).toString();
2041
+ if (isCrossChain) {
2042
+ logger.info("OKX swap: Calculated minAmountOut for cross-chain swap", {
2043
+ toTokenAmount,
2044
+ executionSlippage,
2045
+ effectiveSlippage,
2046
+ slippageBps: slippageBps2.toString(),
2047
+ slippageAmount: slippageAmount.toString(),
2048
+ minAmountOut
2049
+ });
2050
+ }
2051
+ } else {
2052
+ minAmountOut = "0";
2053
+ }
2054
+ logger.info("OKX swap: calling swap API", {
2055
+ chainId: this.chainId,
2056
+ tokenIn: normalizedTokenIn,
2057
+ tokenOut: normalizedTokenOut,
2058
+ amountIn: quote.amountIn,
2059
+ minAmountOut,
2060
+ fromAddress: sender,
2061
+ toAddress: receiveUser,
2062
+ receiveUser,
2063
+ tokenInSymbol: quote.tokenIn.symbol,
2064
+ tokenInDecimals: quote.tokenIn.decimals,
2065
+ tokenOutSymbol: quote.tokenOut.symbol,
2066
+ tokenOutDecimals: quote.tokenOut.decimals
2067
+ });
2068
+ const swapResponse = await this.okxAdapter.swap({
2069
+ chainId: this.chainId,
2070
+ tokenIn: normalizedTokenIn,
2071
+ tokenOut: normalizedTokenOut,
2072
+ amountIn: quote.amountIn,
2073
+ minAmountOut,
2074
+ slippage: executionSlippage,
2075
+ fromAddress: sender,
2076
+ toAddress: receiveUser,
2077
+ tokenInSymbol: quote.tokenIn.symbol,
2078
+ tokenInDecimals: quote.tokenIn.decimals,
2079
+ tokenOutSymbol: quote.tokenOut.symbol,
2080
+ tokenOutDecimals: quote.tokenOut.decimals
2081
+ });
2082
+ if (swapResponse.code !== "0" && swapResponse.code !== 0 && swapResponse.code !== void 0) {
2083
+ return createExecuteError(
2084
+ swapResponse.msg || `OKX swap failed: Error code ${swapResponse.code}`
2085
+ );
2086
+ }
2087
+ if (!swapResponse.data) {
2088
+ return createExecuteError(
2089
+ swapResponse.msg || "OKX swap failed: No transaction data"
2090
+ );
2091
+ }
2092
+ let txData = null;
2093
+ if (Array.isArray(swapResponse.data) && swapResponse.data.length > 0) {
2094
+ const firstItem = swapResponse.data[0];
2095
+ txData = firstItem?.tx || firstItem;
2096
+ } else if (swapResponse.data && !Array.isArray(swapResponse.data)) {
2097
+ const data = swapResponse.data;
2098
+ if (data.tx) {
2099
+ txData = data.tx;
2100
+ } else if (data.transaction) {
2101
+ txData = data.transaction;
2102
+ } else {
2103
+ txData = data;
2104
+ }
2105
+ }
2106
+ if (!txData) {
2107
+ logger.error("OKX swap response: no txData found", {
2108
+ swapResponse
2109
+ });
2110
+ return createExecuteError(
2111
+ swapResponse.msg || "OKX swap failed: No transaction data in response"
2112
+ );
2113
+ }
2114
+ const dataObj = Array.isArray(swapResponse.data) ? swapResponse.data[0] : swapResponse.data;
2115
+ if (dataObj?.estimateRevert === true || txData?.estimateRevert === true) {
2116
+ logger.error("OKX swap: estimateRevert is true, transaction would fail", {
2117
+ swapResponse,
2118
+ txData
2119
+ });
2120
+ return createExecuteError(
2121
+ swapResponse.msg || "OKX swap failed: Transaction would revert (slippage or price impact too high)"
2122
+ );
2123
+ }
2124
+ let transactionData = txData.data || "";
2125
+ const to = txData.to || "";
2126
+ let value = txData.value || "0";
2127
+ if (transactionData && !transactionData.startsWith("0x")) {
2128
+ transactionData = "0x" + transactionData;
2129
+ }
2130
+ const isNativeTokenIn = !normalizedTokenIn || normalizedTokenIn === "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
2131
+ if (isNativeTokenIn) {
2132
+ value = quote.amountIn;
2133
+ }
2134
+ const gas = txData.gas || txData.gasPrice || okxEstimateGasFee || void 0;
2135
+ if (!to || !transactionData) {
2136
+ logger.error("OKX swap response: missing to or transactionData", {
2137
+ to,
2138
+ hasTransactionData: !!transactionData,
2139
+ txData
2140
+ });
2141
+ return createExecuteError(
2142
+ swapResponse.msg || "OKX swap failed: Missing transaction data or recipient address"
2143
+ );
2144
+ }
2145
+ if (transactionData.length < 10 || !/^0x[0-9a-fA-F]+$/.test(transactionData)) {
2146
+ logger.error("OKX swap: invalid transaction data format", {
2147
+ transactionDataLength: transactionData.length,
2148
+ transactionDataPrefix: transactionData.substring(0, 20)
2149
+ });
2150
+ return createExecuteError(
2151
+ swapResponse.msg || "OKX swap failed: Invalid transaction data format"
2152
+ );
2153
+ }
2154
+ logger.info("OKX swap: starting balance and allowance checks");
2155
+ let dexContractAddress;
2156
+ if (normalizedTokenIn && normalizedTokenIn !== "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") {
2157
+ logger.info("OKX swap: checking ERC20 token balance and allowance", {
2158
+ tokenIn: normalizedTokenIn,
2159
+ sender,
2160
+ to
2161
+ });
2162
+ if (!this.evmChainAdapter.getBalance) {
2163
+ return createExecuteError("OKX swap failed: Balance check not supported");
2164
+ }
2165
+ const tokenBalanceFormatted = await this.evmChainAdapter.getBalance({
2166
+ address: sender,
2167
+ tokenAddress: normalizedTokenIn
2168
+ });
2169
+ const tokenDecimals = quote.tokenIn.decimals || 18;
2170
+ const balanceBN = ethers.utils.parseUnits(
2171
+ tokenBalanceFormatted || "0",
2172
+ tokenDecimals
2173
+ );
2174
+ const amountInBN = ethers.BigNumber.from(quote.amountIn);
2175
+ logger.info("OKX swap: token balance check", {
2176
+ balance: balanceBN.toString(),
2177
+ amountIn: amountInBN.toString(),
2178
+ hasEnoughBalance: balanceBN.gte(amountInBN)
2179
+ });
2180
+ if (balanceBN.lt(amountInBN)) {
2181
+ logger.error("OKX swap: insufficient token balance", {
2182
+ balance: balanceBN.toString(),
2183
+ amountIn: amountInBN.toString()
2184
+ });
2185
+ return createExecuteError("OKX swap failed: Insufficient token balance");
2186
+ }
2187
+ logger.info("OKX swap: getting approve transaction to find dexContractAddress", {
2188
+ tokenIn: normalizedTokenIn,
2189
+ amountIn: quote.amountIn
2190
+ });
2191
+ const approveResponse = await this.okxAdapter.getApproveTransaction({
2192
+ chainId: this.chainId,
2193
+ tokenAddress: normalizedTokenIn,
2194
+ approveAmount: quote.amountIn
2195
+ });
2196
+ if (approveResponse.code !== "0" && approveResponse.code !== 0) {
2197
+ logger.error("OKX swap: failed to get approve transaction", {
2198
+ code: approveResponse.code,
2199
+ msg: approveResponse.msg
2200
+ });
2201
+ return createExecuteError(
2202
+ approveResponse.msg || "Failed to get OKX approve transaction"
2203
+ );
2204
+ }
2205
+ dexContractAddress = approveResponse.data?.dexContractAddress;
2206
+ if (!dexContractAddress) {
2207
+ logger.error("OKX swap: no dexContractAddress in approve response");
2208
+ return createExecuteError("Failed to get OKX DEX contract address");
2209
+ }
2210
+ logger.info("OKX swap: checking token allowance", {
2211
+ tokenIn: normalizedTokenIn,
2212
+ owner: sender,
2213
+ spender: dexContractAddress
2214
+ });
2215
+ const currentAllowance = await this.evmChainAdapter.getAllowance({
2216
+ tokenAddress: normalizedTokenIn,
2217
+ owner: sender,
2218
+ spender: dexContractAddress
2219
+ });
2220
+ const allowanceBN = ethers.BigNumber.from(currentAllowance);
2221
+ logger.info("OKX swap: token allowance check", {
2222
+ allowance: allowanceBN.toString(),
2223
+ amountIn: amountInBN.toString(),
2224
+ hasEnoughAllowance: allowanceBN.gte(amountInBN),
2225
+ dexContractAddress,
2226
+ sender,
2227
+ tokenIn: normalizedTokenIn
2228
+ });
2229
+ if (allowanceBN.lt(amountInBN)) {
2230
+ logger.warn("OKX swap: insufficient token allowance, need to approve", {
2231
+ allowance: allowanceBN.toString(),
2232
+ amountIn: amountInBN.toString(),
2233
+ dexContractAddress,
2234
+ sender,
2235
+ tokenIn: normalizedTokenIn
2236
+ });
2237
+ if (!approveResponse.data?.data) {
2238
+ logger.error("OKX swap: no approve transaction data");
2239
+ return createExecuteError("Failed to get OKX approve transaction data");
2240
+ }
2241
+ try {
2242
+ const [estimatedGasLimit, hasReliableEstimate] = await estimateGasLimit(
2243
+ approveResponse.data.gasLimit,
2244
+ normalizedTokenIn,
2245
+ approveResponse.data.data,
2246
+ "0",
2247
+ sender,
2248
+ this.chainId,
2249
+ this.evmChainAdapter
2250
+ );
2251
+ const approveTxParams = {
2252
+ to: normalizedTokenIn,
2253
+ data: approveResponse.data.data,
2254
+ value: "0",
2255
+ gasLimit: estimatedGasLimit.toString()
2256
+ };
2257
+ if (isEip1559Chain(this.chainId)) {
2258
+ const feeData2 = await getEip1559FeeData(this.chainId, this.evmChainAdapter);
2259
+ if (feeData2.maxFeePerGas && feeData2.maxPriorityFeePerGas) {
2260
+ approveTxParams.type = 2;
2261
+ approveTxParams.maxFeePerGas = feeData2.maxFeePerGas.toString();
2262
+ approveTxParams.maxPriorityFeePerGas = feeData2.maxPriorityFeePerGas.toString();
2263
+ } else {
2264
+ if (approveResponse.data.gasPrice) {
2265
+ approveTxParams.gasPrice = approveResponse.data.gasPrice;
2266
+ }
2267
+ }
2268
+ } else {
2269
+ if (approveResponse.data.gasPrice) {
2270
+ approveTxParams.gasPrice = approveResponse.data.gasPrice;
2271
+ } else {
2272
+ const estimatedGasPrice = await getGasPriceEstimate(
2273
+ this.chainId,
2274
+ this.evmChainAdapter
2275
+ );
2276
+ approveTxParams.gasPrice = estimatedGasPrice.toString();
2277
+ }
2278
+ }
2279
+ logger.info("OKX swap: sending approve transaction", {
2280
+ to: normalizedTokenIn,
2281
+ tokenIn: normalizedTokenIn,
2282
+ dexContractAddress,
2283
+ hasData: !!approveResponse.data.data,
2284
+ gasLimit: estimatedGasLimit.toString(),
2285
+ okxGasLimit: approveResponse.data.gasLimit,
2286
+ hasReliableEstimate
2287
+ });
2288
+ const approveResult = await this.evmChainAdapter.sendTransaction(approveTxParams);
2289
+ if (approveResult.status !== "success") {
2290
+ logger.error("OKX swap: approve transaction failed", {
2291
+ status: approveResult.status,
2292
+ message: approveResult.message
2293
+ });
2294
+ return createExecuteError(
2295
+ approveResult.message || "Approve transaction failed"
2296
+ );
2297
+ }
2298
+ logger.info("OKX swap: approve transaction successful", {
2299
+ txHash: approveResult.txHash
2300
+ });
2301
+ let retryCount = 0;
2302
+ while (retryCount < MAX_APPROVAL_RETRIES) {
2303
+ await new Promise(
2304
+ (resolve) => setTimeout(resolve, APPROVAL_RETRY_DELAY_MS * (retryCount + 1))
2305
+ );
2306
+ const newAllowance = await this.evmChainAdapter.getAllowance({
2307
+ tokenAddress: normalizedTokenIn,
2308
+ owner: sender,
2309
+ spender: dexContractAddress
2310
+ });
2311
+ const newAllowanceBN = ethers.BigNumber.from(newAllowance);
2312
+ logger.info("OKX swap: checking approval status", {
2313
+ retryCount,
2314
+ allowance: newAllowanceBN.toString(),
2315
+ amountIn: amountInBN.toString(),
2316
+ hasEnoughAllowance: newAllowanceBN.gte(amountInBN)
2317
+ });
2318
+ if (newAllowanceBN.gte(amountInBN)) {
2319
+ logger.info("OKX swap: approval confirmed");
2320
+ break;
2321
+ }
2322
+ retryCount++;
2323
+ }
2324
+ const finalAllowance = await this.evmChainAdapter.getAllowance({
2325
+ tokenAddress: normalizedTokenIn,
2326
+ owner: sender,
2327
+ spender: dexContractAddress
2328
+ });
2329
+ const finalAllowanceBN = ethers.BigNumber.from(finalAllowance);
2330
+ if (finalAllowanceBN.lt(amountInBN)) {
2331
+ logger.error("OKX swap: approval still insufficient after retries", {
2332
+ allowance: finalAllowanceBN.toString(),
2333
+ amountIn: amountInBN.toString()
2334
+ });
2335
+ return createExecuteError("Token approval failed or insufficient");
2336
+ }
2337
+ } catch (error) {
2338
+ logger.error("OKX swap: approve transaction error", {
2339
+ error: error?.message
2340
+ });
2341
+ return createExecuteError(
2342
+ normalizeError(error?.message) || "Approve transaction failed"
2343
+ );
2344
+ }
2345
+ }
2346
+ } else {
2347
+ if (!this.evmChainAdapter.getBalance) {
2348
+ return createExecuteError("OKX swap failed: Balance check not supported");
2349
+ }
2350
+ const nativeBalanceFormatted = await this.evmChainAdapter.getBalance({
2351
+ address: sender,
2352
+ tokenAddress: void 0
2353
+ });
2354
+ const balanceBN = ethers.utils.parseEther(nativeBalanceFormatted || "0");
2355
+ const amountInBN = ethers.BigNumber.from(quote.amountIn);
2356
+ const gasPriceEstimate2 = await getGasPriceEstimate(
2357
+ this.chainId,
2358
+ this.evmChainAdapter
2359
+ );
2360
+ const [estimatedGasLimitForBalance] = await estimateGasLimit(
2361
+ gas,
2362
+ to,
2363
+ transactionData,
2364
+ value,
2365
+ sender,
2366
+ this.chainId,
2367
+ this.evmChainAdapter
2368
+ );
2369
+ const gasCostEstimate = estimatedGasLimitForBalance.mul(gasPriceEstimate2);
2370
+ const totalRequired = amountInBN.add(gasCostEstimate);
2371
+ if (balanceBN.lt(totalRequired)) {
2372
+ return createExecuteError("OKX swap failed: Insufficient balance (including gas fee)");
2373
+ }
2374
+ }
2375
+ if (normalizedTokenIn && normalizedTokenIn !== "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" && dexContractAddress) {
2376
+ const finalAllowanceCheck = await this.evmChainAdapter.getAllowance({
2377
+ tokenAddress: normalizedTokenIn,
2378
+ owner: sender,
2379
+ spender: dexContractAddress
2380
+ });
2381
+ const finalAllowanceBN = ethers.BigNumber.from(finalAllowanceCheck);
2382
+ const amountInBN = ethers.BigNumber.from(quote.amountIn);
2383
+ if (finalAllowanceBN.lt(amountInBN)) {
2384
+ logger.error("OKX swap: final allowance check failed", {
2385
+ allowance: finalAllowanceBN.toString(),
2386
+ amountIn: amountInBN.toString(),
2387
+ dexContractAddress
2388
+ });
2389
+ return createExecuteError("OKX swap failed: Insufficient token allowance");
2390
+ }
2391
+ }
2392
+ const [rpcEstimatedGasLimit, hasReliableRpcEstimate, rpcEstimateError] = await estimateGasLimit(
2393
+ void 0,
2394
+ to,
2395
+ transactionData,
2396
+ value,
2397
+ sender,
2398
+ this.chainId,
2399
+ this.evmChainAdapter
2400
+ );
2401
+ let finalGasLimit;
2402
+ const MAX_REASONABLE_GAS_LIMIT = ethers.BigNumber.from("500000");
2403
+ const okxGasBN = gas ? ethers.BigNumber.from(gas) : null;
2404
+ const okxEstimateGasFeeBN = okxEstimateGasFee ? ethers.BigNumber.from(okxEstimateGasFee) : null;
2405
+ if (rpcEstimateError) {
2406
+ const isUnpredictableGasLimit = rpcEstimateError.code === "UNPREDICTABLE_GAS_LIMIT" || rpcEstimateError.message && rpcEstimateError.message.includes("UNPREDICTABLE_GAS_LIMIT") || rpcEstimateError.reason && rpcEstimateError.reason.includes("execution reverted");
2407
+ if (isUnpredictableGasLimit) {
2408
+ logger.warn("Blocking transaction due to RPC gas estimation failure", {
2409
+ chainId: this.chainId,
2410
+ tokenIn: quote.tokenIn.symbol,
2411
+ tokenOut: quote.tokenOut.symbol,
2412
+ rpcError: rpcEstimateError
2413
+ });
2414
+ const errorMsg = rpcEstimateError?.message || rpcEstimateError?.reason || "";
2415
+ return createExecuteError(
2416
+ errorMsg || "OKX swap failed: Transaction would revert (slippage or price impact too high)"
2417
+ );
2418
+ }
2419
+ if (okxEstimateGasFeeBN && okxEstimateGasFeeBN.gt(0)) {
2420
+ finalGasLimit = okxEstimateGasFeeBN.gt(MAX_REASONABLE_GAS_LIMIT) ? MAX_REASONABLE_GAS_LIMIT : okxEstimateGasFeeBN;
2421
+ logger.warn("RPC gas estimation failed, using OKX estimateGasFee (capped)", {
2422
+ chainId: this.chainId,
2423
+ tokenIn: quote.tokenIn.symbol,
2424
+ tokenOut: quote.tokenOut.symbol,
2425
+ okxEstimateGasFee: okxEstimateGasFeeBN.toString(),
2426
+ finalGasLimit: finalGasLimit.toString(),
2427
+ rpcError: rpcEstimateError
2428
+ });
2429
+ } else if (okxGasBN && okxGasBN.gt(0)) {
2430
+ finalGasLimit = okxGasBN.gt(MAX_REASONABLE_GAS_LIMIT) ? MAX_REASONABLE_GAS_LIMIT : okxGasBN;
2431
+ logger.warn("RPC gas estimation failed, using OKX gas from swap (capped)", {
2432
+ chainId: this.chainId,
2433
+ tokenIn: quote.tokenIn.symbol,
2434
+ tokenOut: quote.tokenOut.symbol,
2435
+ okxGas: okxGasBN.toString(),
2436
+ finalGasLimit: finalGasLimit.toString(),
2437
+ rpcError: rpcEstimateError
2438
+ });
2439
+ } else {
2440
+ return createExecuteError("OKX swap failed: Unable to determine gas limit");
2441
+ }
2442
+ } else if (hasReliableRpcEstimate && rpcEstimatedGasLimit.gt(0)) {
2443
+ finalGasLimit = rpcEstimatedGasLimit;
2444
+ if (okxGasBN && okxGasBN.gt(rpcEstimatedGasLimit.mul(2))) {
2445
+ logger.warn("OKX gas estimate is much higher than RPC estimate, using RPC estimate", {
2446
+ chainId: this.chainId,
2447
+ tokenIn: quote.tokenIn.symbol,
2448
+ tokenOut: quote.tokenOut.symbol,
2449
+ rpcEstimate: rpcEstimatedGasLimit.toString(),
2450
+ okxGas: okxGasBN.toString(),
2451
+ finalGasLimit: finalGasLimit.toString()
2452
+ });
2453
+ } else {
2454
+ logger.info("Using RPC gas estimate for swap transaction", {
2455
+ chainId: this.chainId,
2456
+ tokenIn: quote.tokenIn.symbol,
2457
+ tokenOut: quote.tokenOut.symbol,
2458
+ rpcEstimate: rpcEstimatedGasLimit.toString(),
2459
+ okxGas: okxGasBN?.toString() || "not provided",
2460
+ finalGasLimit: finalGasLimit.toString()
2461
+ });
2462
+ }
2463
+ } else {
2464
+ if (okxEstimateGasFeeBN && okxEstimateGasFeeBN.gt(0)) {
2465
+ finalGasLimit = okxEstimateGasFeeBN.gt(MAX_REASONABLE_GAS_LIMIT) ? MAX_REASONABLE_GAS_LIMIT : okxEstimateGasFeeBN;
2466
+ } else if (okxGasBN && okxGasBN.gt(0)) {
2467
+ finalGasLimit = okxGasBN.gt(MAX_REASONABLE_GAS_LIMIT) ? MAX_REASONABLE_GAS_LIMIT : okxGasBN;
2468
+ } else if (rpcEstimatedGasLimit.gt(0)) {
2469
+ finalGasLimit = rpcEstimatedGasLimit;
2470
+ } else {
2471
+ return createExecuteError("OKX swap failed: Unable to determine gas limit");
2472
+ }
2473
+ logger.warn("Gas estimation unreliable, using fallback (capped)", {
2474
+ chainId: this.chainId,
2475
+ tokenIn: quote.tokenIn.symbol,
2476
+ tokenOut: quote.tokenOut.symbol,
2477
+ finalGasLimit: finalGasLimit.toString(),
2478
+ okxGas: okxGasBN?.toString() || "not provided",
2479
+ okxEstimateGasFee: okxEstimateGasFeeBN?.toString() || "not provided"
2480
+ });
2481
+ }
2482
+ const supportsEip1559 = isEip1559Chain(this.chainId);
2483
+ let gasPriceEstimate;
2484
+ const getCachedGasPrice = async () => {
2485
+ if (!gasPriceEstimate) {
2486
+ gasPriceEstimate = await getGasPriceEstimate(
2487
+ this.chainId,
2488
+ this.evmChainAdapter
2489
+ );
2490
+ }
2491
+ return gasPriceEstimate;
2492
+ };
2493
+ let feeData = {};
2494
+ if (supportsEip1559) {
2495
+ feeData = await getEip1559FeeData(this.chainId, this.evmChainAdapter);
2496
+ if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas || feeData.maxFeePerGas.lte(0) || feeData.maxPriorityFeePerGas.lte(0)) {
2497
+ const cachedGasPrice = await getCachedGasPrice();
2498
+ if (!cachedGasPrice || cachedGasPrice.lte(0)) {
2499
+ return createExecuteError("OKX swap failed: Unable to get valid gas price");
2500
+ }
2501
+ feeData.maxFeePerGas = cachedGasPrice;
2502
+ feeData.maxPriorityFeePerGas = cachedGasPrice.div(10);
2503
+ const minPriorityFee = ethers.utils.parseUnits("1", "gwei");
2504
+ if (feeData.maxPriorityFeePerGas.lt(minPriorityFee)) {
2505
+ feeData.maxPriorityFeePerGas = minPriorityFee;
2506
+ }
2507
+ }
2508
+ } else {
2509
+ try {
2510
+ const signer = await this.evmChainAdapter.getSigner?.();
2511
+ if (signer?.provider) {
2512
+ const providerFeeData = await signer.provider.getFeeData();
2513
+ if (providerFeeData.gasPrice && providerFeeData.gasPrice.gt(0)) {
2514
+ feeData.gasPrice = providerFeeData.gasPrice;
2515
+ } else {
2516
+ const cachedGasPrice = await getCachedGasPrice();
2517
+ if (!cachedGasPrice || cachedGasPrice.lte(0)) {
2518
+ return createExecuteError("Failed to get valid gas price. Please try again.");
2519
+ }
2520
+ feeData.gasPrice = cachedGasPrice;
2521
+ }
2522
+ } else {
2523
+ const cachedGasPrice = await getCachedGasPrice();
2524
+ if (!cachedGasPrice || cachedGasPrice.lte(0)) {
2525
+ return createExecuteError("Failed to get valid gas price. Please try again.");
2526
+ }
2527
+ feeData.gasPrice = cachedGasPrice;
2528
+ }
2529
+ } catch (error) {
2530
+ const cachedGasPrice = await getCachedGasPrice();
2531
+ if (!cachedGasPrice || cachedGasPrice.lte(0)) {
2532
+ return createExecuteError("OKX swap failed: Unable to get valid gas price");
2533
+ }
2534
+ feeData.gasPrice = cachedGasPrice;
2535
+ }
2536
+ }
2537
+ if (!to || !ethers.utils.isAddress(to)) {
2538
+ return createExecuteError("OKX swap failed: Invalid recipient address");
2539
+ }
2540
+ if (!transactionData || transactionData.length < 10) {
2541
+ return createExecuteError("OKX swap failed: Invalid transaction data");
2542
+ }
2543
+ if (!finalGasLimit || finalGasLimit.lte(0)) {
2544
+ return createExecuteError("OKX swap failed: Invalid gas limit");
2545
+ }
2546
+ if (supportsEip1559) {
2547
+ if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas || feeData.maxFeePerGas.lte(0) || feeData.maxPriorityFeePerGas.lte(0)) {
2548
+ return createExecuteError("OKX swap failed: Invalid gas fee data (EIP-1559)");
2549
+ }
2550
+ } else {
2551
+ if (!feeData.gasPrice || feeData.gasPrice.lte(0)) {
2552
+ return createExecuteError("OKX swap failed: Invalid gas price");
2553
+ }
2554
+ }
2555
+ const txParams = {
2556
+ to,
2557
+ data: transactionData,
2558
+ value: value || "0",
2559
+ gasLimit: finalGasLimit.toString()
2560
+ };
2561
+ if (supportsEip1559 && feeData.maxFeePerGas && feeData.maxPriorityFeePerGas) {
2562
+ txParams.type = 2;
2563
+ txParams.maxFeePerGas = feeData.maxFeePerGas.toString();
2564
+ txParams.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas.toString();
2565
+ } else if (feeData.gasPrice) {
2566
+ txParams.gasPrice = feeData.gasPrice.toString();
2567
+ }
2568
+ logger.info("OKX sending transaction to wallet", {
2569
+ to,
2570
+ hasData: !!transactionData,
2571
+ dataLength: transactionData?.length,
2572
+ value,
2573
+ gasLimit: finalGasLimit.toString(),
2574
+ supportsEip1559,
2575
+ feeData: supportsEip1559 ? {
2576
+ maxFeePerGas: feeData.maxFeePerGas?.toString(),
2577
+ maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.toString()
2578
+ } : { gasPrice: feeData.gasPrice?.toString() }
2579
+ });
2580
+ const result = await this.evmChainAdapter.sendTransaction(txParams);
2581
+ logger.info("OKX transaction result", {
2582
+ status: result.status,
2583
+ txHash: result.txHash,
2584
+ message: result.message
2585
+ });
2586
+ if (result.status === "success") {
2587
+ return {
2588
+ success: true,
2589
+ txHash: result.txHash,
2590
+ txHashArray: result.txHash ? [result.txHash] : []
2591
+ };
2592
+ } else {
2593
+ logger.error("OKX transaction failed", {
2594
+ status: result.status,
2595
+ txHash: result.txHash,
2596
+ message: result.message
2597
+ });
2598
+ return createExecuteError(
2599
+ normalizeError(result.message) || ErrorMessages.EXECUTE_FAILED
2600
+ );
2601
+ }
2602
+ } catch (error) {
2603
+ logger.error("OKX execute swap error:", error);
2604
+ return createExecuteError(
2605
+ normalizeError(error?.message) || ErrorMessages.EXECUTE_FAILED
2606
+ );
2607
+ }
2608
+ }
2609
+ normalizeEvmAddress(address) {
2610
+ if (!address) return address;
2611
+ try {
2612
+ return ethers.utils.getAddress(address);
2613
+ } catch (e) {
2614
+ const addr = address.startsWith("0x") ? address.slice(2) : address;
2615
+ return "0x" + addr.toLowerCase();
2616
+ }
2617
+ }
2618
+ };
2619
+ async function completeQuote(params, config) {
2620
+ const {
2621
+ sourceToken,
2622
+ targetToken,
2623
+ sourceChain,
2624
+ targetChain: _targetChain,
2625
+ // Reserved for future use
2626
+ amountIn,
2627
+ slippage,
2628
+ recipient,
2629
+ refundTo,
2630
+ customRecipientMsg,
2631
+ appFees,
2632
+ evmChainId
2633
+ } = params;
2634
+ const {
2635
+ intentsQuotationAdapter,
2636
+ dexRouters,
2637
+ dexRouter,
2638
+ bluechipTokens,
2639
+ configAdapter,
2640
+ currentUserAddress,
2641
+ isIntentsSupportedToken: customIsIntentsSupportedToken
2642
+ } = config;
2643
+ const wrapNearContractId = configAdapter.getWrapNearContractId();
2644
+ const routers = dexRouters || (dexRouter ? [dexRouter] : []);
2645
+ const userAddress = currentUserAddress || recipient;
2646
+ if (!userAddress) {
2647
+ throw new Error(ErrorMessages.QUOTE_FAILED);
2648
+ }
2649
+ if (sourceToken?.address === void 0) {
2650
+ throw new Error(ErrorMessages.QUOTE_FAILED);
2651
+ }
2652
+ if (targetToken?.address === void 0) {
2653
+ throw new Error(ErrorMessages.QUOTE_FAILED);
2654
+ }
2655
+ const isEvmChain = evmChainId !== void 0 || sourceToken.chain === "evm" || sourceChain === "evm";
2656
+ const isTokenIntentsSupported = isEvmChain && sourceToken.platform === "nearIntents" || (customIsIntentsSupportedToken ? customIsIntentsSupportedToken(sourceToken) : isEvmChain ? isEvmIntentsSupportedToken(sourceToken, bluechipTokens) : isNearIntentsSupportedToken(sourceToken, bluechipTokens));
2657
+ const bluechipToken = isEvmChain ? findBestEvmBluechipToken(
2658
+ bluechipTokens,
2659
+ configAdapter.getEvmNativeWrappedTokenAddress?.()
2660
+ ) : findBestBluechipToken(bluechipTokens, wrapNearContractId);
2661
+ if (!bluechipToken?.address) {
2662
+ throw new Error(ErrorMessages.QUOTE_FAILED);
2663
+ }
2664
+ async function quoteWithRetry(router, quoteParams, _routerType, _routerName, maxRetries = 2, initialDelay = 1e3) {
2665
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
2666
+ try {
2667
+ const quote = await router.quote(quoteParams);
2668
+ if (quote.success) {
2669
+ return quote;
2670
+ }
2671
+ const isRateLimit = quote.error?.includes("429") || quote.error?.toLowerCase().includes("rate limit") || quote.error?.toLowerCase().includes("too many requests");
2672
+ if (!isRateLimit || attempt === maxRetries) {
2673
+ return quote;
2674
+ }
2675
+ const baseDelay = isRateLimit ? 2e3 : initialDelay;
2676
+ const delay = baseDelay * Math.pow(2, attempt);
2677
+ await new Promise((resolve) => setTimeout(resolve, delay));
2678
+ } catch (error) {
2679
+ const errorMessage = error?.message || String(error);
2680
+ const isRateLimit = errorMessage?.includes("429") || errorMessage?.toLowerCase().includes("rate limit") || errorMessage?.toLowerCase().includes("too many requests");
2681
+ if (!isRateLimit || attempt === maxRetries) {
2682
+ throw error;
2683
+ }
2684
+ const baseDelay = isRateLimit ? 2e3 : initialDelay;
2685
+ const delay = baseDelay * Math.pow(2, attempt);
2686
+ await new Promise((resolve) => setTimeout(resolve, delay));
2687
+ }
2688
+ }
2689
+ return null;
2690
+ }
2691
+ const preSwapQuotePromises = routers.map(async (router, index) => {
2692
+ const supportedChain = router.getSupportedChain();
2693
+ const isEvmRouter = supportedChain === "evm";
2694
+ let routeType;
2695
+ if (isEvmRouter) {
2696
+ routeType = "evm";
2697
+ } else {
2698
+ routeType = index === 0 ? "v1" : "v2";
2699
+ }
2700
+ const capabilities = router.getCapabilities();
2701
+ const quoteParams = capabilities.requiresRecipient ? {
2702
+ tokenIn: sourceToken,
2703
+ tokenOut: bluechipToken,
2704
+ amountIn,
2705
+ slippage,
2706
+ swapType: "EXACT_INPUT",
2707
+ sender: userAddress,
2708
+ recipient: userAddress
2709
+ } : {
2710
+ tokenIn: sourceToken,
2711
+ tokenOut: bluechipToken,
2712
+ amountIn,
2713
+ slippage,
2714
+ swapType: "EXACT_INPUT"
2715
+ };
2716
+ const routerName = router.getSupportedChain() === "evm" ? `EVM-${router.getChainId?.() || "unknown"}` : "NEAR";
2717
+ const routerType = router.okxAdapter ? "OKX" : router.bitgetAdapter ? "Bitget" : routerName;
2718
+ try {
2719
+ const preSwapQuote = await quoteWithRetry(
2720
+ router,
2721
+ quoteParams,
2722
+ routerType,
2723
+ routerName,
2724
+ 2,
2725
+ 1e3
2726
+ );
2727
+ if (!preSwapQuote || !preSwapQuote.success) {
2728
+ return null;
2729
+ }
2730
+ return {
2731
+ type: routeType,
2732
+ router,
2733
+ preSwapQuote
2734
+ };
2735
+ } catch (error) {
2736
+ return null;
2737
+ }
2738
+ });
2739
+ const preSwapQuoteResults = await Promise.allSettled(preSwapQuotePromises);
2740
+ const validPreSwapQuotes = [];
2741
+ preSwapQuoteResults.forEach((result) => {
2742
+ if (result.status === "fulfilled" && result.value !== null) {
2743
+ validPreSwapQuotes.push(result.value);
2744
+ }
2745
+ });
2746
+ let bestPreSwapQuote = null;
2747
+ if (validPreSwapQuotes.length > 0) {
2748
+ bestPreSwapQuote = validPreSwapQuotes.reduce((best, current) => {
2749
+ const bestAmount = new Big3(best.preSwapQuote.amountOut);
2750
+ const currentAmount = new Big3(current.preSwapQuote.amountOut);
2751
+ return currentAmount.gt(bestAmount) ? current : best;
2752
+ });
2753
+ }
2754
+ const quotePaths = [];
2755
+ if (bestPreSwapQuote) {
2756
+ const { router, preSwapQuote, type: routeType } = bestPreSwapQuote;
2757
+ let normalizedSourceAsset;
2758
+ if (bluechipToken.assetId && (bluechipToken.assetId.startsWith("nep245:") || bluechipToken.assetId.startsWith("nep141:") || bluechipToken.assetId.startsWith("1cs_v1:"))) {
2759
+ normalizedSourceAsset = bluechipToken.assetId;
2760
+ } else if (isEvmChain) {
2761
+ const bluechipKey = bluechipToken.symbol?.toUpperCase();
2762
+ const bluechipTokenConfig = bluechipKey && bluechipTokens[bluechipKey] || void 0;
2763
+ normalizedSourceAsset = bluechipTokenConfig?.assetId ? bluechipTokenConfig.assetId : `evm:${normalizeEvmAddress(bluechipToken.address)}`;
2764
+ } else {
2765
+ const bluechipKey = bluechipToken.symbol?.toUpperCase() === "WNEAR" ? "NEAR" : bluechipToken.symbol?.toUpperCase();
2766
+ const bluechipTokenConfig = bluechipKey && bluechipTokens[bluechipKey] || void 0;
2767
+ normalizedSourceAsset = bluechipTokenConfig?.assetId ? bluechipTokenConfig.assetId : `nep141:${bluechipToken.address}`;
2768
+ }
2769
+ let normalizedTargetAsset;
2770
+ if (targetToken.assetId && (targetToken.assetId.startsWith("nep245:") || targetToken.assetId.startsWith("nep141:") || targetToken.assetId.startsWith("1cs_v1:"))) {
2771
+ normalizedTargetAsset = targetToken.assetId;
2772
+ } else {
2773
+ normalizedTargetAsset = targetToken.address;
2774
+ if (normalizedTargetAsset?.startsWith("1cs_v1:")) ; else if (normalizedTargetAsset && !normalizedTargetAsset.startsWith("nep141:") && !normalizedTargetAsset.startsWith("nep245:") && normalizedTargetAsset.includes(".")) {
2775
+ normalizedTargetAsset = `nep141:${normalizeTokenId(
2776
+ normalizedTargetAsset,
2777
+ wrapNearContractId
2778
+ )}`;
2779
+ }
2780
+ if (!normalizedTargetAsset?.startsWith("1cs_v1:")) {
2781
+ normalizedTargetAsset = normalizeDestinationAsset(normalizedTargetAsset, wrapNearContractId) || normalizedTargetAsset;
2782
+ }
2783
+ }
2784
+ const slippageBps = convertSlippageToBasisPoints(slippage);
2785
+ const formattedAmountOut = preSwapQuote.amountOut;
2786
+ quotePaths.push({
2787
+ type: routeType,
2788
+ router,
2789
+ promise: (async () => {
2790
+ try {
2791
+ const intentsQuote = await intentsQuotationAdapter.quote({
2792
+ originAsset: normalizedSourceAsset,
2793
+ destinationAsset: normalizedTargetAsset,
2794
+ amount: formattedAmountOut,
2795
+ refundTo: refundTo || recipient,
2796
+ recipient,
2797
+ slippageTolerance: slippageBps,
2798
+ swapType: "FLEX_INPUT",
2799
+ ...customRecipientMsg ? { customRecipientMsg } : {},
2800
+ ...appFees ? { appFees } : {}
2801
+ });
2802
+ if (intentsQuote.quoteStatus !== "success") {
2803
+ const formatError = config.formatErrorMessage || formatErrorMessage;
2804
+ const errorMessage = formatError({
2805
+ error: intentsQuote.messageOriginal || intentsQuote.message,
2806
+ originAsset: normalizedSourceAsset,
2807
+ fallbackMessage: ErrorMessages.QUOTE_FAILED
2808
+ });
2809
+ throw new Error(errorMessage);
2810
+ }
2811
+ return {
2812
+ intentsQuote,
2813
+ preSwapQuote,
2814
+ router,
2815
+ finalAmountOut: intentsQuote.quoteSuccessResult?.quote?.amountOut || "0"
2816
+ };
2817
+ } catch (error) {
2818
+ throw error;
2819
+ }
2820
+ })()
2821
+ });
2822
+ }
2823
+ if (isTokenIntentsSupported) {
2824
+ quotePaths.push({
2825
+ type: "intents",
2826
+ promise: (async () => {
2827
+ let normalizedSourceAsset;
2828
+ if (isEvmChain && sourceToken.platform === "nearIntents" && sourceToken.assetId) {
2829
+ normalizedSourceAsset = sourceToken.assetId;
2830
+ } else if (sourceToken.assetId && (sourceToken.assetId.startsWith("nep245:") || sourceToken.assetId.startsWith("nep141:") || sourceToken.assetId.startsWith("1cs_v1:"))) {
2831
+ normalizedSourceAsset = sourceToken.assetId;
2832
+ } else if (isEvmChain) {
2833
+ const sourceKey = sourceToken.symbol?.toUpperCase();
2834
+ const sourceTokenConfig = sourceKey ? bluechipTokens[sourceKey] : void 0;
2835
+ if (sourceTokenConfig?.assetId) {
2836
+ normalizedSourceAsset = sourceTokenConfig.assetId;
2837
+ } else if (sourceToken.address === "") {
2838
+ normalizedSourceAsset = `evm-${sourceChain}-native`;
2839
+ } else {
2840
+ normalizedSourceAsset = `evm:${normalizeEvmAddress(sourceToken.address)}`;
2841
+ }
2842
+ } else {
2843
+ const sourceKey = sourceToken.symbol?.toUpperCase();
2844
+ const sourceTokenConfig = sourceKey ? bluechipTokens[sourceKey] : void 0;
2845
+ if (sourceTokenConfig?.assetId) {
2846
+ normalizedSourceAsset = sourceTokenConfig.assetId;
2847
+ } else {
2848
+ normalizedSourceAsset = normalizeTokenId(sourceToken.address, wrapNearContractId);
2849
+ if (normalizedSourceAsset === "zec.omft.near" && sourceToken.chain === "near") {
2850
+ normalizedSourceAsset = "1cs_v1:near:nep141:zec.omft.near";
2851
+ } else if (!normalizedSourceAsset.startsWith("nep141:")) {
2852
+ normalizedSourceAsset = `nep141:${normalizedSourceAsset}`;
2853
+ }
2854
+ }
2855
+ }
2856
+ let normalizedTargetAsset;
2857
+ if (isEvmChain && targetToken.platform === "nearIntents" && targetToken.assetId) {
2858
+ normalizedTargetAsset = targetToken.assetId;
2859
+ } else if (targetToken.assetId && (targetToken.assetId.startsWith("nep245:") || targetToken.assetId.startsWith("nep141:") || targetToken.assetId.startsWith("1cs_v1:"))) {
2860
+ normalizedTargetAsset = targetToken.assetId;
2861
+ } else {
2862
+ normalizedTargetAsset = targetToken.address;
2863
+ if (normalizedTargetAsset?.startsWith("1cs_v1:")) ; else if (normalizedTargetAsset && !normalizedTargetAsset.startsWith("nep141:") && !normalizedTargetAsset.startsWith("nep245:") && normalizedTargetAsset.includes(".")) {
2864
+ normalizedTargetAsset = `nep141:${normalizeTokenId(
2865
+ normalizedTargetAsset,
2866
+ wrapNearContractId
2867
+ )}`;
2868
+ }
2869
+ if (!normalizedTargetAsset?.startsWith("1cs_v1:")) {
2870
+ normalizedTargetAsset = normalizeDestinationAsset(normalizedTargetAsset, wrapNearContractId) || normalizedTargetAsset;
2871
+ }
2872
+ }
2873
+ const slippageBps = convertSlippageToBasisPoints(slippage);
2874
+ const intentsQuote = await intentsQuotationAdapter.quote({
2875
+ originAsset: normalizedSourceAsset,
2876
+ destinationAsset: normalizedTargetAsset,
2877
+ amount: amountIn,
2878
+ refundTo: refundTo || recipient,
2879
+ recipient,
2880
+ slippageTolerance: slippageBps,
2881
+ swapType: "EXACT_INPUT",
2882
+ ...customRecipientMsg ? { customRecipientMsg } : {}
2883
+ });
2884
+ if (intentsQuote.quoteStatus !== "success") {
2885
+ const formatError = config.formatErrorMessage || formatErrorMessage;
2886
+ const errorMessage = formatError({
2887
+ error: intentsQuote.messageOriginal || intentsQuote.message,
2888
+ originAsset: normalizedSourceAsset,
2889
+ fallbackMessage: ErrorMessages.QUOTE_FAILED
2890
+ });
2891
+ throw new Error(errorMessage);
2892
+ }
2893
+ return {
2894
+ intentsQuote,
2895
+ finalAmountOut: intentsQuote.quoteSuccessResult?.quote?.amountOut || "0"
2896
+ };
2897
+ })()
2898
+ });
2899
+ }
2900
+ const pathResults = await Promise.allSettled(
2901
+ quotePaths.map((p) => p.promise)
2902
+ );
2903
+ const validPaths = [];
2904
+ const errorMessages = [];
2905
+ pathResults.forEach((result, index) => {
2906
+ const pathType = quotePaths[index].type;
2907
+ if (result.status === "fulfilled") {
2908
+ validPaths.push({
2909
+ type: pathType,
2910
+ ...result.value
2911
+ });
2912
+ } else if (result.status === "rejected") {
2913
+ const error = result.reason;
2914
+ if (error instanceof Error) {
2915
+ errorMessages.push(error.message);
2916
+ } else if (typeof error === "string") {
2917
+ errorMessages.push(error);
2918
+ } else {
2919
+ errorMessages.push(String(error));
2920
+ }
2921
+ }
2922
+ });
2923
+ if (validPaths.length === 0) {
2924
+ const intentsError = errorMessages.find(
2925
+ (msg) => msg && msg !== ErrorMessages.QUOTE_FAILED && (msg.toLowerCase().includes("bridge") || msg.toLowerCase().includes("amount") || msg.toLowerCase().includes("low") || msg.toLowerCase().includes("minimum"))
2926
+ );
2927
+ const bestError = intentsError || errorMessages[0] || ErrorMessages.QUOTE_FAILED;
2928
+ const formatError = config.formatErrorMessage || formatErrorMessage;
2929
+ const formattedError = formatError({
2930
+ error: bestError,
2931
+ fallbackMessage: ErrorMessages.QUOTE_FAILED
2932
+ });
2933
+ throw new Error(formattedError);
2021
2934
  }
2022
2935
  const bestPath = validPaths.reduce((best, current) => {
2023
2936
  const bestAmount = new Big3(best.finalAmountOut);
@@ -2077,28 +2990,32 @@ async function quoteSameChainSwap(params, dexRouters) {
2077
2990
  return router.quote(quoteParams);
2078
2991
  })
2079
2992
  );
2080
- const validQuotes = quoteResults.filter(
2081
- (r) => r.status === "fulfilled" && r.value.success
2993
+ const fulfilledResults = quoteResults.filter(
2994
+ (r) => r.status === "fulfilled"
2082
2995
  ).map((r, index) => ({
2083
- quote: r.value,
2084
- router: dexRouters[index]
2996
+ result: r.value,
2997
+ router: dexRouters[index],
2998
+ index
2085
2999
  }));
2086
- if (validQuotes.length === 0) {
2087
- const errors = quoteResults.map((r, index) => {
2088
- if (r.status === "rejected") {
2089
- return `Router ${index}: ${r.reason}`;
2090
- }
2091
- if (r.status === "fulfilled" && !r.value.success) {
2092
- return `Router ${index}: ${r.value.error}`;
2093
- }
2094
- return null;
2095
- }).filter(Boolean);
2096
- const errorMessage = errors.length > 0 ? `${ErrorMessages.QUOTE_FAILED}: ${errors.join("; ")}` : ErrorMessages.QUOTE_FAILED;
2097
- throw new Error(errorMessage);
2098
- }
2099
- return selectBestQuote(validQuotes);
3000
+ const validQuotes = fulfilledResults.filter((r) => r.result.success).map((r) => ({
3001
+ quote: r.result,
3002
+ router: r.router
3003
+ }));
3004
+ if (validQuotes.length > 0) {
3005
+ return selectBestQuote(validQuotes);
3006
+ }
3007
+ const errors = fulfilledResults.filter((r) => !r.result.success).map((r) => {
3008
+ const error = r.result.error || "";
3009
+ const is429 = error.includes("429") || error.toLowerCase().includes("rate limit") || error.toLowerCase().includes("too many requests");
3010
+ return is429 ? null : `Router ${r.index}: ${error}`;
3011
+ }).filter(Boolean);
3012
+ if (errors.length === 0) {
3013
+ throw new Error("All liquidity providers are busy. Please try again later.");
3014
+ }
3015
+ const errorMessage = errors.length > 0 ? `${ErrorMessages.QUOTE_FAILED}: ${errors.join("; ")}` : ErrorMessages.QUOTE_FAILED;
3016
+ throw new Error(errorMessage);
2100
3017
  }
2101
3018
 
2102
- export { AggregateDexRouter, BitgetRouter, ErrorMessages, NearSmartRouter, TRANSACTION_EXECUTION_ERROR_MESSAGE, completeQuote, convertSlippageToBasisPoints, findBestBluechipToken, findBestEvmBluechipToken, formatGasString, formatGasToTgas, getBluechipTokensConfig, getErrorMessage, isEvmIntentsSupportedToken, isNearIntentsSupportedToken, logger, normalizeDestinationAsset, normalizeError, normalizeEvmAddress, normalizeTokenId, processErrorMessage, quoteSameChainSwap, requiresRecipient, requiresRecipientInExecute, selectBestQuote, setBluechipTokensConfig };
3019
+ export { AggregateDexRouter, BitgetRouter, ErrorMessages, NearSmartRouter, OkxRouter, TRANSACTION_EXECUTION_ERROR_MESSAGE, completeQuote, convertSlippageToBasisPoints, findBestBluechipToken, findBestEvmBluechipToken, formatErrorMessage, formatGasString, formatGasToTgas, getBluechipTokensConfig, getErrorMessage, isEvmIntentsSupportedToken, isNearIntentsSupportedToken, logger, normalizeDestinationAsset, normalizeError, normalizeErrorForIntents, normalizeEvmAddress, normalizeTokenId, processErrorMessage, quoteSameChainSwap, requiresRecipient, requiresRecipientInExecute, selectBestQuote, setBluechipTokensConfig };
2103
3020
  //# sourceMappingURL=index.mjs.map
2104
3021
  //# sourceMappingURL=index.mjs.map