@clonegod/ttd-base-common 1.0.26 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/dist/appconfig/BaseQuoteAppConfig.d.ts +10 -0
  2. package/dist/appconfig/BaseQuoteAppConfig.js +36 -0
  3. package/dist/appconfig/BaseTradeAppConfig.d.ts +7 -0
  4. package/dist/appconfig/BaseTradeAppConfig.js +13 -0
  5. package/dist/appconfig/base_dex_env_args.d.ts +5 -0
  6. package/dist/appconfig/base_dex_env_args.js +68 -0
  7. package/dist/appconfig/base_env_args.d.ts +82 -0
  8. package/dist/appconfig/base_env_args.js +91 -0
  9. package/dist/appconfig/ensure_core_env.d.ts +1 -0
  10. package/dist/appconfig/ensure_core_env.js +18 -0
  11. package/dist/appconfig/index.d.ts +5 -0
  12. package/dist/appconfig/index.js +21 -0
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.js +2 -2
  15. package/dist/quote/depth/amm_depth_calculator.d.ts +19 -0
  16. package/dist/quote/depth/amm_depth_calculator.js +55 -0
  17. package/dist/quote/depth/clmm_depth_calculator.d.ts +28 -0
  18. package/dist/quote/depth/clmm_depth_calculator.js +176 -0
  19. package/dist/quote/depth/index.d.ts +51 -0
  20. package/dist/quote/depth/index.js +264 -0
  21. package/dist/quote/depth/tick_liquidity_snapshot.d.ts +58 -0
  22. package/dist/quote/depth/tick_liquidity_snapshot.js +143 -0
  23. package/dist/quote/event/index.d.ts +1 -0
  24. package/dist/quote/event/index.js +1 -0
  25. package/dist/quote/event/pool_event_listener.d.ts +5 -3
  26. package/dist/quote/event/pool_event_listener.js +128 -150
  27. package/dist/quote/event/swap_debouncer.d.ts +22 -0
  28. package/dist/quote/event/swap_debouncer.js +80 -0
  29. package/dist/quote/get_base_token_price.d.ts +6 -0
  30. package/dist/quote/get_base_token_price.js +90 -0
  31. package/dist/quote/index.d.ts +7 -0
  32. package/dist/quote/index.js +7 -0
  33. package/dist/quote/preload_token_prices.d.ts +2 -0
  34. package/dist/quote/preload_token_prices.js +37 -0
  35. package/dist/quote/price_feed_handler.d.ts +15 -0
  36. package/dist/quote/price_feed_handler.js +56 -0
  37. package/dist/quote/pricing/fee_helpers.d.ts +13 -0
  38. package/dist/quote/pricing/fee_helpers.js +68 -0
  39. package/dist/quote/pricing/index.d.ts +3 -1
  40. package/dist/quote/pricing/index.js +3 -1
  41. package/dist/quote/pricing/pool_state_initializer.d.ts +12 -0
  42. package/dist/quote/pricing/pool_state_initializer.js +191 -0
  43. package/dist/quote/pricing/sdk_token_factory.d.ts +2 -0
  44. package/dist/quote/pricing/sdk_token_factory.js +21 -0
  45. package/dist/quote/quote_amount.d.ts +4 -0
  46. package/dist/quote/quote_amount.js +24 -0
  47. package/dist/quote/tick/cached_tick_data_provider.d.ts +12 -0
  48. package/dist/quote/tick/cached_tick_data_provider.js +45 -0
  49. package/dist/quote/tick/clmm_tick_cache.d.ts +42 -0
  50. package/dist/quote/tick/clmm_tick_cache.js +236 -0
  51. package/dist/quote/tick/index.d.ts +4 -0
  52. package/dist/{ws → quote/tick}/index.js +4 -2
  53. package/dist/quote/tick/state_view_tick_loader.d.ts +17 -0
  54. package/dist/quote/tick/state_view_tick_loader.js +136 -0
  55. package/dist/quote/tick/tick_lens_loaders.d.ts +24 -0
  56. package/dist/quote/tick/tick_lens_loaders.js +158 -0
  57. package/dist/quote/verify/index.d.ts +2 -0
  58. package/dist/quote/verify/index.js +5 -0
  59. package/dist/quote/verify/quote_price_verify.d.ts +30 -0
  60. package/dist/quote/verify/quote_price_verify.js +240 -0
  61. package/dist/redis/redis_client.d.ts +3 -2
  62. package/dist/redis/redis_client.js +86 -116
  63. package/dist/send-tx/constants.d.ts +2 -0
  64. package/dist/send-tx/constants.js +6 -0
  65. package/dist/send-tx/index.d.ts +2 -0
  66. package/dist/{config → send-tx}/index.js +2 -1
  67. package/dist/send-tx/types.d.ts +4 -0
  68. package/dist/send-tx/types.js +2 -0
  69. package/dist/trade/abstract_dex_trade.d.ts +43 -21
  70. package/dist/trade/abstract_dex_trade.js +347 -133
  71. package/dist/trade/caller_manager.d.ts +31 -0
  72. package/dist/trade/caller_manager.js +202 -0
  73. package/dist/trade/check/abstract_tx_result_checker.d.ts +28 -0
  74. package/dist/trade/check/abstract_tx_result_checker.js +192 -0
  75. package/dist/trade/check/index.d.ts +1 -1
  76. package/dist/trade/check/index.js +1 -1
  77. package/dist/trade/index.d.ts +2 -2
  78. package/dist/trade/index.js +2 -2
  79. package/dist/trade/parse/base_parser.d.ts +1 -2
  80. package/dist/trade/parse/base_parser.js +36 -36
  81. package/dist/trade/trade_trace.d.ts +17 -0
  82. package/dist/trade/trade_trace.js +65 -0
  83. package/dist/types/config_types.d.ts +3 -3
  84. package/dist/types/event_types.d.ts +3 -3
  85. package/dist/types/pool_state.d.ts +140 -13
  86. package/dist/utils/ethers_compat.d.ts +13 -0
  87. package/dist/utils/ethers_compat.js +18 -0
  88. package/dist/utils/fast_signer.d.ts +1 -0
  89. package/dist/utils/fast_signer.js +87 -0
  90. package/dist/utils/gas_helper.d.ts +2 -2
  91. package/dist/utils/gas_helper.js +48 -60
  92. package/dist/utils/index.d.ts +5 -2
  93. package/dist/utils/index.js +6 -2
  94. package/dist/utils/pool_filter.d.ts +8 -0
  95. package/dist/utils/pool_filter.js +38 -0
  96. package/dist/utils/trade_direction.d.ts +14 -0
  97. package/dist/utils/trade_direction.js +23 -0
  98. package/package.json +2 -2
  99. package/dist/config/base_env_args.d.ts +0 -11
  100. package/dist/config/base_env_args.js +0 -19
  101. package/dist/config/index.d.ts +0 -1
  102. package/dist/quote/event/verify_clmm_swap_event.d.ts +0 -1
  103. package/dist/quote/event/verify_clmm_swap_event.js +0 -178
  104. package/dist/quote/pricing/token_price_cache.d.ts +0 -10
  105. package/dist/quote/pricing/token_price_cache.js +0 -40
  106. package/dist/trade/abstract_dex_trade_plus.d.ts +0 -43
  107. package/dist/trade/abstract_dex_trade_plus.js +0 -421
  108. package/dist/trade/check/tx_websocket_manager.d.ts +0 -23
  109. package/dist/trade/check/tx_websocket_manager.js +0 -119
  110. package/dist/trade/send/alchemy_base.d.ts +0 -5
  111. package/dist/trade/send/alchemy_base.js +0 -48
  112. package/dist/trade/send/ankr_base.d.ts +0 -5
  113. package/dist/trade/send/ankr_base.js +0 -48
  114. package/dist/trade/send/base_rpc.d.ts +0 -5
  115. package/dist/trade/send/base_rpc.js +0 -48
  116. package/dist/trade/send/blockpi_base.d.ts +0 -5
  117. package/dist/trade/send/blockpi_base.js +0 -48
  118. package/dist/trade/send/bloxroute_base.d.ts +0 -11
  119. package/dist/trade/send/bloxroute_base.js +0 -115
  120. package/dist/trade/send/chainstack_base.d.ts +0 -5
  121. package/dist/trade/send/chainstack_base.js +0 -48
  122. package/dist/trade/send/drpc_base.d.ts +0 -5
  123. package/dist/trade/send/drpc_base.js +0 -48
  124. package/dist/trade/send/getblock_base.d.ts +0 -5
  125. package/dist/trade/send/getblock_base.js +0 -48
  126. package/dist/trade/send/index.d.ts +0 -15
  127. package/dist/trade/send/index.js +0 -33
  128. package/dist/trade/send/infura_base.d.ts +0 -5
  129. package/dist/trade/send/infura_base.js +0 -48
  130. package/dist/trade/send/moralis_base.d.ts +0 -5
  131. package/dist/trade/send/moralis_base.js +0 -48
  132. package/dist/trade/send/onerpc_base.d.ts +0 -5
  133. package/dist/trade/send/onerpc_base.js +0 -48
  134. package/dist/trade/send/quicknode_base.d.ts +0 -5
  135. package/dist/trade/send/quicknode_base.js +0 -48
  136. package/dist/trade/send/send_tx.d.ts +0 -17
  137. package/dist/trade/send/send_tx.js +0 -163
  138. package/dist/ws/event_filter.d.ts +0 -8
  139. package/dist/ws/event_filter.js +0 -36
  140. package/dist/ws/index.d.ts +0 -2
  141. package/dist/ws/subscribe_v2_events.d.ts +0 -14
  142. package/dist/ws/subscribe_v2_events.js +0 -174
  143. package/dist/ws/subscribe_v3_events.d.ts +0 -14
  144. package/dist/ws/subscribe_v3_events.js +0 -174
@@ -1,153 +1,367 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
37
  };
11
38
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.AbstractEvmDexTrade = void 0;
13
- const dist_1 = require("@clonegod/ttd-core/dist");
14
- const ethers_1 = require("ethers");
15
- const common_1 = require("../common");
16
- class AbstractEvmDexTrade extends dist_1.AbastrcatTrade {
39
+ exports.AbstractDexTrade = void 0;
40
+ exports.buildTradeConfig = buildTradeConfig;
41
+ const ttd_core_1 = require("@clonegod/ttd-core");
42
+ const logger = (0, ttd_core_1.createLogger)(__filename);
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const constants_1 = require("../common/constants");
46
+ const BLOCKRAZOR_DEFAULT_TIP_ADDRESS = '0x9D70AC39166ca154307a93fa6b595CF7962fe8e5';
47
+ const caller_manager_1 = require("./caller_manager");
48
+ const abstract_tx_result_checker_1 = require("./check/abstract_tx_result_checker");
49
+ const ttd_core_2 = require("@clonegod/ttd-core");
50
+ const trade_direction_1 = require("../utils/trade_direction");
51
+ const ethers_compat_1 = require("../utils/ethers_compat");
52
+ const fast_signer_1 = require("../utils/fast_signer");
53
+ const trade_trace_1 = require("./trade_trace");
54
+ const decimal_js_1 = __importDefault(require("decimal.js"));
55
+ function buildTradeConfig(envArgs) {
56
+ const vaultGroupId = envArgs.group_id;
57
+ if (!vaultGroupId) {
58
+ throw new Error('GROUP_ID 未配置(envArgs.group_id 为空)');
59
+ }
60
+ const vaultWallet = (0, ttd_core_1.load_wallet)(vaultGroupId, true);
61
+ if (!vaultWallet.public_key) {
62
+ throw new Error(`load_wallet(${vaultGroupId}) 未返回 public_key`);
63
+ }
64
+ return {
65
+ vaultAddress: vaultWallet.public_key,
66
+ executorIds: {
67
+ aerodrome: ethers_compat_1.ethersCompat.id(envArgs.aerodrome_executor_id),
68
+ uniswap: ethers_compat_1.ethersCompat.id(envArgs.uniswap_executor_id),
69
+ },
70
+ };
71
+ }
72
+ const VAULT_ABI = [
73
+ 'function delegatecallExecutorToTrade(bytes32 executorId, bytes calldata data, uint256 deadline, uint256 tipAmount, address tipReceiver) external payable returns (bytes memory result)'
74
+ ];
75
+ const BLOCKRAZOR_CHANNEL_NAME = 'blockrazor';
76
+ class AbstractDexTrade extends ttd_core_1.AbastrcatTrade {
17
77
  constructor(appConfig) {
18
78
  super();
19
79
  this.appConfig = appConfig;
20
- this.approvedTokens = new Map();
21
- this.pairContracts = new Map();
22
- this.tokenContracts = new Map();
23
- this.approvedTokens = new Map();
24
- this.pairContracts = new Map();
25
- this.tokenContracts = new Map();
26
80
  this.initConfigs();
81
+ this.chainNameLower = this.appConfig.env_args.chain_id.toLowerCase();
82
+ this.redisClient = new ttd_core_2.RedisClient(`${this.chainNameLower}:tx`);
83
+ this.vaultInterface = new ethers_compat_1.ethersCompat.Interface(VAULT_ABI);
27
84
  }
28
- init() {
29
- return __awaiter(this, void 0, void 0, function* () {
30
- this.provider = new ethers_1.ethers.providers.JsonRpcProvider(this.chainConfig.rpcEndpoint);
31
- this.wallet = new ethers_1.ethers.Wallet(this.appConfig.trade_runtime.wallet.private_key, this.provider);
32
- (0, dist_1.log_info)(`钱包已初始化,地址: ${this.wallet.address}`);
33
- this.routerContract = new ethers_1.ethers.Contract(this.dexConfig.routerAddress, this.dexConfig.routerAbi, this.wallet);
34
- (0, dist_1.log_info)(`${this.dexConfig.dexName} Router已初始化, 地址: ${this.dexConfig.routerAddress}`);
35
- });
85
+ initConfigs() {
86
+ const env = this.appConfig.env_args;
87
+ this.chainConfig = {
88
+ chainId: 8453,
89
+ nativeCurrency: 'ETH',
90
+ wrappedNativeCurrencyAddress: constants_1.WETH_ADDRESS,
91
+ chainName: 'Base',
92
+ rpcEndpoint: env.rpc_endpoint,
93
+ blockConfirmations: 12,
94
+ gasOptions: {
95
+ gasLimit: env.gas_limit,
96
+ defaultGasPriceGwei: env.gas_price_gwei,
97
+ maxGasLimit: env.gas_max_limit,
98
+ maxGasPriceGwei: env.gas_max_price_gwei,
99
+ maxFeePerGasGwei: env.gas_max_fee_per_gas_gwei,
100
+ maxPriorityFeePerGasGwei: env.gas_max_priority_fee_gwei,
101
+ defaultTipAmountGwei: env.tip_amount_gwei,
102
+ maxTipAmountGwei: env.gas_max_tip_amount_gwei,
103
+ }
104
+ };
105
+ this.tradeConfig = buildTradeConfig(env);
36
106
  }
37
- getTokenContract(tokenAddress) {
38
- if (!this.tokenContracts.has(tokenAddress)) {
39
- const tokenContract = new ethers_1.ethers.Contract(tokenAddress, common_1.ERC20_ABI, this.wallet);
40
- this.tokenContracts.set(tokenAddress, tokenContract);
107
+ async init(transactionSender) {
108
+ (0, fast_signer_1.patchEthersV5Signer)();
109
+ this.provider = new ethers_compat_1.ethersCompat.JsonRpcProvider(this.chainConfig.rpcEndpoint);
110
+ if (transactionSender) {
111
+ this.transactionSender = transactionSender;
41
112
  }
42
- return this.tokenContracts.get(tokenAddress);
43
- }
44
- getGasPriceGwei(context) {
45
- let { evm_gas_limit, evm_gas_price_gwei, evm_tip_amount_gwei } = context.trade_runtime.settings.strategy;
46
- if (evm_gas_price_gwei === undefined || evm_gas_price_gwei === null || evm_gas_price_gwei <= 0) {
47
- evm_gas_price_gwei = this.chainConfig.gasOptions.defaultGasPriceGwei;
113
+ const groupId = this.appConfig.trade_runtime?.group?.id;
114
+ if (!groupId) {
115
+ throw new Error('trade_runtime.group.id 未初始化,请确保 init_trade_runtime() 已调用');
48
116
  }
49
- (0, dist_1.log_info)(`getGasPriceGwei: ${evm_gas_price_gwei} Gwei`);
50
- return evm_gas_price_gwei.toString();
51
- }
52
- getBuilderTipAmoutGwei(context) {
53
- let { evm_gas_limit, evm_gas_price_gwei, evm_tip_amount_gwei } = context.trade_runtime.settings.strategy;
54
- if (evm_tip_amount_gwei === undefined || evm_tip_amount_gwei === null || evm_tip_amount_gwei <= 0) {
55
- evm_tip_amount_gwei = this.chainConfig.gasOptions.defaultTipAmountGwei;
117
+ const callerGroupIds = this.scanCallerFiles(groupId);
118
+ if (callerGroupIds.length === 0) {
119
+ throw new Error(`未找到分组 ${groupId} 的 Caller 钱包文件(${groupId}-CALLER-*.json)`);
56
120
  }
57
- (0, dist_1.log_info)(`getGasTipAmoutGwei: ${evm_tip_amount_gwei} Gwei`);
58
- return evm_tip_amount_gwei.toString();
59
- }
60
- checkTradeTokenApprove(context, routerAddress) {
61
- return __awaiter(this, void 0, void 0, function* () {
62
- const { pool_info } = context;
63
- const { tokenA, tokenB } = pool_info;
64
- const router = routerAddress || this.dexConfig.routerAddress;
65
- yield Promise.all([tokenA, tokenB].map((_a) => __awaiter(this, [_a], void 0, function* ({ symbol, address }) {
66
- const tokenContract = this.getTokenContract(address);
67
- yield this.checkTokenApprove(address, symbol, tokenContract, router);
68
- })));
121
+ this.callerManager = new caller_manager_1.CallerManager({
122
+ chainName: this.chainNameLower,
123
+ groupId,
124
+ provider: this.provider,
125
+ callerGroupIds,
126
+ chainId: this.chainConfig.chainId,
69
127
  });
128
+ await this.callerManager.init();
129
+ logger.info(`AbstractDexTrade initialized, vault=${this.tradeConfig.vaultAddress}, callers=${this.callerManager.getCallerCount()}`);
70
130
  }
71
- checkTokenApprove(tokenAddress, tokenSymbol, tokenContract, spenderAddress) {
72
- return __awaiter(this, void 0, void 0, function* () {
73
- if (this.approvedTokens.get(tokenAddress)) {
74
- (0, dist_1.log_info)(`代币 ${tokenSymbol} 已授权,跳过授权检查`);
75
- return true;
76
- }
77
- const allowance = yield tokenContract.allowance(this.wallet.address, spenderAddress);
78
- const maxAllowance = ethers_1.ethers.constants.MaxUint256.div(2);
79
- (0, dist_1.log_info)(`代币授权检查:`, {
80
- tokenSymbol,
81
- currentAllowance: allowance.toString(),
82
- maxAllowance: maxAllowance.toString(),
83
- isSufficient: allowance.gte(maxAllowance)
84
- });
85
- if (allowance.lt(maxAllowance)) {
86
- const maxApprovalAmount = ethers_1.ethers.utils.formatUnits(ethers_1.ethers.constants.MaxUint256, yield tokenContract.decimals());
87
- const tokenSymbolDisplay = tokenSymbol || `${tokenAddress.substring(0, 6)}...`;
88
- (0, dist_1.log_info)(`正在为合约授权代币 ${tokenSymbolDisplay}`, {
89
- tokenAddress,
90
- tokenSymbol: tokenSymbolDisplay,
91
- currentAllowance: ethers_1.ethers.utils.formatUnits(allowance, yield tokenContract.decimals()),
92
- newAllowance: maxApprovalAmount
93
- });
94
- const tx = yield tokenContract.approve(spenderAddress, ethers_1.ethers.constants.MaxUint256, {
95
- gasLimit: this.chainConfig.gasOptions.gasLimit
96
- });
97
- yield tx.wait();
98
- this.approvedTokens.set(tokenAddress, true);
99
- (0, dist_1.log_info)(`代币 ${tokenSymbolDisplay} 授权完成,数量: ${maxApprovalAmount} ${tokenSymbolDisplay}, 交易哈希: ${tx.hash}`);
100
- return true;
131
+ async execute(context) {
132
+ const { price_msg, order_msg, slippage_bps, order_trace_id, pool_info } = context;
133
+ const { pair } = price_msg;
134
+ const { isBuy, amount } = order_msg;
135
+ const direction = isBuy ? 'BUY' : 'SELL';
136
+ const { inputToken, outputToken } = this.determineInputOutputTokens(order_msg, pool_info);
137
+ const amountOutMin = this.calculateAmountOutMin(context, inputToken, outputToken);
138
+ context._precomputed = { inputToken, outputToken, amountOutMin };
139
+ const trace = new trade_trace_1.TradeTrace(order_trace_id, pair, direction);
140
+ trace.set('input', `${amount} ${inputToken.symbol}`);
141
+ trace.set('outputMin', `${ethers_compat_1.ethersCompat.formatUnits(amountOutMin, outputToken.decimals)} ${outputToken.symbol}`);
142
+ trace.set('slippage', `${slippage_bps / 100}%`);
143
+ const maxAttempts = 3;
144
+ const { wallet: caller, nonce: initialNonce } = await this.callerManager.acquireCaller();
145
+ trace.mark('caller');
146
+ trace.set('caller', caller.address);
147
+ let nonce = initialNonce;
148
+ let nonce_from_error = -1;
149
+ let txid = '';
150
+ let i = 1;
151
+ do {
152
+ try {
153
+ if (i > 1) {
154
+ if (nonce_from_error >= 0) {
155
+ nonce = nonce_from_error;
156
+ nonce_from_error = -1;
157
+ }
158
+ else {
159
+ nonce = await this.provider.getTransactionCount(caller.address, 'pending');
160
+ }
161
+ trace.set('retry', `${i}/${maxAttempts}`);
162
+ }
163
+ const { executorId, data } = this.encodeTradeData(context);
164
+ trace.mark('encode');
165
+ const deadline = Math.floor(Date.now() / 1000) + 60;
166
+ const vaultCalldataNoTip = this.vaultInterface.encodeFunctionData('delegatecallExecutorToTrade', [executorId, data, deadline, 0, constants_1.NATIVE_ETH_ADDRESS]);
167
+ const { mainGasFields } = this.buildGasFields(context);
168
+ const gasLimit = this.getGasLimit(context);
169
+ const mainTx = {
170
+ to: this.tradeConfig.vaultAddress,
171
+ data: vaultCalldataNoTip,
172
+ gasLimit,
173
+ ...mainGasFields,
174
+ nonce,
175
+ chainId: this.chainConfig.chainId,
176
+ value: 0,
177
+ };
178
+ const env = this.appConfig.env_args;
179
+ const tipChannelTxs = [];
180
+ if (env.send_tx_blockrazor && env.blockrazor_tip_amount_wei && env.blockrazor_tip_amount_wei > 0) {
181
+ const tipAmountWei = BigInt(env.blockrazor_tip_amount_wei);
182
+ const tipReceiver = env.blockrazor_tip_address || BLOCKRAZOR_DEFAULT_TIP_ADDRESS;
183
+ const vaultCalldataWithTip = this.vaultInterface.encodeFunctionData('delegatecallExecutorToTrade', [executorId, data, deadline, tipAmountWei.toString(), tipReceiver]);
184
+ tipChannelTxs.push({
185
+ channel: BLOCKRAZOR_CHANNEL_NAME,
186
+ tx: {
187
+ to: this.tradeConfig.vaultAddress,
188
+ data: vaultCalldataWithTip,
189
+ gasLimit,
190
+ ...mainGasFields,
191
+ nonce,
192
+ chainId: this.chainConfig.chainId,
193
+ value: tipAmountWei.toString(),
194
+ },
195
+ });
196
+ context.ui_tip_amount = new decimal_js_1.default(tipAmountWei.toString()).div(1e18).toNumber();
197
+ }
198
+ else {
199
+ context.ui_tip_amount = 0;
200
+ }
201
+ const [rawMainTx, ...rawTips] = await Promise.all([
202
+ caller.signTransaction(mainTx),
203
+ ...tipChannelTxs.map(({ tx }) => caller.signTransaction(tx)),
204
+ ]);
205
+ trace.mark('sign');
206
+ const ensure0x = (tx) => tx.startsWith('0x') ? tx : `0x${tx}`;
207
+ const signedMainTx = ensure0x(rawMainTx);
208
+ const signedTips = rawTips.map(ensure0x);
209
+ txid = ethers_compat_1.ethersCompat.keccak256(signedMainTx);
210
+ const tipTxMap = new Map();
211
+ tipChannelTxs.forEach(({ channel }, idx) => tipTxMap.set(channel, signedTips[idx]));
212
+ trace.set('txid', txid);
213
+ trace.set('nonce', nonce);
214
+ trace.set('gasType', mainGasFields.type === 2 ? 'EIP1559' : 'Legacy');
215
+ trace.set('tip', context.ui_tip_amount);
216
+ const only_bundle = order_msg.is_dex_maker;
217
+ trace.mark('sent');
218
+ await this.transactionSender.sendTransaction(signedMainTx, tipTxMap, order_trace_id, pair, only_bundle, trace);
219
+ trace.mark('send_complete');
220
+ trace.flush();
221
+ context._execution_marks = trace.getAbsoluteMarks();
222
+ context._execution_start_time = trace.getStartTime();
223
+ try {
224
+ abstract_tx_result_checker_1.TradeResultSubscriber.getInstance().sendNonceWatch(caller.address, txid);
225
+ }
226
+ catch { }
227
+ return txid;
101
228
  }
102
- else {
103
- this.approvedTokens.set(tokenAddress, true);
104
- return true;
229
+ catch (error) {
230
+ const errorMessage = error.message?.toLowerCase() || '';
231
+ if (errorMessage.includes('rate limit')) {
232
+ trace.markError('send', `rate limit, attempt ${i}`);
233
+ trace.flush();
234
+ return txid;
235
+ }
236
+ if (errorMessage.includes('replacement transaction underpriced')) {
237
+ trace.set('underpriced_retry', i);
238
+ continue;
239
+ }
240
+ const isNonceError = errorMessage.includes('nonce');
241
+ if (!isNonceError || i >= maxAttempts) {
242
+ trace.markError('send', `${errorMessage}, attempt ${i}/${maxAttempts}`);
243
+ trace.flush();
244
+ return txid;
245
+ }
246
+ if (errorMessage.includes('nonce too')) {
247
+ const correctNonce = this.extractNonceFromErrorMsg(errorMessage);
248
+ if (correctNonce !== null && correctNonce >= 0) {
249
+ nonce_from_error = correctNonce;
250
+ }
251
+ }
105
252
  }
106
- });
253
+ } while (++i <= maxAttempts);
254
+ if (!txid) {
255
+ trace.markError('exhaust', `failed after ${maxAttempts} attempts`);
256
+ trace.flush();
257
+ const groupId = this.appConfig.trade_runtime?.group?.id;
258
+ ttd_core_1.ALERT_TYPES.TRADE_BUILDER_REJECTED.report({
259
+ identity: caller.address.toLowerCase(),
260
+ scope: { caller_address: caller.address.toLowerCase(), group_id: groupId },
261
+ title: `Trade execution exhausted after ${maxAttempts} attempts (order ${order_trace_id})`,
262
+ detail: { order_trace_id, attempts: maxAttempts, last_nonce: nonce, last_error_nonce: nonce_from_error },
263
+ });
264
+ throw new Error(`交易执行失败,已重试 ${maxAttempts} 次,orderId: ${order_trace_id}`);
265
+ }
266
+ return txid;
267
+ }
268
+ scanCallerFiles(groupId) {
269
+ const walletDir = this.appConfig.env_args.wallet_dir || path.join((0, ttd_core_1.home_dir)(), 'data', 'keypairs');
270
+ const prefix = `${groupId}-CALLER-`;
271
+ const files = fs.readdirSync(walletDir)
272
+ .filter(f => f.startsWith(prefix) && f.endsWith('.json'))
273
+ .sort();
274
+ return files.map(f => f.replace('.json', ''));
275
+ }
276
+ computeEvmGasArgs(context) {
277
+ const env = this.appConfig.env_args;
278
+ const defaults = {
279
+ gasLimit: env.gas_limit,
280
+ gasPriceGwei: env.gas_price_gwei,
281
+ tipAmountGwei: env.tip_amount_gwei,
282
+ };
283
+ const caps = {
284
+ gasLimit: env.gas_max_limit,
285
+ gasPriceGwei: env.gas_max_price_gwei,
286
+ maxFeePerGasGwei: env.gas_max_fee_per_gas_gwei,
287
+ maxPriorityFeePerGasGwei: env.gas_max_priority_fee_gwei,
288
+ tipAmountGwei: env.gas_max_tip_amount_gwei,
289
+ };
290
+ return (0, ttd_core_1.buildEvmGasArgs)(context.trade_runtime.settings.strategy, defaults, caps);
107
291
  }
108
- isNonceRelatedError(error) {
109
- if (!error)
110
- return false;
111
- const errorMessage = error.message ? error.message.toLowerCase() : '';
112
- const nonceErrorKeywords = [
113
- 'nonce',
114
- 'nonce too low',
115
- 'nonce too high',
116
- 'nonce has already been used',
117
- 'already known',
118
- 'replacement transaction underpriced',
119
- 'transaction with same nonce',
120
- 'transaction nonce is too low',
121
- 'invalid transaction nonce',
122
- 'insufficient funds',
123
- 'sign bundle to get failure details',
124
- ];
125
- return nonceErrorKeywords.some(keyword => errorMessage.includes(keyword));
126
- }
127
- isNativeCurrency(symbol) {
128
- return symbol.toUpperCase() === this.chainConfig.nativeCurrency;
129
- }
130
- getWrappedNativeAddress() {
131
- return this.chainConfig.wrappedNativeCurrencyAddress;
132
- }
133
- buildTipTransferTx(to, transfer_amount_gwei, gas_price_gwei, transfer_nonce) {
134
- return __awaiter(this, void 0, void 0, function* () {
135
- let real_transfer_amount_gwei = Math.min(Number(transfer_amount_gwei), this.chainConfig.gasOptions.maxTipAmountGwei).toString();
136
- let real_gas_price_gwei = Math.min(Number(gas_price_gwei), this.chainConfig.gasOptions.maxGasPriceGwei).toString();
137
- let tx_data = {
138
- from: this.wallet.address,
139
- to,
140
- value: ethers_1.ethers.utils.parseUnits(real_transfer_amount_gwei, 'gwei'),
141
- gasLimit: 21000,
142
- gasPrice: ethers_1.ethers.utils.parseUnits(real_gas_price_gwei, 'gwei'),
143
- nonce: transfer_nonce,
144
- chainId: this.chainConfig.chainId
292
+ gasArgsToMainFields(args) {
293
+ if (args.txType === 2) {
294
+ return {
295
+ type: 2,
296
+ maxFeePerGas: ethers_compat_1.ethersCompat.parseUnits(args.maxFeePerGasGwei.toString(), 'gwei'),
297
+ maxPriorityFeePerGas: ethers_compat_1.ethersCompat.parseUnits(args.maxPriorityFeePerGasGwei.toString(), 'gwei'),
145
298
  };
146
- let signedTx = yield this.wallet.signTransaction(tx_data);
147
- (0, dist_1.log_info)(`构建转账交易: `, Object.assign(Object.assign({}, tx_data), { real_transfer_amount_gwei,
148
- real_gas_price_gwei, txhash: ethers_1.ethers.utils.keccak256(signedTx) }));
149
- return signedTx;
150
- });
299
+ }
300
+ return {
301
+ gasPrice: ethers_compat_1.ethersCompat.parseUnits(args.gasPriceGwei.toString(), 'gwei'),
302
+ };
303
+ }
304
+ buildGasFields(context) {
305
+ const args = this.computeEvmGasArgs(context);
306
+ return {
307
+ mainGasFields: this.gasArgsToMainFields(args),
308
+ tipGasPriceGwei: args.tipGasPriceGwei.toString(),
309
+ };
310
+ }
311
+ getGasLimit(context) {
312
+ return this.computeEvmGasArgs(context).gasLimit;
313
+ }
314
+ getLegacyGasPriceGwei(context) {
315
+ return this.computeEvmGasArgs(context).tipGasPriceGwei.toString();
316
+ }
317
+ getBuilderTipAmoutGwei(context) {
318
+ return this.computeEvmGasArgs(context).tipAmountGwei.toString();
319
+ }
320
+ async buildTipTransferTx(to, transfer_amount_gwei, gas_price_gwei, nonce, wallet) {
321
+ const real_transfer_amount_gwei = Math.min(Number(transfer_amount_gwei), this.chainConfig.gasOptions.maxTipAmountGwei).toString();
322
+ const real_gas_price_gwei = Math.min(Number(gas_price_gwei), this.chainConfig.gasOptions.maxGasPriceGwei).toString();
323
+ const tx_data = {
324
+ to,
325
+ value: ethers_compat_1.ethersCompat.parseUnits(real_transfer_amount_gwei, 'gwei'),
326
+ gasLimit: 21000,
327
+ gasPrice: ethers_compat_1.ethersCompat.parseUnits(real_gas_price_gwei, 'gwei'),
328
+ nonce,
329
+ chainId: this.chainConfig.chainId
330
+ };
331
+ const rawSignedTx = await wallet.signTransaction(tx_data);
332
+ const signedTx = rawSignedTx.startsWith('0x') ? rawSignedTx : `0x${rawSignedTx}`;
333
+ return signedTx;
334
+ }
335
+ determineInputOutputTokens(order_msg, pool_info) {
336
+ const { inputToken, outputToken } = (0, trade_direction_1.resolveTradeDirection)(pool_info, order_msg.isBuy);
337
+ return { inputToken, outputToken };
338
+ }
339
+ calculateAmountOutMin(context, _inputToken, outputToken) {
340
+ const { price_msg, slippage_bps, order_msg } = context;
341
+ const { isBuy } = order_msg;
342
+ const slippage = slippage_bps / 10000;
343
+ const inputAmount = new decimal_js_1.default(order_msg.amount.toString());
344
+ let expectedOut;
345
+ if (isBuy) {
346
+ const price = new decimal_js_1.default(price_msg.ask.price);
347
+ expectedOut = inputAmount.div(price);
348
+ }
349
+ else {
350
+ const price = new decimal_js_1.default(price_msg.bid.price);
351
+ expectedOut = inputAmount.mul(price);
352
+ }
353
+ const minOutput = expectedOut.mul(new decimal_js_1.default(1).sub(new decimal_js_1.default(slippage)));
354
+ return ethers_compat_1.ethersCompat.parseUnits(minOutput.toFixed(outputToken.decimals), outputToken.decimals);
355
+ }
356
+ extractNonceFromErrorMsg(errorMessage) {
357
+ try {
358
+ const match = errorMessage.match(/tx:\s*(\d+)\s*state:\s*(\d+)/);
359
+ if (match) {
360
+ return parseInt(match[2]);
361
+ }
362
+ }
363
+ catch { }
364
+ return null;
151
365
  }
152
366
  }
153
- exports.AbstractEvmDexTrade = AbstractEvmDexTrade;
367
+ exports.AbstractDexTrade = AbstractDexTrade;
@@ -0,0 +1,31 @@
1
+ import { ethers } from "ethers";
2
+ export interface CallerHandle {
3
+ wallet: ethers.Wallet;
4
+ nonce: number;
5
+ }
6
+ export interface CallerManagerConfig {
7
+ chainName: string;
8
+ groupId: string;
9
+ provider: any;
10
+ callerGroupIds: string[];
11
+ chainId: number;
12
+ }
13
+ export declare class CallerManager {
14
+ private callers;
15
+ private redis;
16
+ private config;
17
+ constructor(config: CallerManagerConfig);
18
+ init(): Promise<void>;
19
+ acquireCaller(): Promise<CallerHandle>;
20
+ private acquireCallerWithLock;
21
+ private acquireCallerWithLuaCas;
22
+ private readNonce;
23
+ confirmNonce(address: string, confirmedNonce: number): Promise<void>;
24
+ forceSetNonce(address: string, nonce: number): Promise<void>;
25
+ getCallerCount(): number;
26
+ getCallerAddresses(): string[];
27
+ private getNonceRedisKey;
28
+ private getLastUsedRedisKey;
29
+ private getNonce;
30
+ private setNonce;
31
+ }