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

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