@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.
- package/dist/appconfig/BaseQuoteAppConfig.d.ts +10 -0
- package/dist/appconfig/BaseQuoteAppConfig.js +36 -0
- package/dist/appconfig/BaseTradeAppConfig.d.ts +7 -0
- package/dist/appconfig/BaseTradeAppConfig.js +13 -0
- package/dist/appconfig/base_dex_env_args.d.ts +5 -0
- package/dist/appconfig/base_dex_env_args.js +68 -0
- package/dist/appconfig/base_env_args.d.ts +82 -0
- package/dist/appconfig/base_env_args.js +91 -0
- package/dist/appconfig/ensure_core_env.d.ts +1 -0
- package/dist/appconfig/ensure_core_env.js +18 -0
- package/dist/appconfig/index.d.ts +5 -0
- package/dist/appconfig/index.js +21 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/quote/depth/amm_depth_calculator.d.ts +19 -0
- package/dist/quote/depth/amm_depth_calculator.js +55 -0
- package/dist/quote/depth/clmm_depth_calculator.d.ts +28 -0
- package/dist/quote/depth/clmm_depth_calculator.js +176 -0
- package/dist/quote/depth/index.d.ts +51 -0
- package/dist/quote/depth/index.js +264 -0
- package/dist/quote/depth/tick_liquidity_snapshot.d.ts +58 -0
- package/dist/quote/depth/tick_liquidity_snapshot.js +143 -0
- package/dist/quote/event/index.d.ts +1 -0
- package/dist/quote/event/index.js +1 -0
- package/dist/quote/event/pool_event_listener.d.ts +5 -3
- package/dist/quote/event/pool_event_listener.js +128 -150
- package/dist/quote/event/swap_debouncer.d.ts +22 -0
- package/dist/quote/event/swap_debouncer.js +80 -0
- package/dist/quote/get_base_token_price.d.ts +6 -0
- package/dist/quote/get_base_token_price.js +90 -0
- package/dist/quote/index.d.ts +7 -0
- package/dist/quote/index.js +7 -0
- package/dist/quote/preload_token_prices.d.ts +2 -0
- package/dist/quote/preload_token_prices.js +37 -0
- package/dist/quote/price_feed_handler.d.ts +15 -0
- package/dist/quote/price_feed_handler.js +56 -0
- package/dist/quote/pricing/fee_helpers.d.ts +13 -0
- package/dist/quote/pricing/fee_helpers.js +68 -0
- package/dist/quote/pricing/index.d.ts +3 -1
- package/dist/quote/pricing/index.js +3 -1
- package/dist/quote/pricing/pool_state_initializer.d.ts +12 -0
- package/dist/quote/pricing/pool_state_initializer.js +191 -0
- package/dist/quote/pricing/sdk_token_factory.d.ts +2 -0
- package/dist/quote/pricing/sdk_token_factory.js +21 -0
- package/dist/quote/quote_amount.d.ts +4 -0
- package/dist/quote/quote_amount.js +24 -0
- package/dist/quote/tick/cached_tick_data_provider.d.ts +12 -0
- package/dist/quote/tick/cached_tick_data_provider.js +45 -0
- package/dist/quote/tick/clmm_tick_cache.d.ts +42 -0
- package/dist/quote/tick/clmm_tick_cache.js +236 -0
- package/dist/quote/tick/index.d.ts +4 -0
- package/dist/{ws → quote/tick}/index.js +4 -2
- package/dist/quote/tick/state_view_tick_loader.d.ts +17 -0
- package/dist/quote/tick/state_view_tick_loader.js +136 -0
- package/dist/quote/tick/tick_lens_loaders.d.ts +24 -0
- package/dist/quote/tick/tick_lens_loaders.js +158 -0
- package/dist/quote/verify/index.d.ts +2 -0
- package/dist/quote/verify/index.js +5 -0
- package/dist/quote/verify/quote_price_verify.d.ts +30 -0
- package/dist/quote/verify/quote_price_verify.js +240 -0
- package/dist/redis/redis_client.d.ts +3 -2
- package/dist/redis/redis_client.js +86 -116
- package/dist/send-tx/constants.d.ts +2 -0
- package/dist/send-tx/constants.js +6 -0
- package/dist/send-tx/index.d.ts +2 -0
- package/dist/{config → send-tx}/index.js +2 -1
- package/dist/send-tx/types.d.ts +4 -0
- package/dist/send-tx/types.js +2 -0
- package/dist/trade/abstract_dex_trade.d.ts +43 -21
- package/dist/trade/abstract_dex_trade.js +347 -133
- package/dist/trade/caller_manager.d.ts +31 -0
- package/dist/trade/caller_manager.js +202 -0
- package/dist/trade/check/abstract_tx_result_checker.d.ts +28 -0
- package/dist/trade/check/abstract_tx_result_checker.js +192 -0
- package/dist/trade/check/index.d.ts +1 -1
- package/dist/trade/check/index.js +1 -1
- package/dist/trade/index.d.ts +2 -2
- package/dist/trade/index.js +2 -2
- package/dist/trade/parse/base_parser.d.ts +1 -2
- package/dist/trade/parse/base_parser.js +36 -36
- package/dist/trade/trade_trace.d.ts +17 -0
- package/dist/trade/trade_trace.js +65 -0
- package/dist/types/config_types.d.ts +3 -3
- package/dist/types/event_types.d.ts +3 -3
- package/dist/types/pool_state.d.ts +140 -13
- package/dist/utils/ethers_compat.d.ts +13 -0
- package/dist/utils/ethers_compat.js +18 -0
- package/dist/utils/fast_signer.d.ts +1 -0
- package/dist/utils/fast_signer.js +87 -0
- package/dist/utils/gas_helper.d.ts +2 -2
- package/dist/utils/gas_helper.js +48 -60
- package/dist/utils/index.d.ts +5 -2
- package/dist/utils/index.js +6 -2
- package/dist/utils/pool_filter.d.ts +8 -0
- package/dist/utils/pool_filter.js +38 -0
- package/dist/utils/trade_direction.d.ts +14 -0
- package/dist/utils/trade_direction.js +23 -0
- package/package.json +2 -2
- package/dist/config/base_env_args.d.ts +0 -11
- package/dist/config/base_env_args.js +0 -19
- package/dist/config/index.d.ts +0 -1
- package/dist/quote/event/verify_clmm_swap_event.d.ts +0 -1
- package/dist/quote/event/verify_clmm_swap_event.js +0 -178
- package/dist/quote/pricing/token_price_cache.d.ts +0 -10
- package/dist/quote/pricing/token_price_cache.js +0 -40
- package/dist/trade/abstract_dex_trade_plus.d.ts +0 -43
- package/dist/trade/abstract_dex_trade_plus.js +0 -421
- package/dist/trade/check/tx_websocket_manager.d.ts +0 -23
- package/dist/trade/check/tx_websocket_manager.js +0 -119
- package/dist/trade/send/alchemy_base.d.ts +0 -5
- package/dist/trade/send/alchemy_base.js +0 -48
- package/dist/trade/send/ankr_base.d.ts +0 -5
- package/dist/trade/send/ankr_base.js +0 -48
- package/dist/trade/send/base_rpc.d.ts +0 -5
- package/dist/trade/send/base_rpc.js +0 -48
- package/dist/trade/send/blockpi_base.d.ts +0 -5
- package/dist/trade/send/blockpi_base.js +0 -48
- package/dist/trade/send/bloxroute_base.d.ts +0 -11
- package/dist/trade/send/bloxroute_base.js +0 -115
- package/dist/trade/send/chainstack_base.d.ts +0 -5
- package/dist/trade/send/chainstack_base.js +0 -48
- package/dist/trade/send/drpc_base.d.ts +0 -5
- package/dist/trade/send/drpc_base.js +0 -48
- package/dist/trade/send/getblock_base.d.ts +0 -5
- package/dist/trade/send/getblock_base.js +0 -48
- package/dist/trade/send/index.d.ts +0 -15
- package/dist/trade/send/index.js +0 -33
- package/dist/trade/send/infura_base.d.ts +0 -5
- package/dist/trade/send/infura_base.js +0 -48
- package/dist/trade/send/moralis_base.d.ts +0 -5
- package/dist/trade/send/moralis_base.js +0 -48
- package/dist/trade/send/onerpc_base.d.ts +0 -5
- package/dist/trade/send/onerpc_base.js +0 -48
- package/dist/trade/send/quicknode_base.d.ts +0 -5
- package/dist/trade/send/quicknode_base.js +0 -48
- package/dist/trade/send/send_tx.d.ts +0 -17
- package/dist/trade/send/send_tx.js +0 -163
- package/dist/ws/event_filter.d.ts +0 -8
- package/dist/ws/event_filter.js +0 -36
- package/dist/ws/index.d.ts +0 -2
- package/dist/ws/subscribe_v2_events.d.ts +0 -14
- package/dist/ws/subscribe_v2_events.js +0 -174
- package/dist/ws/subscribe_v3_events.d.ts +0 -14
- package/dist/ws/subscribe_v3_events.js +0 -174
|
@@ -1,153 +1,367 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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.
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
109
|
-
if (
|
|
110
|
-
return
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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.
|
|
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
|
+
}
|