@rhea-finance/cross-chain-aggregation-dex 0.2.0 → 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.mjs CHANGED
@@ -10,31 +10,12 @@ function requiresRecipientInExecute(params) {
10
10
  }
11
11
 
12
12
  // src/utils/errorMessages.ts
13
+ var TRANSACTION_EXECUTION_ERROR_MESSAGE = "Transaction execution error occurred. Please contact support";
13
14
  var ErrorMessages = {
14
- // Parameter validation errors
15
- MISSING_PARAMS: "Required parameters missing",
16
- MISSING_TOKEN_ADDRESS: "Token address required",
17
- INVALID_TOKEN_ADDRESS: "Invalid token address",
18
- MISSING_USER_ADDRESS: "User address required",
19
- INVALID_USER_ADDRESS: "Invalid user address",
20
15
  // Quote errors
21
16
  QUOTE_FAILED: "Failed to get quote",
22
- QUOTE_INVALID: "Invalid quote",
23
- QUOTE_NO_ROUTE: "No route found",
24
- QUOTE_EXPIRED: "Quote expired, please refresh",
25
17
  // Execution errors
26
- EXECUTE_FAILED: "Transaction failed",
27
- EXECUTE_INVALID_QUOTE: "Invalid quote, please refresh",
28
- EXECUTE_INSUFFICIENT_BALANCE: "Insufficient balance",
29
- EXECUTE_INSUFFICIENT_LIQUIDITY: "Insufficient liquidity",
30
- EXECUTE_SLIPPAGE_TOO_HIGH: "Price changed, please refresh",
31
- // Network/API errors
32
- NETWORK_ERROR: "Network error, please try again",
33
- API_ERROR: "Service temporarily unavailable",
34
- // Gas/Transaction errors
35
- GAS_ESTIMATE_FAILED: "Unable to estimate transaction fee",
36
- GAS_PRICE_FAILED: "Failed to get transaction fee",
37
- TRANSACTION_REVERTED: "Transaction would fail, please refresh"
18
+ EXECUTE_FAILED: "Transaction failed"
38
19
  };
39
20
  function getErrorMessage(error, fallback = ErrorMessages.EXECUTE_FAILED) {
40
21
  if (!error) return fallback;
@@ -43,41 +24,70 @@ function getErrorMessage(error, fallback = ErrorMessages.EXECUTE_FAILED) {
43
24
  }
44
25
  return fallback;
45
26
  }
46
- function normalizeError(error) {
47
- if (!error) return ErrorMessages.EXECUTE_FAILED;
48
- const errorLower = error.toLowerCase();
49
- if (errorLower.includes("missing") || errorLower.includes("required")) {
50
- if (errorLower.includes("token")) return ErrorMessages.MISSING_TOKEN_ADDRESS;
51
- if (errorLower.includes("address") || errorLower.includes("user") || errorLower.includes("sender") || errorLower.includes("recipient")) {
52
- return ErrorMessages.MISSING_USER_ADDRESS;
53
- }
54
- return ErrorMessages.MISSING_PARAMS;
55
- }
56
- if (errorLower.includes("invalid")) {
57
- if (errorLower.includes("token")) return ErrorMessages.INVALID_TOKEN_ADDRESS;
58
- if (errorLower.includes("address") || errorLower.includes("user")) {
59
- return ErrorMessages.INVALID_USER_ADDRESS;
60
- }
61
- if (errorLower.includes("quote")) return ErrorMessages.QUOTE_INVALID;
27
+ function processErrorMessage(errorMessage) {
28
+ const errorString = errorMessage.toLowerCase();
29
+ const userActionKeywords = [
30
+ "user",
31
+ "rejected",
32
+ "cancelled",
33
+ "cancel",
34
+ "denied",
35
+ "undefined",
36
+ "null"
37
+ ];
38
+ const containsUserActionKeyword = userActionKeywords.some(
39
+ (keyword) => errorString.includes(keyword)
40
+ );
41
+ if (containsUserActionKeyword) {
42
+ return errorMessage;
62
43
  }
63
- if (errorLower.includes("quote") && (errorLower.includes("fail") || errorLower.includes("error"))) {
44
+ return TRANSACTION_EXECUTION_ERROR_MESSAGE;
45
+ }
46
+ function normalizeErrorForIntents(error) {
47
+ if (!error) return ErrorMessages.QUOTE_FAILED;
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("gas") || errorLower.includes("network")) {
64
50
  return ErrorMessages.QUOTE_FAILED;
65
51
  }
66
- if (errorLower.includes("route") || errorLower.includes("no path")) {
67
- return ErrorMessages.QUOTE_NO_ROUTE;
68
- }
69
- if (errorLower.includes("balance") && errorLower.includes("insufficient")) {
70
- return ErrorMessages.EXECUTE_INSUFFICIENT_BALANCE;
71
- }
72
- if (errorLower.includes("liquidity") || errorLower.includes("slippage")) {
73
- return ErrorMessages.EXECUTE_SLIPPAGE_TOO_HIGH;
74
- }
75
- if (errorLower.includes("gas")) {
76
- if (errorLower.includes("price")) return ErrorMessages.GAS_PRICE_FAILED;
77
- return ErrorMessages.GAS_ESTIMATE_FAILED;
78
- }
52
+ return processErrorMessage(error);
53
+ }
54
+ function normalizeError(error) {
55
+ if (!error) return ErrorMessages.EXECUTE_FAILED;
79
56
  return error;
80
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
+ }
81
91
 
82
92
  // src/utils/logger.ts
83
93
  var LOG_LEVELS = {
@@ -400,7 +410,7 @@ var NearSmartRouter = class {
400
410
  amountOut: "0",
401
411
  minAmountOut: "0",
402
412
  routes: [],
403
- error: ErrorMessages.MISSING_TOKEN_ADDRESS
413
+ error: ErrorMessages.QUOTE_FAILED
404
414
  };
405
415
  }
406
416
  const normalizedTokenIn = normalizeTokenId(
@@ -420,7 +430,7 @@ var NearSmartRouter = class {
420
430
  amountOut: "0",
421
431
  minAmountOut: "0",
422
432
  routes: [],
423
- error: ErrorMessages.INVALID_TOKEN_ADDRESS
433
+ error: ErrorMessages.QUOTE_FAILED
424
434
  };
425
435
  }
426
436
  const slippageBps = convertSlippageToBasisPoints(slippage);
@@ -441,7 +451,7 @@ var NearSmartRouter = class {
441
451
  amountOut: "0",
442
452
  minAmountOut: "0",
443
453
  routes: [],
444
- error: normalizeError(response?.result_msg || response?.result_message) || ErrorMessages.QUOTE_NO_ROUTE
454
+ error: response?.result_msg || response?.result_message || ErrorMessages.QUOTE_FAILED
445
455
  };
446
456
  }
447
457
  const { routes: serverRoutes, amount_out } = response.result_data;
@@ -480,7 +490,7 @@ var NearSmartRouter = class {
480
490
  amountOut: "0",
481
491
  minAmountOut: "0",
482
492
  routes: [],
483
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
493
+ error: error?.message || ErrorMessages.QUOTE_FAILED
484
494
  };
485
495
  }
486
496
  }
@@ -493,7 +503,7 @@ var NearSmartRouter = class {
493
503
  if (!quote.success || !quote.routes.length) {
494
504
  return {
495
505
  success: false,
496
- error: ErrorMessages.EXECUTE_INVALID_QUOTE
506
+ error: ErrorMessages.QUOTE_FAILED
497
507
  };
498
508
  }
499
509
  const swapActions = [];
@@ -512,7 +522,7 @@ var NearSmartRouter = class {
512
522
  if (!swapActions.length) {
513
523
  return {
514
524
  success: false,
515
- error: ErrorMessages.QUOTE_INVALID
525
+ error: ErrorMessages.QUOTE_FAILED
516
526
  };
517
527
  }
518
528
  const finalRecipient = depositAddress || recipient;
@@ -542,7 +552,6 @@ var NearSmartRouter = class {
542
552
  },
543
553
  gas: "50000000000000",
544
554
  expandDeposit: "1250000000000000000000"
545
- // 0.00125 NEAR
546
555
  });
547
556
  }
548
557
  transactions.push({
@@ -598,7 +607,6 @@ var NearSmartRouter = class {
598
607
  msg: JSON.stringify(swapMsg)
599
608
  },
600
609
  gas: "250",
601
- // NEP-141 requires attaching 1 yoctoNEAR for certain calls.
602
610
  expandDeposit: "1"
603
611
  });
604
612
  const result = await this.nearChainAdapter.call({
@@ -681,7 +689,7 @@ var AggregateDexRouter = class {
681
689
  amountOut: "0",
682
690
  minAmountOut: "0",
683
691
  routes: [],
684
- error: ErrorMessages.MISSING_USER_ADDRESS
692
+ error: ErrorMessages.QUOTE_FAILED
685
693
  };
686
694
  }
687
695
  const { tokenIn, tokenOut, amountIn, slippage, sender, recipient } = params;
@@ -694,7 +702,7 @@ var AggregateDexRouter = class {
694
702
  amountOut: "0",
695
703
  minAmountOut: "0",
696
704
  routes: [],
697
- error: ErrorMessages.MISSING_USER_ADDRESS
705
+ error: ErrorMessages.QUOTE_FAILED
698
706
  };
699
707
  }
700
708
  if (!tokenIn?.address || !tokenOut?.address) {
@@ -706,7 +714,7 @@ var AggregateDexRouter = class {
706
714
  amountOut: "0",
707
715
  minAmountOut: "0",
708
716
  routes: [],
709
- error: ErrorMessages.MISSING_TOKEN_ADDRESS
717
+ error: ErrorMessages.QUOTE_FAILED
710
718
  };
711
719
  }
712
720
  const normalizedTokenIn = normalizeTokenId(
@@ -726,7 +734,7 @@ var AggregateDexRouter = class {
726
734
  amountOut: "0",
727
735
  minAmountOut: "0",
728
736
  routes: [],
729
- error: ErrorMessages.INVALID_TOKEN_ADDRESS
737
+ error: ErrorMessages.QUOTE_FAILED
730
738
  };
731
739
  }
732
740
  const slippageBps = convertSlippageToBasisPoints(slippage);
@@ -785,7 +793,7 @@ var AggregateDexRouter = class {
785
793
  amountOut: "0",
786
794
  minAmountOut: "0",
787
795
  routes: [],
788
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
796
+ error: error?.message || ErrorMessages.QUOTE_FAILED
789
797
  };
790
798
  }
791
799
  }
@@ -794,7 +802,7 @@ var AggregateDexRouter = class {
794
802
  */
795
803
  async finalizeQuote(params, depositAddress) {
796
804
  if (!requiresRecipient(params)) {
797
- throw new Error(ErrorMessages.MISSING_USER_ADDRESS);
805
+ throw new Error(ErrorMessages.QUOTE_FAILED);
798
806
  }
799
807
  return await this.quote({
800
808
  ...params,
@@ -866,26 +874,26 @@ var AggregateDexRouter = class {
866
874
  if (!requiresRecipientInExecute(params)) {
867
875
  return {
868
876
  success: false,
869
- error: ErrorMessages.MISSING_USER_ADDRESS
877
+ error: ErrorMessages.QUOTE_FAILED
870
878
  };
871
879
  }
872
880
  const { quote, sender, receiveUser } = params;
873
881
  if (!quote.success) {
874
882
  return {
875
883
  success: false,
876
- error: ErrorMessages.EXECUTE_INVALID_QUOTE
884
+ error: ErrorMessages.QUOTE_FAILED
877
885
  };
878
886
  }
879
887
  if (!receiveUser || receiveUser.trim() === "") {
880
888
  return {
881
889
  success: false,
882
- error: ErrorMessages.MISSING_USER_ADDRESS
890
+ error: ErrorMessages.QUOTE_FAILED
883
891
  };
884
892
  }
885
893
  if (receiveUser.startsWith("0x") && receiveUser.length === 42) {
886
894
  return {
887
895
  success: false,
888
- error: ErrorMessages.INVALID_USER_ADDRESS
896
+ error: ErrorMessages.QUOTE_FAILED
889
897
  };
890
898
  }
891
899
  const slippage = quote.slippage || 5e-3;
@@ -917,7 +925,7 @@ var AggregateDexRouter = class {
917
925
  } catch (error) {
918
926
  return {
919
927
  success: false,
920
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
928
+ error: error?.message || ErrorMessages.QUOTE_FAILED
921
929
  };
922
930
  }
923
931
  const routerMsg = finalQuote.routerMsg;
@@ -1120,7 +1128,8 @@ var AggregateDexRouter = class {
1120
1128
  } catch (error) {
1121
1129
  return {
1122
1130
  success: false,
1123
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
1131
+ // 预交易阶段:保留原始错误信息,不简化
1132
+ error: error?.message || ErrorMessages.QUOTE_FAILED
1124
1133
  };
1125
1134
  }
1126
1135
  const finalAmountToTransfer = finalQuoteForExecution.amountIn;
@@ -1288,17 +1297,9 @@ async function estimateGasLimit(gas, to, transactionData, value, sender, chainId
1288
1297
  reason: estimateError?.reason,
1289
1298
  message: estimateError?.message || String(estimateError)
1290
1299
  };
1291
- console.warn("RPC gas estimate failed:", {
1292
- error: estimateError?.message || String(estimateError),
1293
- code: estimateError?.code,
1294
- reason: estimateError?.reason
1295
- });
1296
1300
  }
1297
1301
  }
1298
1302
  } catch (error) {
1299
- console.warn("Gas estimation error:", {
1300
- error: error?.message || String(error)
1301
- });
1302
1303
  }
1303
1304
  if (estimatedGasLimit && estimatedGasLimit.gt(0) && hasReliableEstimate) {
1304
1305
  return [estimatedGasLimit, true, rpcEstimateError];
@@ -1384,7 +1385,7 @@ var BitgetRouter = class {
1384
1385
  amountOut: "0",
1385
1386
  minAmountOut: "0",
1386
1387
  routes: [],
1387
- error: ErrorMessages.MISSING_USER_ADDRESS
1388
+ error: "Bitget quote failed: Router requires recipient address"
1388
1389
  };
1389
1390
  }
1390
1391
  const { tokenIn, tokenOut, amountIn, slippage, sender, recipient } = params;
@@ -1397,7 +1398,7 @@ var BitgetRouter = class {
1397
1398
  amountOut: "0",
1398
1399
  minAmountOut: "0",
1399
1400
  routes: [],
1400
- error: ErrorMessages.MISSING_USER_ADDRESS
1401
+ error: "Bitget quote failed: Sender and recipient addresses are required"
1401
1402
  };
1402
1403
  }
1403
1404
  if (tokenIn?.address === void 0 || tokenOut?.address === void 0) {
@@ -1409,7 +1410,7 @@ var BitgetRouter = class {
1409
1410
  amountOut: "0",
1410
1411
  minAmountOut: "0",
1411
1412
  routes: [],
1412
- error: ErrorMessages.MISSING_TOKEN_ADDRESS
1413
+ error: "Bitget quote failed: Token addresses are required"
1413
1414
  };
1414
1415
  }
1415
1416
  const normalizedTokenIn = tokenIn.address === "" ? "" : this.normalizeEvmAddress(tokenIn.address);
@@ -1431,7 +1432,7 @@ var BitgetRouter = class {
1431
1432
  if (!isBitgetResponseSuccess(response) || !response.data) {
1432
1433
  return createQuoteError(
1433
1434
  params,
1434
- normalizeError(response.msg) || ErrorMessages.QUOTE_FAILED
1435
+ response.msg || "Bitget API error: Failed to get quote"
1435
1436
  );
1436
1437
  }
1437
1438
  const {
@@ -1497,21 +1498,23 @@ var BitgetRouter = class {
1497
1498
  } catch (error) {
1498
1499
  return createQuoteError(
1499
1500
  params,
1500
- normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
1501
+ error?.message || "Bitget quote failed: Unknown error"
1501
1502
  );
1502
1503
  }
1503
1504
  }
1504
1505
  async executeSwap(params) {
1505
1506
  try {
1506
1507
  if (!requiresRecipientInExecute(params)) {
1507
- return createExecuteError(ErrorMessages.MISSING_USER_ADDRESS);
1508
+ return createExecuteError("Bitget swap failed: Router requires recipient address");
1508
1509
  }
1509
1510
  const { quote, sender, receiveUser } = params;
1510
1511
  if (!quote.success) {
1511
- return createExecuteError(ErrorMessages.EXECUTE_INVALID_QUOTE);
1512
+ return createExecuteError(
1513
+ quote.error || "Bitget swap failed: Invalid quote"
1514
+ );
1512
1515
  }
1513
1516
  if (!receiveUser || receiveUser.trim() === "") {
1514
- return createExecuteError(ErrorMessages.MISSING_USER_ADDRESS);
1517
+ return createExecuteError("Bitget swap failed: Recipient address is required");
1515
1518
  }
1516
1519
  let market;
1517
1520
  try {
@@ -1519,13 +1522,13 @@ var BitgetRouter = class {
1519
1522
  const routerMsg = JSON.parse(quote.routerMsg);
1520
1523
  market = routerMsg.market || "";
1521
1524
  } else {
1522
- return createExecuteError(ErrorMessages.QUOTE_EXPIRED);
1525
+ return createExecuteError("Bitget swap failed: Missing router message data");
1523
1526
  }
1524
1527
  } catch (error) {
1525
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1528
+ return createExecuteError("Bitget swap failed: Invalid router message format");
1526
1529
  }
1527
1530
  if (!market) {
1528
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1531
+ return createExecuteError("Bitget swap failed: Market information is required");
1529
1532
  }
1530
1533
  const normalizedTokenIn = this.normalizeEvmAddress(
1531
1534
  quote.tokenIn.address
@@ -1548,9 +1551,8 @@ var BitgetRouter = class {
1548
1551
  tokenOutDecimals: quote.tokenOut.decimals
1549
1552
  });
1550
1553
  if (!isBitgetResponseSuccess(reQuoteResponse) || !reQuoteResponse.data) {
1551
- return createExecuteError(
1552
- normalizeError(reQuoteResponse.msg) || ErrorMessages.QUOTE_EXPIRED
1553
- );
1554
+ const errorMsg = reQuoteResponse.msg || "Bitget re-quote failed: No data returned";
1555
+ return createExecuteError(errorMsg);
1554
1556
  }
1555
1557
  market = reQuoteResponse.data?.market || market;
1556
1558
  const swapResponse = await this.bitgetAdapter.swap({
@@ -1569,11 +1571,13 @@ var BitgetRouter = class {
1569
1571
  });
1570
1572
  if (!isBitgetResponseSuccess(swapResponse) || !swapResponse.data) {
1571
1573
  return createExecuteError(
1572
- normalizeError(swapResponse.msg) || ErrorMessages.QUOTE_FAILED
1574
+ swapResponse.msg || "Bitget swap failed: No transaction data"
1573
1575
  );
1574
1576
  }
1575
1577
  if (swapResponse.data?.estimateRevert === true) {
1576
- return createExecuteError(ErrorMessages.EXECUTE_SLIPPAGE_TOO_HIGH);
1578
+ return createExecuteError(
1579
+ swapResponse.msg || "Bitget swap failed: Transaction would revert (slippage or price impact too high)"
1580
+ );
1577
1581
  }
1578
1582
  let transactionData = swapResponse.data?.calldata || swapResponse.data?.data;
1579
1583
  const to = swapResponse.data?.contract || swapResponse.data?.to;
@@ -1587,14 +1591,18 @@ var BitgetRouter = class {
1587
1591
  }
1588
1592
  const gas = swapResponse.data?.gas || (swapResponse.data?.computeUnits !== void 0 ? String(swapResponse.data.computeUnits) : void 0);
1589
1593
  if (!to || !transactionData) {
1590
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1594
+ return createExecuteError(
1595
+ swapResponse.msg || "Bitget swap failed: Missing transaction data or recipient address"
1596
+ );
1591
1597
  }
1592
1598
  if (transactionData.length < 10 || !/^0x[0-9a-fA-F]+$/.test(transactionData)) {
1593
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1599
+ return createExecuteError(
1600
+ swapResponse.msg || "Bitget swap failed: Invalid transaction data format"
1601
+ );
1594
1602
  }
1595
1603
  if (normalizedTokenIn && normalizedTokenIn !== "") {
1596
1604
  if (!this.evmChainAdapter.getBalance) {
1597
- return createExecuteError(ErrorMessages.NETWORK_ERROR);
1605
+ return createExecuteError("Bitget swap failed: Balance check not supported");
1598
1606
  }
1599
1607
  const tokenBalanceFormatted = await this.evmChainAdapter.getBalance({
1600
1608
  address: sender,
@@ -1604,7 +1612,7 @@ var BitgetRouter = class {
1604
1612
  const balanceBN = ethers.utils.parseUnits(tokenBalanceFormatted || "0", tokenDecimals);
1605
1613
  const amountInBN = ethers.BigNumber.from(quote.amountIn);
1606
1614
  if (balanceBN.lt(amountInBN)) {
1607
- return createExecuteError(ErrorMessages.EXECUTE_INSUFFICIENT_BALANCE);
1615
+ return createExecuteError("Bitget swap failed: Insufficient token balance");
1608
1616
  }
1609
1617
  const currentAllowance = await this.evmChainAdapter.getAllowance({
1610
1618
  tokenAddress: normalizedTokenIn,
@@ -1637,17 +1645,17 @@ var BitgetRouter = class {
1637
1645
  retryCount++;
1638
1646
  }
1639
1647
  if (newAllowanceBN.lt(amountInBN)) {
1640
- return createExecuteError(ErrorMessages.NETWORK_ERROR);
1648
+ return createExecuteError("Bitget swap failed: Token approval failed or insufficient");
1641
1649
  }
1642
1650
  } catch (error) {
1643
1651
  return createExecuteError(
1644
- normalizeError(error?.message) || ErrorMessages.NETWORK_ERROR
1652
+ error?.message || "Bitget approval check failed"
1645
1653
  );
1646
1654
  }
1647
1655
  }
1648
1656
  } else {
1649
1657
  if (!this.evmChainAdapter.getBalance) {
1650
- return createExecuteError(ErrorMessages.NETWORK_ERROR);
1658
+ return createExecuteError("Bitget swap failed: Balance check not supported");
1651
1659
  }
1652
1660
  const nativeBalanceFormatted = await this.evmChainAdapter.getBalance({
1653
1661
  address: sender,
@@ -1671,7 +1679,7 @@ var BitgetRouter = class {
1671
1679
  const gasCostEstimate = estimatedGasLimitForBalance.mul(gasPriceEstimate2);
1672
1680
  const totalRequired = amountInBN.add(gasCostEstimate);
1673
1681
  if (balanceBN.lt(totalRequired)) {
1674
- return createExecuteError(ErrorMessages.EXECUTE_INSUFFICIENT_BALANCE);
1682
+ return createExecuteError("Bitget swap failed: Insufficient balance (including gas fee)");
1675
1683
  }
1676
1684
  }
1677
1685
  if (normalizedTokenIn && normalizedTokenIn !== "") {
@@ -1683,7 +1691,7 @@ var BitgetRouter = class {
1683
1691
  const finalAllowanceBN = ethers.BigNumber.from(finalAllowanceCheck);
1684
1692
  const amountInBN = ethers.BigNumber.from(quote.amountIn);
1685
1693
  if (finalAllowanceBN.lt(amountInBN)) {
1686
- return createExecuteError(ErrorMessages.NETWORK_ERROR);
1694
+ return createExecuteError("Bitget swap failed: Insufficient token allowance");
1687
1695
  }
1688
1696
  }
1689
1697
  const [estimatedGasLimit, hasReliableEstimate, rpcEstimateError] = await estimateGasLimit(
@@ -1704,10 +1712,13 @@ var BitgetRouter = class {
1704
1712
  tokenOut: quote.tokenOut.symbol,
1705
1713
  rpcError: rpcEstimateError
1706
1714
  });
1707
- return createExecuteError(ErrorMessages.EXECUTE_INSUFFICIENT_LIQUIDITY);
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
+ );
1708
1719
  }
1709
1720
  if (!gas || gas === "0") {
1710
- return createExecuteError(ErrorMessages.GAS_ESTIMATE_FAILED);
1721
+ return createExecuteError("Bitget swap failed: Invalid gas estimate from Bitget");
1711
1722
  }
1712
1723
  logger.warn("RPC gas estimation failed, using Bitget estimate", {
1713
1724
  chainId: this.chainId,
@@ -1740,7 +1751,7 @@ var BitgetRouter = class {
1740
1751
  if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas || feeData.maxFeePerGas.lte(0) || feeData.maxPriorityFeePerGas.lte(0)) {
1741
1752
  const cachedGasPrice = await getCachedGasPrice();
1742
1753
  if (!cachedGasPrice || cachedGasPrice.lte(0)) {
1743
- return createExecuteError(ErrorMessages.GAS_PRICE_FAILED);
1754
+ return createExecuteError("Bitget swap failed: Unable to get valid gas price");
1744
1755
  }
1745
1756
  feeData.maxFeePerGas = cachedGasPrice;
1746
1757
  feeData.maxPriorityFeePerGas = cachedGasPrice.div(10);
@@ -1773,27 +1784,27 @@ var BitgetRouter = class {
1773
1784
  } catch (error) {
1774
1785
  const cachedGasPrice = await getCachedGasPrice();
1775
1786
  if (!cachedGasPrice || cachedGasPrice.lte(0)) {
1776
- return createExecuteError(ErrorMessages.GAS_PRICE_FAILED);
1787
+ return createExecuteError("Bitget swap failed: Unable to get valid gas price");
1777
1788
  }
1778
1789
  feeData.gasPrice = cachedGasPrice;
1779
1790
  }
1780
1791
  }
1781
1792
  if (!to || !ethers.utils.isAddress(to)) {
1782
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1793
+ return createExecuteError("Bitget swap failed: Invalid recipient address");
1783
1794
  }
1784
1795
  if (!transactionData || transactionData.length < 10) {
1785
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1796
+ return createExecuteError("Bitget swap failed: Invalid transaction data");
1786
1797
  }
1787
1798
  if (!estimatedGasLimit || estimatedGasLimit.lte(0)) {
1788
- return createExecuteError(ErrorMessages.GAS_ESTIMATE_FAILED);
1799
+ return createExecuteError("Bitget swap failed: Invalid gas limit");
1789
1800
  }
1790
1801
  if (supportsEip1559) {
1791
1802
  if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas || feeData.maxFeePerGas.lte(0) || feeData.maxPriorityFeePerGas.lte(0)) {
1792
- return createExecuteError(ErrorMessages.GAS_PRICE_FAILED);
1803
+ return createExecuteError("Bitget swap failed: Invalid gas fee data (EIP-1559)");
1793
1804
  }
1794
1805
  } else {
1795
1806
  if (!feeData.gasPrice || feeData.gasPrice.lte(0)) {
1796
- return createExecuteError(ErrorMessages.GAS_PRICE_FAILED);
1807
+ return createExecuteError("Bitget swap failed: Invalid gas price");
1797
1808
  }
1798
1809
  }
1799
1810
  const txParams = {
@@ -1833,159 +1844,1000 @@ var BitgetRouter = class {
1833
1844
  }
1834
1845
  }
1835
1846
  };
1836
- async function completeQuote(params, config) {
1837
- const {
1838
- sourceToken,
1839
- targetToken,
1840
- sourceChain,
1841
- targetChain: _targetChain,
1842
- // Reserved for future use
1843
- amountIn,
1844
- slippage,
1845
- recipient,
1846
- refundTo,
1847
- customRecipientMsg,
1848
- appFees,
1849
- evmChainId
1850
- } = params;
1851
- const {
1852
- intentsQuotationAdapter,
1853
- dexRouters,
1854
- dexRouter,
1855
- bluechipTokens,
1856
- configAdapter,
1857
- currentUserAddress,
1858
- isIntentsSupportedToken: customIsIntentsSupportedToken
1859
- } = config;
1860
- const wrapNearContractId = configAdapter.getWrapNearContractId();
1861
- const routers = dexRouters || (dexRouter ? [dexRouter] : []);
1862
- const userAddress = currentUserAddress || recipient;
1863
- if (!userAddress) {
1864
- throw new Error(ErrorMessages.MISSING_USER_ADDRESS);
1847
+ var OkxRouter = class {
1848
+ constructor(config) {
1849
+ this.okxAdapter = config.okxAdapter;
1850
+ this.evmChainAdapter = config.evmChainAdapter;
1851
+ this.chainId = config.chainId;
1865
1852
  }
1866
- if (sourceToken?.address === void 0) {
1867
- throw new Error(ErrorMessages.MISSING_TOKEN_ADDRESS);
1853
+ getCapabilities() {
1854
+ return {
1855
+ requiresRecipient: true,
1856
+ requiresFinalizeQuote: false,
1857
+ requiresComplexRegistration: false,
1858
+ supportedChain: "evm"
1859
+ };
1868
1860
  }
1869
- if (targetToken?.address === void 0) {
1870
- throw new Error(ErrorMessages.MISSING_TOKEN_ADDRESS);
1861
+ getSupportedChain() {
1862
+ return "evm";
1871
1863
  }
1872
- const isEvmChain = evmChainId !== void 0 || sourceChain === "evm" || sourceChain === "ethereum" || sourceChain === "bsc" || sourceChain === "polygon" || sourceChain === "base" || sourceChain === "monad" || sourceToken.chain === "evm";
1873
- const isTokenIntentsSupported = customIsIntentsSupportedToken ? customIsIntentsSupportedToken(sourceToken) : isEvmChain ? isEvmIntentsSupportedToken(sourceToken, bluechipTokens) : isNearIntentsSupportedToken(sourceToken, bluechipTokens);
1874
- const bluechipToken = isEvmChain ? findBestEvmBluechipToken(
1875
- bluechipTokens,
1876
- configAdapter.getEvmNativeWrappedTokenAddress?.()
1877
- ) : findBestBluechipToken(bluechipTokens, wrapNearContractId);
1878
- if (!bluechipToken?.address) {
1879
- throw new Error(ErrorMessages.QUOTE_FAILED);
1864
+ getChainId() {
1865
+ return this.chainId;
1880
1866
  }
1881
- const quotePaths = [];
1882
- routers.forEach((router, index) => {
1883
- const supportedChain = router.getSupportedChain();
1884
- const isEvmRouter = supportedChain === "evm";
1885
- let routeType;
1886
- if (isEvmRouter) {
1887
- routeType = "evm";
1888
- } else {
1889
- routeType = index === 0 ? "v1" : "v2";
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) {
1897
+ return {
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"
1906
+ };
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
1977
+ });
1978
+ const detailedError = isRateLimit ? `OKX API rate limit error: ${errorMessage}. Please try again later.` : `OKX quote failed: ${errorMessage}`;
1979
+ return createQuoteError(params, detailedError);
1890
1980
  }
1891
- const capabilities = router.getCapabilities();
1892
- const quoteParams = capabilities.requiresRecipient ? {
1893
- tokenIn: sourceToken,
1894
- tokenOut: bluechipToken,
1895
- amountIn,
1896
- slippage,
1897
- swapType: "EXACT_INPUT",
1898
- sender: userAddress,
1899
- recipient: userAddress
1900
- } : {
1901
- tokenIn: sourceToken,
1902
- tokenOut: bluechipToken,
1903
- amountIn,
1904
- slippage,
1905
- swapType: "EXACT_INPUT"
1906
- };
1907
- quotePaths.push({
1908
- type: routeType,
1909
- router,
1910
- promise: (async () => {
1911
- const preSwapQuote = await router.quote(quoteParams);
1912
- if (!preSwapQuote.success) {
1913
- throw new Error(ErrorMessages.QUOTE_FAILED);
1914
- }
1915
- let normalizedSourceAsset;
1916
- if (isEvmChain) {
1917
- const bluechipKey = bluechipToken.symbol?.toUpperCase();
1918
- const bluechipTokenConfig = bluechipKey && bluechipTokens[bluechipKey] || void 0;
1919
- normalizedSourceAsset = bluechipTokenConfig?.assetId ? bluechipTokenConfig.assetId : `evm:${normalizeEvmAddress(bluechipToken.address)}`;
1920
- } else {
1921
- const bluechipKey = bluechipToken.symbol?.toUpperCase() === "WNEAR" ? "NEAR" : bluechipToken.symbol?.toUpperCase();
1922
- const bluechipTokenConfig = bluechipKey && bluechipTokens[bluechipKey] || void 0;
1923
- normalizedSourceAsset = bluechipTokenConfig?.assetId ? bluechipTokenConfig.assetId : `nep141:${bluechipToken.address}`;
1924
- }
1925
- let normalizedTargetAsset = targetToken.address;
1926
- if (normalizedTargetAsset?.startsWith("1cs_v1:")) ; else if (normalizedTargetAsset && !normalizedTargetAsset.startsWith("nep141:") && !normalizedTargetAsset.startsWith("nep245:") && normalizedTargetAsset.includes(".")) {
1927
- normalizedTargetAsset = `nep141:${normalizeTokenId(
1928
- normalizedTargetAsset,
1929
- wrapNearContractId
1930
- )}`;
1931
- }
1932
- if (!normalizedTargetAsset?.startsWith("1cs_v1:")) {
1933
- normalizedTargetAsset = normalizeDestinationAsset(normalizedTargetAsset, wrapNearContractId) || normalizedTargetAsset;
1934
- }
1935
- const slippageBps = convertSlippageToBasisPoints(slippage);
1936
- let formattedAmountOut = preSwapQuote.amountOut;
1937
- if (isEvmChain && bluechipToken.decimals !== void 0) {
1938
- try {
1939
- const amountBN = new Big3(preSwapQuote.amountOut);
1940
- if (amountBN.lte(0)) {
1941
- throw new Error(ErrorMessages.QUOTE_INVALID);
1942
- }
1943
- formattedAmountOut = amountBN.toFixed(0, Big3.roundDown);
1944
- } catch (error) {
1945
- throw new Error(ErrorMessages.QUOTE_FAILED);
1946
- }
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
+ });
1947
2034
  }
1948
- try {
1949
- const intentsQuote = await intentsQuotationAdapter.quote({
1950
- originAsset: normalizedSourceAsset,
1951
- destinationAsset: normalizedTargetAsset,
1952
- amount: formattedAmountOut,
1953
- refundTo: refundTo || recipient,
1954
- recipient,
1955
- slippageTolerance: slippageBps,
1956
- swapType: "FLEX_INPUT",
1957
- ...customRecipientMsg ? { customRecipientMsg } : {},
1958
- ...appFees ? { appFees } : {}
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
1959
2049
  });
1960
- if (intentsQuote.quoteStatus !== "success") {
1961
- throw new Error(ErrorMessages.QUOTE_FAILED);
1962
- }
1963
- return {
1964
- intentsQuote,
1965
- preSwapQuote,
1966
- router,
1967
- finalAmountOut: intentsQuote.quoteSuccessResult?.quote?.amountOut || "0"
1968
- };
1969
- } catch (error) {
1970
- throw error;
1971
2050
  }
1972
- })()
1973
- });
1974
- });
1975
- if (isTokenIntentsSupported) {
1976
- quotePaths.push({
1977
- type: "intents",
1978
- promise: (async () => {
1979
- let normalizedSourceAsset;
1980
- if (isEvmChain) {
1981
- const sourceKey = sourceToken.symbol?.toUpperCase();
1982
- const sourceTokenConfig = sourceKey ? bluechipTokens[sourceKey] : void 0;
1983
- if (sourceTokenConfig?.assetId) {
1984
- normalizedSourceAsset = sourceTokenConfig.assetId;
1985
- } else if (sourceToken.address === "") {
1986
- normalizedSourceAsset = `evm-${sourceChain}-native`;
1987
- } else {
1988
- normalizedSourceAsset = `evm:${normalizeEvmAddress(sourceToken.address)}`;
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)}`;
1989
2841
  }
1990
2842
  } else {
1991
2843
  const sourceKey = sourceToken.symbol?.toUpperCase();
@@ -1999,15 +2851,22 @@ async function completeQuote(params, config) {
1999
2851
  }
2000
2852
  }
2001
2853
  }
2002
- let normalizedTargetAsset = targetToken.address;
2003
- if (normalizedTargetAsset?.startsWith("1cs_v1:")) ; else if (normalizedTargetAsset && !normalizedTargetAsset.startsWith("nep141:") && !normalizedTargetAsset.startsWith("nep245:") && normalizedTargetAsset.includes(".")) {
2004
- normalizedTargetAsset = `nep141:${normalizeTokenId(
2005
- normalizedTargetAsset,
2006
- wrapNearContractId
2007
- )}`;
2008
- }
2009
- if (!normalizedTargetAsset?.startsWith("1cs_v1:")) {
2010
- normalizedTargetAsset = normalizeDestinationAsset(normalizedTargetAsset, wrapNearContractId) || normalizedTargetAsset;
2854
+ let normalizedTargetAsset;
2855
+ if (isEvmChain && targetToken.platform === "nearIntents" && targetToken.assetId) {
2856
+ normalizedTargetAsset = targetToken.assetId;
2857
+ } else if (targetToken.assetId && (targetToken.assetId.startsWith("nep245:") || targetToken.assetId.startsWith("nep141:") || targetToken.assetId.startsWith("1cs_v1:"))) {
2858
+ normalizedTargetAsset = targetToken.assetId;
2859
+ } else {
2860
+ normalizedTargetAsset = targetToken.address;
2861
+ if (normalizedTargetAsset?.startsWith("1cs_v1:")) ; else if (normalizedTargetAsset && !normalizedTargetAsset.startsWith("nep141:") && !normalizedTargetAsset.startsWith("nep245:") && normalizedTargetAsset.includes(".")) {
2862
+ normalizedTargetAsset = `nep141:${normalizeTokenId(
2863
+ normalizedTargetAsset,
2864
+ wrapNearContractId
2865
+ )}`;
2866
+ }
2867
+ if (!normalizedTargetAsset?.startsWith("1cs_v1:")) {
2868
+ normalizedTargetAsset = normalizeDestinationAsset(normalizedTargetAsset, wrapNearContractId) || normalizedTargetAsset;
2869
+ }
2011
2870
  }
2012
2871
  const slippageBps = convertSlippageToBasisPoints(slippage);
2013
2872
  const intentsQuote = await intentsQuotationAdapter.quote({
@@ -2021,7 +2880,13 @@ async function completeQuote(params, config) {
2021
2880
  ...customRecipientMsg ? { customRecipientMsg } : {}
2022
2881
  });
2023
2882
  if (intentsQuote.quoteStatus !== "success") {
2024
- throw new Error(ErrorMessages.QUOTE_FAILED);
2883
+ const formatError = config.formatErrorMessage || formatErrorMessage;
2884
+ const errorMessage = formatError({
2885
+ error: intentsQuote.messageOriginal || intentsQuote.message,
2886
+ originAsset: normalizedSourceAsset,
2887
+ fallbackMessage: ErrorMessages.QUOTE_FAILED
2888
+ });
2889
+ throw new Error(errorMessage);
2025
2890
  }
2026
2891
  return {
2027
2892
  intentsQuote,
@@ -2034,6 +2899,7 @@ async function completeQuote(params, config) {
2034
2899
  quotePaths.map((p) => p.promise)
2035
2900
  );
2036
2901
  const validPaths = [];
2902
+ const errorMessages = [];
2037
2903
  pathResults.forEach((result, index) => {
2038
2904
  const pathType = quotePaths[index].type;
2039
2905
  if (result.status === "fulfilled") {
@@ -2041,10 +2907,28 @@ async function completeQuote(params, config) {
2041
2907
  type: pathType,
2042
2908
  ...result.value
2043
2909
  });
2910
+ } else if (result.status === "rejected") {
2911
+ const error = result.reason;
2912
+ if (error instanceof Error) {
2913
+ errorMessages.push(error.message);
2914
+ } else if (typeof error === "string") {
2915
+ errorMessages.push(error);
2916
+ } else {
2917
+ errorMessages.push(String(error));
2918
+ }
2044
2919
  }
2045
2920
  });
2046
2921
  if (validPaths.length === 0) {
2047
- throw new Error(ErrorMessages.QUOTE_FAILED);
2922
+ const intentsError = errorMessages.find(
2923
+ (msg) => msg && msg !== ErrorMessages.QUOTE_FAILED && (msg.toLowerCase().includes("bridge") || msg.toLowerCase().includes("amount") || msg.toLowerCase().includes("low") || msg.toLowerCase().includes("minimum"))
2924
+ );
2925
+ const bestError = intentsError || errorMessages[0] || ErrorMessages.QUOTE_FAILED;
2926
+ const formatError = config.formatErrorMessage || formatErrorMessage;
2927
+ const formattedError = formatError({
2928
+ error: bestError,
2929
+ fallbackMessage: ErrorMessages.QUOTE_FAILED
2930
+ });
2931
+ throw new Error(formattedError);
2048
2932
  }
2049
2933
  const bestPath = validPaths.reduce((best, current) => {
2050
2934
  const bestAmount = new Big3(best.finalAmountOut);
@@ -2053,7 +2937,7 @@ async function completeQuote(params, config) {
2053
2937
  });
2054
2938
  const depositAddress = bestPath.intentsQuote.quoteSuccessResult?.quote?.depositAddress || "";
2055
2939
  if (!depositAddress) {
2056
- throw new Error(ErrorMessages.QUOTE_INVALID);
2940
+ throw new Error(ErrorMessages.QUOTE_FAILED);
2057
2941
  }
2058
2942
  return {
2059
2943
  intents: {
@@ -2104,28 +2988,32 @@ async function quoteSameChainSwap(params, dexRouters) {
2104
2988
  return router.quote(quoteParams);
2105
2989
  })
2106
2990
  );
2107
- const validQuotes = quoteResults.filter(
2108
- (r) => r.status === "fulfilled" && r.value.success
2991
+ const fulfilledResults = quoteResults.filter(
2992
+ (r) => r.status === "fulfilled"
2109
2993
  ).map((r, index) => ({
2110
- quote: r.value,
2111
- router: dexRouters[index]
2994
+ result: r.value,
2995
+ router: dexRouters[index],
2996
+ index
2112
2997
  }));
2113
- if (validQuotes.length === 0) {
2114
- const errors = quoteResults.map((r, index) => {
2115
- if (r.status === "rejected") {
2116
- return `Router ${index}: ${r.reason}`;
2117
- }
2118
- if (r.status === "fulfilled" && !r.value.success) {
2119
- return `Router ${index}: ${r.value.error}`;
2120
- }
2121
- return null;
2122
- }).filter(Boolean);
2123
- const errorMessage = errors.length > 0 ? `${ErrorMessages.QUOTE_FAILED}: ${errors.join("; ")}` : ErrorMessages.QUOTE_FAILED;
2124
- throw new Error(errorMessage);
2125
- }
2126
- return selectBestQuote(validQuotes);
2998
+ const validQuotes = fulfilledResults.filter((r) => r.result.success).map((r) => ({
2999
+ quote: r.result,
3000
+ router: r.router
3001
+ }));
3002
+ if (validQuotes.length > 0) {
3003
+ return selectBestQuote(validQuotes);
3004
+ }
3005
+ const errors = fulfilledResults.filter((r) => !r.result.success).map((r) => {
3006
+ const error = r.result.error || "";
3007
+ const is429 = error.includes("429") || error.toLowerCase().includes("rate limit") || error.toLowerCase().includes("too many requests");
3008
+ return is429 ? null : `Router ${r.index}: ${error}`;
3009
+ }).filter(Boolean);
3010
+ if (errors.length === 0) {
3011
+ throw new Error("All liquidity providers are busy. Please try again later.");
3012
+ }
3013
+ const errorMessage = errors.length > 0 ? `${ErrorMessages.QUOTE_FAILED}: ${errors.join("; ")}` : ErrorMessages.QUOTE_FAILED;
3014
+ throw new Error(errorMessage);
2127
3015
  }
2128
3016
 
2129
- export { AggregateDexRouter, BitgetRouter, ErrorMessages, NearSmartRouter, completeQuote, convertSlippageToBasisPoints, findBestBluechipToken, findBestEvmBluechipToken, formatGasString, formatGasToTgas, getBluechipTokensConfig, getErrorMessage, isEvmIntentsSupportedToken, isNearIntentsSupportedToken, logger, normalizeDestinationAsset, normalizeError, normalizeEvmAddress, normalizeTokenId, quoteSameChainSwap, requiresRecipient, requiresRecipientInExecute, selectBestQuote, setBluechipTokensConfig };
3017
+ 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 };
2130
3018
  //# sourceMappingURL=index.mjs.map
2131
3019
  //# sourceMappingURL=index.mjs.map