@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.js CHANGED
@@ -16,31 +16,12 @@ function requiresRecipientInExecute(params) {
16
16
  }
17
17
 
18
18
  // src/utils/errorMessages.ts
19
+ var TRANSACTION_EXECUTION_ERROR_MESSAGE = "Transaction execution error occurred. Please contact support";
19
20
  var ErrorMessages = {
20
- // Parameter validation errors
21
- MISSING_PARAMS: "Required parameters missing",
22
- MISSING_TOKEN_ADDRESS: "Token address required",
23
- INVALID_TOKEN_ADDRESS: "Invalid token address",
24
- MISSING_USER_ADDRESS: "User address required",
25
- INVALID_USER_ADDRESS: "Invalid user address",
26
21
  // Quote errors
27
22
  QUOTE_FAILED: "Failed to get quote",
28
- QUOTE_INVALID: "Invalid quote",
29
- QUOTE_NO_ROUTE: "No route found",
30
- QUOTE_EXPIRED: "Quote expired, please refresh",
31
23
  // Execution errors
32
- EXECUTE_FAILED: "Transaction failed",
33
- EXECUTE_INVALID_QUOTE: "Invalid quote, please refresh",
34
- EXECUTE_INSUFFICIENT_BALANCE: "Insufficient balance",
35
- EXECUTE_INSUFFICIENT_LIQUIDITY: "Insufficient liquidity",
36
- EXECUTE_SLIPPAGE_TOO_HIGH: "Price changed, please refresh",
37
- // Network/API errors
38
- NETWORK_ERROR: "Network error, please try again",
39
- API_ERROR: "Service temporarily unavailable",
40
- // Gas/Transaction errors
41
- GAS_ESTIMATE_FAILED: "Unable to estimate transaction fee",
42
- GAS_PRICE_FAILED: "Failed to get transaction fee",
43
- TRANSACTION_REVERTED: "Transaction would fail, please refresh"
24
+ EXECUTE_FAILED: "Transaction failed"
44
25
  };
45
26
  function getErrorMessage(error, fallback = ErrorMessages.EXECUTE_FAILED) {
46
27
  if (!error) return fallback;
@@ -49,41 +30,70 @@ function getErrorMessage(error, fallback = ErrorMessages.EXECUTE_FAILED) {
49
30
  }
50
31
  return fallback;
51
32
  }
52
- function normalizeError(error) {
53
- if (!error) return ErrorMessages.EXECUTE_FAILED;
54
- const errorLower = error.toLowerCase();
55
- if (errorLower.includes("missing") || errorLower.includes("required")) {
56
- if (errorLower.includes("token")) return ErrorMessages.MISSING_TOKEN_ADDRESS;
57
- if (errorLower.includes("address") || errorLower.includes("user") || errorLower.includes("sender") || errorLower.includes("recipient")) {
58
- return ErrorMessages.MISSING_USER_ADDRESS;
59
- }
60
- return ErrorMessages.MISSING_PARAMS;
61
- }
62
- if (errorLower.includes("invalid")) {
63
- if (errorLower.includes("token")) return ErrorMessages.INVALID_TOKEN_ADDRESS;
64
- if (errorLower.includes("address") || errorLower.includes("user")) {
65
- return ErrorMessages.INVALID_USER_ADDRESS;
66
- }
67
- if (errorLower.includes("quote")) return ErrorMessages.QUOTE_INVALID;
33
+ function processErrorMessage(errorMessage) {
34
+ const errorString = errorMessage.toLowerCase();
35
+ const userActionKeywords = [
36
+ "user",
37
+ "rejected",
38
+ "cancelled",
39
+ "cancel",
40
+ "denied",
41
+ "undefined",
42
+ "null"
43
+ ];
44
+ const containsUserActionKeyword = userActionKeywords.some(
45
+ (keyword) => errorString.includes(keyword)
46
+ );
47
+ if (containsUserActionKeyword) {
48
+ return errorMessage;
68
49
  }
69
- if (errorLower.includes("quote") && (errorLower.includes("fail") || errorLower.includes("error"))) {
50
+ return TRANSACTION_EXECUTION_ERROR_MESSAGE;
51
+ }
52
+ function normalizeErrorForIntents(error) {
53
+ if (!error) return ErrorMessages.QUOTE_FAILED;
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("gas") || errorLower.includes("network")) {
70
56
  return ErrorMessages.QUOTE_FAILED;
71
57
  }
72
- if (errorLower.includes("route") || errorLower.includes("no path")) {
73
- return ErrorMessages.QUOTE_NO_ROUTE;
74
- }
75
- if (errorLower.includes("balance") && errorLower.includes("insufficient")) {
76
- return ErrorMessages.EXECUTE_INSUFFICIENT_BALANCE;
77
- }
78
- if (errorLower.includes("liquidity") || errorLower.includes("slippage")) {
79
- return ErrorMessages.EXECUTE_SLIPPAGE_TOO_HIGH;
80
- }
81
- if (errorLower.includes("gas")) {
82
- if (errorLower.includes("price")) return ErrorMessages.GAS_PRICE_FAILED;
83
- return ErrorMessages.GAS_ESTIMATE_FAILED;
84
- }
58
+ return processErrorMessage(error);
59
+ }
60
+ function normalizeError(error) {
61
+ if (!error) return ErrorMessages.EXECUTE_FAILED;
85
62
  return error;
86
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
+ }
87
97
 
88
98
  // src/utils/logger.ts
89
99
  var LOG_LEVELS = {
@@ -406,7 +416,7 @@ var NearSmartRouter = class {
406
416
  amountOut: "0",
407
417
  minAmountOut: "0",
408
418
  routes: [],
409
- error: ErrorMessages.MISSING_TOKEN_ADDRESS
419
+ error: ErrorMessages.QUOTE_FAILED
410
420
  };
411
421
  }
412
422
  const normalizedTokenIn = normalizeTokenId(
@@ -426,7 +436,7 @@ var NearSmartRouter = class {
426
436
  amountOut: "0",
427
437
  minAmountOut: "0",
428
438
  routes: [],
429
- error: ErrorMessages.INVALID_TOKEN_ADDRESS
439
+ error: ErrorMessages.QUOTE_FAILED
430
440
  };
431
441
  }
432
442
  const slippageBps = convertSlippageToBasisPoints(slippage);
@@ -447,7 +457,7 @@ var NearSmartRouter = class {
447
457
  amountOut: "0",
448
458
  minAmountOut: "0",
449
459
  routes: [],
450
- error: normalizeError(response?.result_msg || response?.result_message) || ErrorMessages.QUOTE_NO_ROUTE
460
+ error: response?.result_msg || response?.result_message || ErrorMessages.QUOTE_FAILED
451
461
  };
452
462
  }
453
463
  const { routes: serverRoutes, amount_out } = response.result_data;
@@ -486,7 +496,7 @@ var NearSmartRouter = class {
486
496
  amountOut: "0",
487
497
  minAmountOut: "0",
488
498
  routes: [],
489
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
499
+ error: error?.message || ErrorMessages.QUOTE_FAILED
490
500
  };
491
501
  }
492
502
  }
@@ -499,7 +509,7 @@ var NearSmartRouter = class {
499
509
  if (!quote.success || !quote.routes.length) {
500
510
  return {
501
511
  success: false,
502
- error: ErrorMessages.EXECUTE_INVALID_QUOTE
512
+ error: ErrorMessages.QUOTE_FAILED
503
513
  };
504
514
  }
505
515
  const swapActions = [];
@@ -518,7 +528,7 @@ var NearSmartRouter = class {
518
528
  if (!swapActions.length) {
519
529
  return {
520
530
  success: false,
521
- error: ErrorMessages.QUOTE_INVALID
531
+ error: ErrorMessages.QUOTE_FAILED
522
532
  };
523
533
  }
524
534
  const finalRecipient = depositAddress || recipient;
@@ -548,7 +558,6 @@ var NearSmartRouter = class {
548
558
  },
549
559
  gas: "50000000000000",
550
560
  expandDeposit: "1250000000000000000000"
551
- // 0.00125 NEAR
552
561
  });
553
562
  }
554
563
  transactions.push({
@@ -604,7 +613,6 @@ var NearSmartRouter = class {
604
613
  msg: JSON.stringify(swapMsg)
605
614
  },
606
615
  gas: "250",
607
- // NEP-141 requires attaching 1 yoctoNEAR for certain calls.
608
616
  expandDeposit: "1"
609
617
  });
610
618
  const result = await this.nearChainAdapter.call({
@@ -687,7 +695,7 @@ var AggregateDexRouter = class {
687
695
  amountOut: "0",
688
696
  minAmountOut: "0",
689
697
  routes: [],
690
- error: ErrorMessages.MISSING_USER_ADDRESS
698
+ error: ErrorMessages.QUOTE_FAILED
691
699
  };
692
700
  }
693
701
  const { tokenIn, tokenOut, amountIn, slippage, sender, recipient } = params;
@@ -700,7 +708,7 @@ var AggregateDexRouter = class {
700
708
  amountOut: "0",
701
709
  minAmountOut: "0",
702
710
  routes: [],
703
- error: ErrorMessages.MISSING_USER_ADDRESS
711
+ error: ErrorMessages.QUOTE_FAILED
704
712
  };
705
713
  }
706
714
  if (!tokenIn?.address || !tokenOut?.address) {
@@ -712,7 +720,7 @@ var AggregateDexRouter = class {
712
720
  amountOut: "0",
713
721
  minAmountOut: "0",
714
722
  routes: [],
715
- error: ErrorMessages.MISSING_TOKEN_ADDRESS
723
+ error: ErrorMessages.QUOTE_FAILED
716
724
  };
717
725
  }
718
726
  const normalizedTokenIn = normalizeTokenId(
@@ -732,7 +740,7 @@ var AggregateDexRouter = class {
732
740
  amountOut: "0",
733
741
  minAmountOut: "0",
734
742
  routes: [],
735
- error: ErrorMessages.INVALID_TOKEN_ADDRESS
743
+ error: ErrorMessages.QUOTE_FAILED
736
744
  };
737
745
  }
738
746
  const slippageBps = convertSlippageToBasisPoints(slippage);
@@ -791,7 +799,7 @@ var AggregateDexRouter = class {
791
799
  amountOut: "0",
792
800
  minAmountOut: "0",
793
801
  routes: [],
794
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
802
+ error: error?.message || ErrorMessages.QUOTE_FAILED
795
803
  };
796
804
  }
797
805
  }
@@ -800,7 +808,7 @@ var AggregateDexRouter = class {
800
808
  */
801
809
  async finalizeQuote(params, depositAddress) {
802
810
  if (!requiresRecipient(params)) {
803
- throw new Error(ErrorMessages.MISSING_USER_ADDRESS);
811
+ throw new Error(ErrorMessages.QUOTE_FAILED);
804
812
  }
805
813
  return await this.quote({
806
814
  ...params,
@@ -872,26 +880,26 @@ var AggregateDexRouter = class {
872
880
  if (!requiresRecipientInExecute(params)) {
873
881
  return {
874
882
  success: false,
875
- error: ErrorMessages.MISSING_USER_ADDRESS
883
+ error: ErrorMessages.QUOTE_FAILED
876
884
  };
877
885
  }
878
886
  const { quote, sender, receiveUser } = params;
879
887
  if (!quote.success) {
880
888
  return {
881
889
  success: false,
882
- error: ErrorMessages.EXECUTE_INVALID_QUOTE
890
+ error: ErrorMessages.QUOTE_FAILED
883
891
  };
884
892
  }
885
893
  if (!receiveUser || receiveUser.trim() === "") {
886
894
  return {
887
895
  success: false,
888
- error: ErrorMessages.MISSING_USER_ADDRESS
896
+ error: ErrorMessages.QUOTE_FAILED
889
897
  };
890
898
  }
891
899
  if (receiveUser.startsWith("0x") && receiveUser.length === 42) {
892
900
  return {
893
901
  success: false,
894
- error: ErrorMessages.INVALID_USER_ADDRESS
902
+ error: ErrorMessages.QUOTE_FAILED
895
903
  };
896
904
  }
897
905
  const slippage = quote.slippage || 5e-3;
@@ -923,7 +931,7 @@ var AggregateDexRouter = class {
923
931
  } catch (error) {
924
932
  return {
925
933
  success: false,
926
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
934
+ error: error?.message || ErrorMessages.QUOTE_FAILED
927
935
  };
928
936
  }
929
937
  const routerMsg = finalQuote.routerMsg;
@@ -1126,7 +1134,8 @@ var AggregateDexRouter = class {
1126
1134
  } catch (error) {
1127
1135
  return {
1128
1136
  success: false,
1129
- error: normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
1137
+ // 预交易阶段:保留原始错误信息,不简化
1138
+ error: error?.message || ErrorMessages.QUOTE_FAILED
1130
1139
  };
1131
1140
  }
1132
1141
  const finalAmountToTransfer = finalQuoteForExecution.amountIn;
@@ -1294,17 +1303,9 @@ async function estimateGasLimit(gas, to, transactionData, value, sender, chainId
1294
1303
  reason: estimateError?.reason,
1295
1304
  message: estimateError?.message || String(estimateError)
1296
1305
  };
1297
- console.warn("RPC gas estimate failed:", {
1298
- error: estimateError?.message || String(estimateError),
1299
- code: estimateError?.code,
1300
- reason: estimateError?.reason
1301
- });
1302
1306
  }
1303
1307
  }
1304
1308
  } catch (error) {
1305
- console.warn("Gas estimation error:", {
1306
- error: error?.message || String(error)
1307
- });
1308
1309
  }
1309
1310
  if (estimatedGasLimit && estimatedGasLimit.gt(0) && hasReliableEstimate) {
1310
1311
  return [estimatedGasLimit, true, rpcEstimateError];
@@ -1390,7 +1391,7 @@ var BitgetRouter = class {
1390
1391
  amountOut: "0",
1391
1392
  minAmountOut: "0",
1392
1393
  routes: [],
1393
- error: ErrorMessages.MISSING_USER_ADDRESS
1394
+ error: "Bitget quote failed: Router requires recipient address"
1394
1395
  };
1395
1396
  }
1396
1397
  const { tokenIn, tokenOut, amountIn, slippage, sender, recipient } = params;
@@ -1403,7 +1404,7 @@ var BitgetRouter = class {
1403
1404
  amountOut: "0",
1404
1405
  minAmountOut: "0",
1405
1406
  routes: [],
1406
- error: ErrorMessages.MISSING_USER_ADDRESS
1407
+ error: "Bitget quote failed: Sender and recipient addresses are required"
1407
1408
  };
1408
1409
  }
1409
1410
  if (tokenIn?.address === void 0 || tokenOut?.address === void 0) {
@@ -1415,7 +1416,7 @@ var BitgetRouter = class {
1415
1416
  amountOut: "0",
1416
1417
  minAmountOut: "0",
1417
1418
  routes: [],
1418
- error: ErrorMessages.MISSING_TOKEN_ADDRESS
1419
+ error: "Bitget quote failed: Token addresses are required"
1419
1420
  };
1420
1421
  }
1421
1422
  const normalizedTokenIn = tokenIn.address === "" ? "" : this.normalizeEvmAddress(tokenIn.address);
@@ -1437,7 +1438,7 @@ var BitgetRouter = class {
1437
1438
  if (!isBitgetResponseSuccess(response) || !response.data) {
1438
1439
  return createQuoteError(
1439
1440
  params,
1440
- normalizeError(response.msg) || ErrorMessages.QUOTE_FAILED
1441
+ response.msg || "Bitget API error: Failed to get quote"
1441
1442
  );
1442
1443
  }
1443
1444
  const {
@@ -1503,21 +1504,23 @@ var BitgetRouter = class {
1503
1504
  } catch (error) {
1504
1505
  return createQuoteError(
1505
1506
  params,
1506
- normalizeError(error?.message) || ErrorMessages.QUOTE_FAILED
1507
+ error?.message || "Bitget quote failed: Unknown error"
1507
1508
  );
1508
1509
  }
1509
1510
  }
1510
1511
  async executeSwap(params) {
1511
1512
  try {
1512
1513
  if (!requiresRecipientInExecute(params)) {
1513
- return createExecuteError(ErrorMessages.MISSING_USER_ADDRESS);
1514
+ return createExecuteError("Bitget swap failed: Router requires recipient address");
1514
1515
  }
1515
1516
  const { quote, sender, receiveUser } = params;
1516
1517
  if (!quote.success) {
1517
- return createExecuteError(ErrorMessages.EXECUTE_INVALID_QUOTE);
1518
+ return createExecuteError(
1519
+ quote.error || "Bitget swap failed: Invalid quote"
1520
+ );
1518
1521
  }
1519
1522
  if (!receiveUser || receiveUser.trim() === "") {
1520
- return createExecuteError(ErrorMessages.MISSING_USER_ADDRESS);
1523
+ return createExecuteError("Bitget swap failed: Recipient address is required");
1521
1524
  }
1522
1525
  let market;
1523
1526
  try {
@@ -1525,13 +1528,13 @@ var BitgetRouter = class {
1525
1528
  const routerMsg = JSON.parse(quote.routerMsg);
1526
1529
  market = routerMsg.market || "";
1527
1530
  } else {
1528
- return createExecuteError(ErrorMessages.QUOTE_EXPIRED);
1531
+ return createExecuteError("Bitget swap failed: Missing router message data");
1529
1532
  }
1530
1533
  } catch (error) {
1531
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1534
+ return createExecuteError("Bitget swap failed: Invalid router message format");
1532
1535
  }
1533
1536
  if (!market) {
1534
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1537
+ return createExecuteError("Bitget swap failed: Market information is required");
1535
1538
  }
1536
1539
  const normalizedTokenIn = this.normalizeEvmAddress(
1537
1540
  quote.tokenIn.address
@@ -1554,9 +1557,8 @@ var BitgetRouter = class {
1554
1557
  tokenOutDecimals: quote.tokenOut.decimals
1555
1558
  });
1556
1559
  if (!isBitgetResponseSuccess(reQuoteResponse) || !reQuoteResponse.data) {
1557
- return createExecuteError(
1558
- normalizeError(reQuoteResponse.msg) || ErrorMessages.QUOTE_EXPIRED
1559
- );
1560
+ const errorMsg = reQuoteResponse.msg || "Bitget re-quote failed: No data returned";
1561
+ return createExecuteError(errorMsg);
1560
1562
  }
1561
1563
  market = reQuoteResponse.data?.market || market;
1562
1564
  const swapResponse = await this.bitgetAdapter.swap({
@@ -1575,11 +1577,13 @@ var BitgetRouter = class {
1575
1577
  });
1576
1578
  if (!isBitgetResponseSuccess(swapResponse) || !swapResponse.data) {
1577
1579
  return createExecuteError(
1578
- normalizeError(swapResponse.msg) || ErrorMessages.QUOTE_FAILED
1580
+ swapResponse.msg || "Bitget swap failed: No transaction data"
1579
1581
  );
1580
1582
  }
1581
1583
  if (swapResponse.data?.estimateRevert === true) {
1582
- return createExecuteError(ErrorMessages.EXECUTE_SLIPPAGE_TOO_HIGH);
1584
+ return createExecuteError(
1585
+ swapResponse.msg || "Bitget swap failed: Transaction would revert (slippage or price impact too high)"
1586
+ );
1583
1587
  }
1584
1588
  let transactionData = swapResponse.data?.calldata || swapResponse.data?.data;
1585
1589
  const to = swapResponse.data?.contract || swapResponse.data?.to;
@@ -1593,14 +1597,18 @@ var BitgetRouter = class {
1593
1597
  }
1594
1598
  const gas = swapResponse.data?.gas || (swapResponse.data?.computeUnits !== void 0 ? String(swapResponse.data.computeUnits) : void 0);
1595
1599
  if (!to || !transactionData) {
1596
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1600
+ return createExecuteError(
1601
+ swapResponse.msg || "Bitget swap failed: Missing transaction data or recipient address"
1602
+ );
1597
1603
  }
1598
1604
  if (transactionData.length < 10 || !/^0x[0-9a-fA-F]+$/.test(transactionData)) {
1599
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1605
+ return createExecuteError(
1606
+ swapResponse.msg || "Bitget swap failed: Invalid transaction data format"
1607
+ );
1600
1608
  }
1601
1609
  if (normalizedTokenIn && normalizedTokenIn !== "") {
1602
1610
  if (!this.evmChainAdapter.getBalance) {
1603
- return createExecuteError(ErrorMessages.NETWORK_ERROR);
1611
+ return createExecuteError("Bitget swap failed: Balance check not supported");
1604
1612
  }
1605
1613
  const tokenBalanceFormatted = await this.evmChainAdapter.getBalance({
1606
1614
  address: sender,
@@ -1610,7 +1618,7 @@ var BitgetRouter = class {
1610
1618
  const balanceBN = ethers.ethers.utils.parseUnits(tokenBalanceFormatted || "0", tokenDecimals);
1611
1619
  const amountInBN = ethers.ethers.BigNumber.from(quote.amountIn);
1612
1620
  if (balanceBN.lt(amountInBN)) {
1613
- return createExecuteError(ErrorMessages.EXECUTE_INSUFFICIENT_BALANCE);
1621
+ return createExecuteError("Bitget swap failed: Insufficient token balance");
1614
1622
  }
1615
1623
  const currentAllowance = await this.evmChainAdapter.getAllowance({
1616
1624
  tokenAddress: normalizedTokenIn,
@@ -1643,17 +1651,17 @@ var BitgetRouter = class {
1643
1651
  retryCount++;
1644
1652
  }
1645
1653
  if (newAllowanceBN.lt(amountInBN)) {
1646
- return createExecuteError(ErrorMessages.NETWORK_ERROR);
1654
+ return createExecuteError("Bitget swap failed: Token approval failed or insufficient");
1647
1655
  }
1648
1656
  } catch (error) {
1649
1657
  return createExecuteError(
1650
- normalizeError(error?.message) || ErrorMessages.NETWORK_ERROR
1658
+ error?.message || "Bitget approval check failed"
1651
1659
  );
1652
1660
  }
1653
1661
  }
1654
1662
  } else {
1655
1663
  if (!this.evmChainAdapter.getBalance) {
1656
- return createExecuteError(ErrorMessages.NETWORK_ERROR);
1664
+ return createExecuteError("Bitget swap failed: Balance check not supported");
1657
1665
  }
1658
1666
  const nativeBalanceFormatted = await this.evmChainAdapter.getBalance({
1659
1667
  address: sender,
@@ -1677,7 +1685,7 @@ var BitgetRouter = class {
1677
1685
  const gasCostEstimate = estimatedGasLimitForBalance.mul(gasPriceEstimate2);
1678
1686
  const totalRequired = amountInBN.add(gasCostEstimate);
1679
1687
  if (balanceBN.lt(totalRequired)) {
1680
- return createExecuteError(ErrorMessages.EXECUTE_INSUFFICIENT_BALANCE);
1688
+ return createExecuteError("Bitget swap failed: Insufficient balance (including gas fee)");
1681
1689
  }
1682
1690
  }
1683
1691
  if (normalizedTokenIn && normalizedTokenIn !== "") {
@@ -1689,7 +1697,7 @@ var BitgetRouter = class {
1689
1697
  const finalAllowanceBN = ethers.ethers.BigNumber.from(finalAllowanceCheck);
1690
1698
  const amountInBN = ethers.ethers.BigNumber.from(quote.amountIn);
1691
1699
  if (finalAllowanceBN.lt(amountInBN)) {
1692
- return createExecuteError(ErrorMessages.NETWORK_ERROR);
1700
+ return createExecuteError("Bitget swap failed: Insufficient token allowance");
1693
1701
  }
1694
1702
  }
1695
1703
  const [estimatedGasLimit, hasReliableEstimate, rpcEstimateError] = await estimateGasLimit(
@@ -1710,10 +1718,13 @@ var BitgetRouter = class {
1710
1718
  tokenOut: quote.tokenOut.symbol,
1711
1719
  rpcError: rpcEstimateError
1712
1720
  });
1713
- return createExecuteError(ErrorMessages.EXECUTE_INSUFFICIENT_LIQUIDITY);
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
+ );
1714
1725
  }
1715
1726
  if (!gas || gas === "0") {
1716
- return createExecuteError(ErrorMessages.GAS_ESTIMATE_FAILED);
1727
+ return createExecuteError("Bitget swap failed: Invalid gas estimate from Bitget");
1717
1728
  }
1718
1729
  logger.warn("RPC gas estimation failed, using Bitget estimate", {
1719
1730
  chainId: this.chainId,
@@ -1746,7 +1757,7 @@ var BitgetRouter = class {
1746
1757
  if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas || feeData.maxFeePerGas.lte(0) || feeData.maxPriorityFeePerGas.lte(0)) {
1747
1758
  const cachedGasPrice = await getCachedGasPrice();
1748
1759
  if (!cachedGasPrice || cachedGasPrice.lte(0)) {
1749
- return createExecuteError(ErrorMessages.GAS_PRICE_FAILED);
1760
+ return createExecuteError("Bitget swap failed: Unable to get valid gas price");
1750
1761
  }
1751
1762
  feeData.maxFeePerGas = cachedGasPrice;
1752
1763
  feeData.maxPriorityFeePerGas = cachedGasPrice.div(10);
@@ -1779,27 +1790,27 @@ var BitgetRouter = class {
1779
1790
  } catch (error) {
1780
1791
  const cachedGasPrice = await getCachedGasPrice();
1781
1792
  if (!cachedGasPrice || cachedGasPrice.lte(0)) {
1782
- return createExecuteError(ErrorMessages.GAS_PRICE_FAILED);
1793
+ return createExecuteError("Bitget swap failed: Unable to get valid gas price");
1783
1794
  }
1784
1795
  feeData.gasPrice = cachedGasPrice;
1785
1796
  }
1786
1797
  }
1787
1798
  if (!to || !ethers.ethers.utils.isAddress(to)) {
1788
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1799
+ return createExecuteError("Bitget swap failed: Invalid recipient address");
1789
1800
  }
1790
1801
  if (!transactionData || transactionData.length < 10) {
1791
- return createExecuteError(ErrorMessages.QUOTE_INVALID);
1802
+ return createExecuteError("Bitget swap failed: Invalid transaction data");
1792
1803
  }
1793
1804
  if (!estimatedGasLimit || estimatedGasLimit.lte(0)) {
1794
- return createExecuteError(ErrorMessages.GAS_ESTIMATE_FAILED);
1805
+ return createExecuteError("Bitget swap failed: Invalid gas limit");
1795
1806
  }
1796
1807
  if (supportsEip1559) {
1797
1808
  if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas || feeData.maxFeePerGas.lte(0) || feeData.maxPriorityFeePerGas.lte(0)) {
1798
- return createExecuteError(ErrorMessages.GAS_PRICE_FAILED);
1809
+ return createExecuteError("Bitget swap failed: Invalid gas fee data (EIP-1559)");
1799
1810
  }
1800
1811
  } else {
1801
1812
  if (!feeData.gasPrice || feeData.gasPrice.lte(0)) {
1802
- return createExecuteError(ErrorMessages.GAS_PRICE_FAILED);
1813
+ return createExecuteError("Bitget swap failed: Invalid gas price");
1803
1814
  }
1804
1815
  }
1805
1816
  const txParams = {
@@ -1839,159 +1850,1000 @@ var BitgetRouter = class {
1839
1850
  }
1840
1851
  }
1841
1852
  };
1842
- async function completeQuote(params, config) {
1843
- const {
1844
- sourceToken,
1845
- targetToken,
1846
- sourceChain,
1847
- targetChain: _targetChain,
1848
- // Reserved for future use
1849
- amountIn,
1850
- slippage,
1851
- recipient,
1852
- refundTo,
1853
- customRecipientMsg,
1854
- appFees,
1855
- evmChainId
1856
- } = params;
1857
- const {
1858
- intentsQuotationAdapter,
1859
- dexRouters,
1860
- dexRouter,
1861
- bluechipTokens,
1862
- configAdapter,
1863
- currentUserAddress,
1864
- isIntentsSupportedToken: customIsIntentsSupportedToken
1865
- } = config;
1866
- const wrapNearContractId = configAdapter.getWrapNearContractId();
1867
- const routers = dexRouters || (dexRouter ? [dexRouter] : []);
1868
- const userAddress = currentUserAddress || recipient;
1869
- if (!userAddress) {
1870
- throw new Error(ErrorMessages.MISSING_USER_ADDRESS);
1853
+ var OkxRouter = class {
1854
+ constructor(config) {
1855
+ this.okxAdapter = config.okxAdapter;
1856
+ this.evmChainAdapter = config.evmChainAdapter;
1857
+ this.chainId = config.chainId;
1871
1858
  }
1872
- if (sourceToken?.address === void 0) {
1873
- throw new Error(ErrorMessages.MISSING_TOKEN_ADDRESS);
1859
+ getCapabilities() {
1860
+ return {
1861
+ requiresRecipient: true,
1862
+ requiresFinalizeQuote: false,
1863
+ requiresComplexRegistration: false,
1864
+ supportedChain: "evm"
1865
+ };
1874
1866
  }
1875
- if (targetToken?.address === void 0) {
1876
- throw new Error(ErrorMessages.MISSING_TOKEN_ADDRESS);
1867
+ getSupportedChain() {
1868
+ return "evm";
1877
1869
  }
1878
- const isEvmChain = evmChainId !== void 0 || sourceChain === "evm" || sourceChain === "ethereum" || sourceChain === "bsc" || sourceChain === "polygon" || sourceChain === "base" || sourceChain === "monad" || sourceToken.chain === "evm";
1879
- const isTokenIntentsSupported = customIsIntentsSupportedToken ? customIsIntentsSupportedToken(sourceToken) : isEvmChain ? isEvmIntentsSupportedToken(sourceToken, bluechipTokens) : isNearIntentsSupportedToken(sourceToken, bluechipTokens);
1880
- const bluechipToken = isEvmChain ? findBestEvmBluechipToken(
1881
- bluechipTokens,
1882
- configAdapter.getEvmNativeWrappedTokenAddress?.()
1883
- ) : findBestBluechipToken(bluechipTokens, wrapNearContractId);
1884
- if (!bluechipToken?.address) {
1885
- throw new Error(ErrorMessages.QUOTE_FAILED);
1870
+ getChainId() {
1871
+ return this.chainId;
1886
1872
  }
1887
- const quotePaths = [];
1888
- routers.forEach((router, index) => {
1889
- const supportedChain = router.getSupportedChain();
1890
- const isEvmRouter = supportedChain === "evm";
1891
- let routeType;
1892
- if (isEvmRouter) {
1893
- routeType = "evm";
1894
- } else {
1895
- 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);
1896
1986
  }
1897
- const capabilities = router.getCapabilities();
1898
- const quoteParams = capabilities.requiresRecipient ? {
1899
- tokenIn: sourceToken,
1900
- tokenOut: bluechipToken,
1901
- amountIn,
1902
- slippage,
1903
- swapType: "EXACT_INPUT",
1904
- sender: userAddress,
1905
- recipient: userAddress
1906
- } : {
1907
- tokenIn: sourceToken,
1908
- tokenOut: bluechipToken,
1909
- amountIn,
1910
- slippage,
1911
- swapType: "EXACT_INPUT"
1912
- };
1913
- quotePaths.push({
1914
- type: routeType,
1915
- router,
1916
- promise: (async () => {
1917
- const preSwapQuote = await router.quote(quoteParams);
1918
- if (!preSwapQuote.success) {
1919
- throw new Error(ErrorMessages.QUOTE_FAILED);
1920
- }
1921
- let normalizedSourceAsset;
1922
- if (isEvmChain) {
1923
- const bluechipKey = bluechipToken.symbol?.toUpperCase();
1924
- const bluechipTokenConfig = bluechipKey && bluechipTokens[bluechipKey] || void 0;
1925
- normalizedSourceAsset = bluechipTokenConfig?.assetId ? bluechipTokenConfig.assetId : `evm:${normalizeEvmAddress(bluechipToken.address)}`;
1926
- } else {
1927
- const bluechipKey = bluechipToken.symbol?.toUpperCase() === "WNEAR" ? "NEAR" : bluechipToken.symbol?.toUpperCase();
1928
- const bluechipTokenConfig = bluechipKey && bluechipTokens[bluechipKey] || void 0;
1929
- normalizedSourceAsset = bluechipTokenConfig?.assetId ? bluechipTokenConfig.assetId : `nep141:${bluechipToken.address}`;
1930
- }
1931
- let normalizedTargetAsset = targetToken.address;
1932
- if (normalizedTargetAsset?.startsWith("1cs_v1:")) ; else if (normalizedTargetAsset && !normalizedTargetAsset.startsWith("nep141:") && !normalizedTargetAsset.startsWith("nep245:") && normalizedTargetAsset.includes(".")) {
1933
- normalizedTargetAsset = `nep141:${normalizeTokenId(
1934
- normalizedTargetAsset,
1935
- wrapNearContractId
1936
- )}`;
1937
- }
1938
- if (!normalizedTargetAsset?.startsWith("1cs_v1:")) {
1939
- normalizedTargetAsset = normalizeDestinationAsset(normalizedTargetAsset, wrapNearContractId) || normalizedTargetAsset;
1940
- }
1941
- const slippageBps = convertSlippageToBasisPoints(slippage);
1942
- let formattedAmountOut = preSwapQuote.amountOut;
1943
- if (isEvmChain && bluechipToken.decimals !== void 0) {
1944
- try {
1945
- const amountBN = new Big3__default.default(preSwapQuote.amountOut);
1946
- if (amountBN.lte(0)) {
1947
- throw new Error(ErrorMessages.QUOTE_INVALID);
1948
- }
1949
- formattedAmountOut = amountBN.toFixed(0, Big3__default.default.roundDown);
1950
- } catch (error) {
1951
- throw new Error(ErrorMessages.QUOTE_FAILED);
1952
- }
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
+ });
1953
2040
  }
1954
- try {
1955
- const intentsQuote = await intentsQuotationAdapter.quote({
1956
- originAsset: normalizedSourceAsset,
1957
- destinationAsset: normalizedTargetAsset,
1958
- amount: formattedAmountOut,
1959
- refundTo: refundTo || recipient,
1960
- recipient,
1961
- slippageTolerance: slippageBps,
1962
- swapType: "FLEX_INPUT",
1963
- ...customRecipientMsg ? { customRecipientMsg } : {},
1964
- ...appFees ? { appFees } : {}
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
1965
2055
  });
1966
- if (intentsQuote.quoteStatus !== "success") {
1967
- throw new Error(ErrorMessages.QUOTE_FAILED);
1968
- }
1969
- return {
1970
- intentsQuote,
1971
- preSwapQuote,
1972
- router,
1973
- finalAmountOut: intentsQuote.quoteSuccessResult?.quote?.amountOut || "0"
1974
- };
1975
- } catch (error) {
1976
- throw error;
1977
2056
  }
1978
- })()
1979
- });
1980
- });
1981
- if (isTokenIntentsSupported) {
1982
- quotePaths.push({
1983
- type: "intents",
1984
- promise: (async () => {
1985
- let normalizedSourceAsset;
1986
- if (isEvmChain) {
1987
- const sourceKey = sourceToken.symbol?.toUpperCase();
1988
- const sourceTokenConfig = sourceKey ? bluechipTokens[sourceKey] : void 0;
1989
- if (sourceTokenConfig?.assetId) {
1990
- normalizedSourceAsset = sourceTokenConfig.assetId;
1991
- } else if (sourceToken.address === "") {
1992
- normalizedSourceAsset = `evm-${sourceChain}-native`;
1993
- } else {
1994
- normalizedSourceAsset = `evm:${normalizeEvmAddress(sourceToken.address)}`;
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;
2110
+ }
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");
2170
+ }
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
+ }
2247
+ try {
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");
2342
+ }
2343
+ } catch (error) {
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;
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;
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 () => {
2796
+ try {
2797
+ const intentsQuote = await intentsQuotationAdapter.quote({
2798
+ originAsset: normalizedSourceAsset,
2799
+ destinationAsset: normalizedTargetAsset,
2800
+ amount: formattedAmountOut,
2801
+ refundTo: refundTo || recipient,
2802
+ recipient,
2803
+ slippageTolerance: slippageBps,
2804
+ swapType: "FLEX_INPUT",
2805
+ ...customRecipientMsg ? { customRecipientMsg } : {},
2806
+ ...appFees ? { appFees } : {}
2807
+ });
2808
+ if (intentsQuote.quoteStatus !== "success") {
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);
2816
+ }
2817
+ return {
2818
+ intentsQuote,
2819
+ preSwapQuote,
2820
+ router,
2821
+ finalAmountOut: intentsQuote.quoteSuccessResult?.quote?.amountOut || "0"
2822
+ };
2823
+ } catch (error) {
2824
+ throw error;
2825
+ }
2826
+ })()
2827
+ });
2828
+ }
2829
+ if (isTokenIntentsSupported) {
2830
+ quotePaths.push({
2831
+ type: "intents",
2832
+ promise: (async () => {
2833
+ let normalizedSourceAsset;
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) {
2839
+ const sourceKey = sourceToken.symbol?.toUpperCase();
2840
+ const sourceTokenConfig = sourceKey ? bluechipTokens[sourceKey] : void 0;
2841
+ if (sourceTokenConfig?.assetId) {
2842
+ normalizedSourceAsset = sourceTokenConfig.assetId;
2843
+ } else if (sourceToken.address === "") {
2844
+ normalizedSourceAsset = `evm-${sourceChain}-native`;
2845
+ } else {
2846
+ normalizedSourceAsset = `evm:${normalizeEvmAddress(sourceToken.address)}`;
1995
2847
  }
1996
2848
  } else {
1997
2849
  const sourceKey = sourceToken.symbol?.toUpperCase();
@@ -2005,15 +2857,22 @@ async function completeQuote(params, config) {
2005
2857
  }
2006
2858
  }
2007
2859
  }
2008
- let normalizedTargetAsset = targetToken.address;
2009
- if (normalizedTargetAsset?.startsWith("1cs_v1:")) ; else if (normalizedTargetAsset && !normalizedTargetAsset.startsWith("nep141:") && !normalizedTargetAsset.startsWith("nep245:") && normalizedTargetAsset.includes(".")) {
2010
- normalizedTargetAsset = `nep141:${normalizeTokenId(
2011
- normalizedTargetAsset,
2012
- wrapNearContractId
2013
- )}`;
2014
- }
2015
- if (!normalizedTargetAsset?.startsWith("1cs_v1:")) {
2016
- 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
+ }
2017
2876
  }
2018
2877
  const slippageBps = convertSlippageToBasisPoints(slippage);
2019
2878
  const intentsQuote = await intentsQuotationAdapter.quote({
@@ -2027,7 +2886,13 @@ async function completeQuote(params, config) {
2027
2886
  ...customRecipientMsg ? { customRecipientMsg } : {}
2028
2887
  });
2029
2888
  if (intentsQuote.quoteStatus !== "success") {
2030
- 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);
2031
2896
  }
2032
2897
  return {
2033
2898
  intentsQuote,
@@ -2040,6 +2905,7 @@ async function completeQuote(params, config) {
2040
2905
  quotePaths.map((p) => p.promise)
2041
2906
  );
2042
2907
  const validPaths = [];
2908
+ const errorMessages = [];
2043
2909
  pathResults.forEach((result, index) => {
2044
2910
  const pathType = quotePaths[index].type;
2045
2911
  if (result.status === "fulfilled") {
@@ -2047,10 +2913,28 @@ async function completeQuote(params, config) {
2047
2913
  type: pathType,
2048
2914
  ...result.value
2049
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
+ }
2050
2925
  }
2051
2926
  });
2052
2927
  if (validPaths.length === 0) {
2053
- 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);
2054
2938
  }
2055
2939
  const bestPath = validPaths.reduce((best, current) => {
2056
2940
  const bestAmount = new Big3__default.default(best.finalAmountOut);
@@ -2059,7 +2943,7 @@ async function completeQuote(params, config) {
2059
2943
  });
2060
2944
  const depositAddress = bestPath.intentsQuote.quoteSuccessResult?.quote?.depositAddress || "";
2061
2945
  if (!depositAddress) {
2062
- throw new Error(ErrorMessages.QUOTE_INVALID);
2946
+ throw new Error(ErrorMessages.QUOTE_FAILED);
2063
2947
  }
2064
2948
  return {
2065
2949
  intents: {
@@ -2110,36 +2994,43 @@ async function quoteSameChainSwap(params, dexRouters) {
2110
2994
  return router.quote(quoteParams);
2111
2995
  })
2112
2996
  );
2113
- const validQuotes = quoteResults.filter(
2114
- (r) => r.status === "fulfilled" && r.value.success
2997
+ const fulfilledResults = quoteResults.filter(
2998
+ (r) => r.status === "fulfilled"
2115
2999
  ).map((r, index) => ({
2116
- quote: r.value,
2117
- router: dexRouters[index]
3000
+ result: r.value,
3001
+ router: dexRouters[index],
3002
+ index
2118
3003
  }));
2119
- if (validQuotes.length === 0) {
2120
- const errors = quoteResults.map((r, index) => {
2121
- if (r.status === "rejected") {
2122
- return `Router ${index}: ${r.reason}`;
2123
- }
2124
- if (r.status === "fulfilled" && !r.value.success) {
2125
- return `Router ${index}: ${r.value.error}`;
2126
- }
2127
- return null;
2128
- }).filter(Boolean);
2129
- const errorMessage = errors.length > 0 ? `${ErrorMessages.QUOTE_FAILED}: ${errors.join("; ")}` : ErrorMessages.QUOTE_FAILED;
2130
- throw new Error(errorMessage);
2131
- }
2132
- 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);
2133
3021
  }
2134
3022
 
2135
3023
  exports.AggregateDexRouter = AggregateDexRouter;
2136
3024
  exports.BitgetRouter = BitgetRouter;
2137
3025
  exports.ErrorMessages = ErrorMessages;
2138
3026
  exports.NearSmartRouter = NearSmartRouter;
3027
+ exports.OkxRouter = OkxRouter;
3028
+ exports.TRANSACTION_EXECUTION_ERROR_MESSAGE = TRANSACTION_EXECUTION_ERROR_MESSAGE;
2139
3029
  exports.completeQuote = completeQuote;
2140
3030
  exports.convertSlippageToBasisPoints = convertSlippageToBasisPoints;
2141
3031
  exports.findBestBluechipToken = findBestBluechipToken;
2142
3032
  exports.findBestEvmBluechipToken = findBestEvmBluechipToken;
3033
+ exports.formatErrorMessage = formatErrorMessage;
2143
3034
  exports.formatGasString = formatGasString;
2144
3035
  exports.formatGasToTgas = formatGasToTgas;
2145
3036
  exports.getBluechipTokensConfig = getBluechipTokensConfig;
@@ -2149,8 +3040,10 @@ exports.isNearIntentsSupportedToken = isNearIntentsSupportedToken;
2149
3040
  exports.logger = logger;
2150
3041
  exports.normalizeDestinationAsset = normalizeDestinationAsset;
2151
3042
  exports.normalizeError = normalizeError;
3043
+ exports.normalizeErrorForIntents = normalizeErrorForIntents;
2152
3044
  exports.normalizeEvmAddress = normalizeEvmAddress;
2153
3045
  exports.normalizeTokenId = normalizeTokenId;
3046
+ exports.processErrorMessage = processErrorMessage;
2154
3047
  exports.quoteSameChainSwap = quoteSameChainSwap;
2155
3048
  exports.requiresRecipient = requiresRecipient;
2156
3049
  exports.requiresRecipientInExecute = requiresRecipientInExecute;