@clonegod/ttd-bsc-common 3.1.83 → 3.1.88
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/bsc_env_args.d.ts +1 -0
- package/dist/appconfig/bsc_env_args.js +1 -0
- package/dist/config/bsc_env_args.d.ts +11 -0
- package/dist/config/bsc_env_args.js +19 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +17 -0
- package/dist/quote/event/verify_clmm_swap_event.d.ts +1 -0
- package/dist/quote/event/verify_clmm_swap_event.js +178 -0
- package/dist/quote/pricing/token_price_cache.d.ts +9 -0
- package/dist/quote/pricing/token_price_cache.js +40 -0
- package/dist/redis/index.d.ts +1 -0
- package/dist/redis/index.js +17 -0
- package/dist/redis/redis_client.d.ts +21 -0
- package/dist/redis/redis_client.js +155 -0
- package/dist/trade/abstract_dex_trade_plus.d.ts +30 -0
- package/dist/trade/abstract_dex_trade_plus.js +227 -0
- package/dist/trade/check/tx_websocket_manager.d.ts +23 -0
- package/dist/trade/check/tx_websocket_manager.js +119 -0
- package/dist/trade/send/48club.d.ts +18 -0
- package/dist/trade/send/48club.js +97 -0
- package/dist/trade/send/blockrazor.d.ts +7 -0
- package/dist/trade/send/blockrazor.js +79 -0
- package/dist/trade/send/bsc_rpc.d.ts +6 -0
- package/dist/trade/send/bsc_rpc.js +43 -0
- package/dist/trade/send/index.d.ts +5 -0
- package/dist/trade/send/index.js +23 -0
- package/dist/trade/send/send_tx.d.ts +8 -0
- package/dist/trade/send/send_tx.js +91 -0
- package/dist/ws/event_filter.d.ts +8 -0
- package/dist/ws/event_filter.js +36 -0
- package/dist/ws/index.d.ts +2 -0
- package/dist/ws/index.js +18 -0
- package/dist/ws/subscribe_v2_events.d.ts +14 -0
- package/dist/ws/subscribe_v2_events.js +174 -0
- package/dist/ws/subscribe_v3_events.d.ts +16 -0
- package/dist/ws/subscribe_v3_events.js +177 -0
- package/package.json +2 -2
- package/dist/quote/hook_fee_monitor.d.ts +0 -10
- package/dist/quote/hook_fee_monitor.js +0 -32
|
@@ -0,0 +1,227 @@
|
|
|
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
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AbstractEvmDexTradePlus = 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 AbstractEvmDexTradePlus extends dist_1.AbastrcatTrade {
|
|
17
|
+
constructor(appConfig) {
|
|
18
|
+
super();
|
|
19
|
+
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
|
+
this.initConfigs();
|
|
27
|
+
}
|
|
28
|
+
init() {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
var _a;
|
|
31
|
+
this.provider = new ethers_1.ethers.providers.JsonRpcProvider(this.chainConfig.rpcEndpoint);
|
|
32
|
+
if (!process.env.WALLET_GROUP_IDS) {
|
|
33
|
+
throw new Error('必须配置 WALLET_GROUP_IDS 环境变量来启用多钱包功能');
|
|
34
|
+
}
|
|
35
|
+
let wallet_infos = (0, dist_1.load_wallet_multi)(((_a = process.env.WALLET_GROUP_IDS) === null || _a === void 0 ? void 0 : _a.split(',')) || [], false);
|
|
36
|
+
this.group_wallets = wallet_infos.map(info => new ethers_1.ethers.Wallet(info.private_key, this.provider));
|
|
37
|
+
(0, dist_1.log_info)(`组钱包已初始化,数量: ${this.group_wallets.length}`, this.group_wallets.map(e => e.address));
|
|
38
|
+
this.routerContract = new ethers_1.ethers.Contract(this.dexConfig.routerAddress, this.dexConfig.routerAbi, this.provider);
|
|
39
|
+
(0, dist_1.log_info)(`${this.dexConfig.dexName} Router已初始化, 地址: ${this.dexConfig.routerAddress}, 钱包数量: ${this.group_wallets.length}`);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
getTokenContract(tokenAddress) {
|
|
43
|
+
if (!this.tokenContracts.has(tokenAddress)) {
|
|
44
|
+
const tokenContract = new ethers_1.ethers.Contract(tokenAddress, common_1.ERC20_ABI, this.provider);
|
|
45
|
+
this.tokenContracts.set(tokenAddress, tokenContract);
|
|
46
|
+
}
|
|
47
|
+
return this.tokenContracts.get(tokenAddress);
|
|
48
|
+
}
|
|
49
|
+
getTokenContractWithWallet(tokenAddress, wallet) {
|
|
50
|
+
const tokenContract = this.getTokenContract(tokenAddress);
|
|
51
|
+
return tokenContract.connect(wallet);
|
|
52
|
+
}
|
|
53
|
+
getGasPriceGwei(context) {
|
|
54
|
+
let { evm_gas_limit, evm_gas_price_gwei, evm_tip_amount_gwei } = context.trade_runtime.settings.strategy;
|
|
55
|
+
if (evm_gas_price_gwei === undefined || evm_gas_price_gwei === null || evm_gas_price_gwei <= 0) {
|
|
56
|
+
evm_gas_price_gwei = this.chainConfig.gasOptions.defaultGasPriceGwei;
|
|
57
|
+
}
|
|
58
|
+
(0, dist_1.log_info)(`getGasPriceGwei: ${evm_gas_price_gwei} Gwei`);
|
|
59
|
+
return evm_gas_price_gwei.toString();
|
|
60
|
+
}
|
|
61
|
+
getBuilderTipAmoutGwei(context) {
|
|
62
|
+
let { evm_gas_limit, evm_gas_price_gwei, evm_tip_amount_gwei } = context.trade_runtime.settings.strategy;
|
|
63
|
+
if (evm_tip_amount_gwei === undefined || evm_tip_amount_gwei === null || evm_tip_amount_gwei <= 0) {
|
|
64
|
+
evm_tip_amount_gwei = this.chainConfig.gasOptions.defaultTipAmountGwei;
|
|
65
|
+
}
|
|
66
|
+
(0, dist_1.log_info)(`getGasTipAmoutGwei: ${evm_tip_amount_gwei} Gwei`);
|
|
67
|
+
return evm_tip_amount_gwei.toString();
|
|
68
|
+
}
|
|
69
|
+
preApproveAllWallets() {
|
|
70
|
+
return __awaiter(this, arguments, void 0, function* (token_list = []) {
|
|
71
|
+
if (!this.group_wallets || this.group_wallets.length === 0) {
|
|
72
|
+
(0, dist_1.log_info)('No wallets available for pre-approval');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (!token_list || token_list.length === 0) {
|
|
76
|
+
token_list = Array.from(this.tokenContracts.keys());
|
|
77
|
+
}
|
|
78
|
+
(0, dist_1.log_info)(`Pre-approve: ${this.group_wallets.length} wallets, ${token_list.length} tokens`, token_list);
|
|
79
|
+
const tokenAddresses = new Set();
|
|
80
|
+
for (const token_address of token_list) {
|
|
81
|
+
tokenAddresses.add(token_address.toLowerCase());
|
|
82
|
+
}
|
|
83
|
+
const tokens = Array.from(tokenAddresses);
|
|
84
|
+
for (const wallet of this.group_wallets) {
|
|
85
|
+
try {
|
|
86
|
+
(0, dist_1.log_info)(`Pre-approve: wallet ${wallet.address}`);
|
|
87
|
+
for (const tokenAddress of tokens) {
|
|
88
|
+
try {
|
|
89
|
+
yield this.approveTokenIfNeeded(wallet, tokenAddress);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
console.warn(`Failed to pre-approve token ${tokenAddress} for wallet ${wallet.address}:`, error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
(0, dist_1.log_info)(`Pre-approve: wallet ${wallet.address} completed`);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.warn(`Failed to pre-approve tokens for wallet ${wallet.address}:`, error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
(0, dist_1.log_info)('Pre-approve: all wallets completed');
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
approveTokenIfNeeded(wallet, tokenAddress) {
|
|
105
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
106
|
+
try {
|
|
107
|
+
const tokenContract = this.getTokenContract(tokenAddress);
|
|
108
|
+
const routerAddress = this.dexConfig.routerAddress;
|
|
109
|
+
const currentAllowance = yield tokenContract.allowance(wallet.address, routerAddress);
|
|
110
|
+
if (currentAllowance.gt(0)) {
|
|
111
|
+
(0, dist_1.log_info)(`Pre-approve: token ${tokenAddress} already approved`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const maxAllowance = ethers_1.ethers.constants.MaxUint256;
|
|
115
|
+
const approveTx = yield tokenContract.connect(wallet).approve(routerAddress, maxAllowance);
|
|
116
|
+
(0, dist_1.log_info)(`Pre-approve: token ${tokenAddress} approve success, tx: ${approveTx.hash}`);
|
|
117
|
+
yield approveTx.wait();
|
|
118
|
+
(0, dist_1.log_info)(`Pre-approve: token ${tokenAddress} approved successfully`);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.warn(`Failed to approve token ${tokenAddress} for wallet ${wallet.address}:`, error);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
checkTradeTokenApprove(context, wallet, routerAddress) {
|
|
127
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
128
|
+
const { pool_info } = context;
|
|
129
|
+
const { tokenA, tokenB } = pool_info;
|
|
130
|
+
const router = routerAddress || this.dexConfig.routerAddress;
|
|
131
|
+
if (!this.approvedTokens.has(wallet.address)) {
|
|
132
|
+
this.approvedTokens.set(wallet.address, new Map());
|
|
133
|
+
}
|
|
134
|
+
yield Promise.all([tokenA, tokenB].map((_a) => __awaiter(this, [_a], void 0, function* ({ symbol, address }) {
|
|
135
|
+
const tokenContract = this.getTokenContractWithWallet(address, wallet);
|
|
136
|
+
yield this.checkTokenApprove(address, symbol, tokenContract, router, wallet);
|
|
137
|
+
})));
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
checkTokenApprove(tokenAddress, tokenSymbol, tokenContract, spenderAddress, wallet) {
|
|
141
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
if (!this.approvedTokens.has(wallet.address)) {
|
|
143
|
+
this.approvedTokens.set(wallet.address, new Map());
|
|
144
|
+
}
|
|
145
|
+
if (this.approvedTokens.get(wallet.address).get(tokenAddress)) {
|
|
146
|
+
(0, dist_1.log_info)(`钱包 ${wallet.address} 的代币 ${tokenSymbol} 已授权,跳过授权检查`);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
const allowance = yield tokenContract.allowance(wallet.address, spenderAddress);
|
|
150
|
+
const maxAllowance = ethers_1.ethers.constants.MaxUint256.div(2);
|
|
151
|
+
(0, dist_1.log_info)(`代币授权检查:`, {
|
|
152
|
+
walletAddress: wallet.address,
|
|
153
|
+
tokenSymbol,
|
|
154
|
+
currentAllowance: allowance.toString(),
|
|
155
|
+
maxAllowance: maxAllowance.toString(),
|
|
156
|
+
isSufficient: allowance.gte(maxAllowance)
|
|
157
|
+
});
|
|
158
|
+
if (allowance.lt(maxAllowance)) {
|
|
159
|
+
const maxApprovalAmount = ethers_1.ethers.utils.formatUnits(ethers_1.ethers.constants.MaxUint256, yield tokenContract.decimals());
|
|
160
|
+
const tokenSymbolDisplay = tokenSymbol || `${tokenAddress.substring(0, 6)}...`;
|
|
161
|
+
(0, dist_1.log_info)(`正在为钱包 ${wallet.address} 授权代币 ${tokenSymbolDisplay}`, {
|
|
162
|
+
walletAddress: wallet.address,
|
|
163
|
+
tokenAddress,
|
|
164
|
+
tokenSymbol: tokenSymbolDisplay,
|
|
165
|
+
currentAllowance: ethers_1.ethers.utils.formatUnits(allowance, yield tokenContract.decimals()),
|
|
166
|
+
newAllowance: maxApprovalAmount
|
|
167
|
+
});
|
|
168
|
+
const tx = yield tokenContract.approve(spenderAddress, ethers_1.ethers.constants.MaxUint256, {
|
|
169
|
+
gasLimit: this.chainConfig.gasOptions.gasLimit
|
|
170
|
+
});
|
|
171
|
+
yield tx.wait();
|
|
172
|
+
this.approvedTokens.get(wallet.address).set(tokenAddress, true);
|
|
173
|
+
(0, dist_1.log_info)(`钱包 ${wallet.address} 的代币 ${tokenSymbolDisplay} 授权完成,数量: ${maxApprovalAmount} ${tokenSymbolDisplay}, 交易哈希: ${tx.hash}`);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
this.approvedTokens.get(wallet.address).set(tokenAddress, true);
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
isNonceRelatedError(error) {
|
|
183
|
+
if (!error)
|
|
184
|
+
return false;
|
|
185
|
+
const errorMessage = error.message ? error.message.toLowerCase() : '';
|
|
186
|
+
const nonceErrorKeywords = [
|
|
187
|
+
'nonce',
|
|
188
|
+
'nonce too low',
|
|
189
|
+
'nonce too high',
|
|
190
|
+
'nonce has already been used',
|
|
191
|
+
'already known',
|
|
192
|
+
'replacement transaction underpriced',
|
|
193
|
+
'transaction with same nonce',
|
|
194
|
+
'transaction nonce is too low',
|
|
195
|
+
'invalid transaction nonce',
|
|
196
|
+
'insufficient funds',
|
|
197
|
+
'sign bundle to get failure details',
|
|
198
|
+
];
|
|
199
|
+
return nonceErrorKeywords.some(keyword => errorMessage.includes(keyword));
|
|
200
|
+
}
|
|
201
|
+
isNativeCurrency(symbol) {
|
|
202
|
+
return symbol.toUpperCase() === this.chainConfig.nativeCurrency;
|
|
203
|
+
}
|
|
204
|
+
getWrappedNativeAddress() {
|
|
205
|
+
return this.chainConfig.wrappedNativeCurrencyAddress;
|
|
206
|
+
}
|
|
207
|
+
buildTipTransferTx(to, transfer_amount_gwei, gas_price_gwei, transfer_nonce, wallet) {
|
|
208
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
209
|
+
let real_transfer_amount_gwei = Math.min(Number(transfer_amount_gwei), this.chainConfig.gasOptions.maxTipAmountGwei).toString();
|
|
210
|
+
let real_gas_price_gwei = Math.min(Number(gas_price_gwei), this.chainConfig.gasOptions.maxGasPriceGwei).toString();
|
|
211
|
+
let tx_data = {
|
|
212
|
+
from: wallet.address,
|
|
213
|
+
to,
|
|
214
|
+
value: ethers_1.ethers.utils.parseUnits(real_transfer_amount_gwei, 'gwei'),
|
|
215
|
+
gasLimit: 21000,
|
|
216
|
+
gasPrice: ethers_1.ethers.utils.parseUnits(real_gas_price_gwei, 'gwei'),
|
|
217
|
+
nonce: transfer_nonce,
|
|
218
|
+
chainId: this.chainConfig.chainId
|
|
219
|
+
};
|
|
220
|
+
let signedTx = yield wallet.signTransaction(tx_data);
|
|
221
|
+
(0, dist_1.log_info)(`构建转账交易: `, Object.assign(Object.assign({}, tx_data), { real_transfer_amount_gwei,
|
|
222
|
+
real_gas_price_gwei, txhash: ethers_1.ethers.utils.keccak256(signedTx) }));
|
|
223
|
+
return signedTx;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
exports.AbstractEvmDexTradePlus = AbstractEvmDexTradePlus;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
export declare class WebSocketManager {
|
|
3
|
+
private static instance;
|
|
4
|
+
private wsProvider;
|
|
5
|
+
private wsConnections;
|
|
6
|
+
private reconnectAttempts;
|
|
7
|
+
private readonly MAX_RECONNECT_ATTEMPTS;
|
|
8
|
+
private readonly RECONNECT_DELAY;
|
|
9
|
+
private reconnecting;
|
|
10
|
+
private eventListeners;
|
|
11
|
+
private constructor();
|
|
12
|
+
static getInstance(): WebSocketManager;
|
|
13
|
+
getWsProvider(wsEndpoint: string, network: {
|
|
14
|
+
name: string;
|
|
15
|
+
chainId: number;
|
|
16
|
+
}): ethers.providers.WebSocketProvider | null;
|
|
17
|
+
private handleWsError;
|
|
18
|
+
private handleWsDisconnection;
|
|
19
|
+
incrementConnections(): void;
|
|
20
|
+
decrementConnections(): void;
|
|
21
|
+
listenToEvent(eventName: string, handler: Function): void;
|
|
22
|
+
removeEventListener(eventName: string, handler: Function): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebSocketManager = void 0;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
6
|
+
class WebSocketManager {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.wsProvider = null;
|
|
9
|
+
this.wsConnections = 0;
|
|
10
|
+
this.reconnectAttempts = 0;
|
|
11
|
+
this.MAX_RECONNECT_ATTEMPTS = 5;
|
|
12
|
+
this.RECONNECT_DELAY = 2000;
|
|
13
|
+
this.reconnecting = false;
|
|
14
|
+
this.eventListeners = new Map();
|
|
15
|
+
}
|
|
16
|
+
static getInstance() {
|
|
17
|
+
if (!WebSocketManager.instance) {
|
|
18
|
+
WebSocketManager.instance = new WebSocketManager();
|
|
19
|
+
}
|
|
20
|
+
return WebSocketManager.instance;
|
|
21
|
+
}
|
|
22
|
+
getWsProvider(wsEndpoint, network) {
|
|
23
|
+
if (!wsEndpoint) {
|
|
24
|
+
(0, dist_1.log_warn)('WebSocket endpoint not configured');
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (!this.wsProvider && !this.reconnecting) {
|
|
28
|
+
(0, dist_1.log_info)('Creating new WebSocket provider');
|
|
29
|
+
try {
|
|
30
|
+
this.wsProvider = new ethers_1.ethers.providers.WebSocketProvider(wsEndpoint, network);
|
|
31
|
+
this.wsProvider.on('error', (error) => {
|
|
32
|
+
this.handleWsError(error, wsEndpoint, network);
|
|
33
|
+
});
|
|
34
|
+
this.wsProvider._websocket.on('close', () => {
|
|
35
|
+
(0, dist_1.log_warn)('WebSocket connection closed');
|
|
36
|
+
this.handleWsDisconnection(wsEndpoint, network);
|
|
37
|
+
});
|
|
38
|
+
this.reconnectAttempts = 0;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
(0, dist_1.log_error)('Failed to create WebSocket provider', error);
|
|
42
|
+
this.wsProvider = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return this.wsProvider;
|
|
46
|
+
}
|
|
47
|
+
handleWsError(error, wsEndpoint, network) {
|
|
48
|
+
(0, dist_1.log_error)('WebSocket provider error', error);
|
|
49
|
+
this.handleWsDisconnection(wsEndpoint, network);
|
|
50
|
+
}
|
|
51
|
+
handleWsDisconnection(wsEndpoint, network) {
|
|
52
|
+
if (this.reconnecting)
|
|
53
|
+
return;
|
|
54
|
+
if (this.wsProvider) {
|
|
55
|
+
this.wsProvider.removeAllListeners();
|
|
56
|
+
this.wsProvider = null;
|
|
57
|
+
}
|
|
58
|
+
if (this.wsConnections > 0 &&
|
|
59
|
+
this.reconnectAttempts < this.MAX_RECONNECT_ATTEMPTS) {
|
|
60
|
+
this.reconnecting = true;
|
|
61
|
+
this.reconnectAttempts++;
|
|
62
|
+
(0, dist_1.log_info)(`Attempting to reconnect WebSocket (${this.reconnectAttempts}/${this.MAX_RECONNECT_ATTEMPTS})`);
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
this.reconnecting = false;
|
|
65
|
+
this.getWsProvider(wsEndpoint, network);
|
|
66
|
+
}, this.RECONNECT_DELAY * this.reconnectAttempts);
|
|
67
|
+
}
|
|
68
|
+
else if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
|
|
69
|
+
(0, dist_1.log_error)(`Maximum WebSocket reconnection attempts (${this.MAX_RECONNECT_ATTEMPTS}) reached`, new Error('Max reconnect attempts'));
|
|
70
|
+
this.wsConnections = 0;
|
|
71
|
+
this.reconnectAttempts = 0;
|
|
72
|
+
this.reconnecting = false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
incrementConnections() {
|
|
76
|
+
this.wsConnections++;
|
|
77
|
+
(0, dist_1.log_info)(`WebSocket active connections: ${this.wsConnections}`);
|
|
78
|
+
}
|
|
79
|
+
decrementConnections() {
|
|
80
|
+
if (this.wsConnections > 0) {
|
|
81
|
+
this.wsConnections--;
|
|
82
|
+
}
|
|
83
|
+
(0, dist_1.log_info)(`WebSocket active connections: ${this.wsConnections}`);
|
|
84
|
+
if (this.wsConnections === 0 && this.wsProvider) {
|
|
85
|
+
(0, dist_1.log_info)('No active connections, closing WebSocket');
|
|
86
|
+
this.wsProvider.removeAllListeners();
|
|
87
|
+
this.wsProvider = null;
|
|
88
|
+
this.reconnectAttempts = 0;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
listenToEvent(eventName, handler) {
|
|
92
|
+
var _a;
|
|
93
|
+
if (!this.wsProvider) {
|
|
94
|
+
(0, dist_1.log_warn)('WebSocket provider not available for event listener');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (!this.eventListeners.has(eventName)) {
|
|
98
|
+
this.eventListeners.set(eventName, new Set());
|
|
99
|
+
}
|
|
100
|
+
(_a = this.eventListeners.get(eventName)) === null || _a === void 0 ? void 0 : _a.add(handler);
|
|
101
|
+
this.wsProvider.once(eventName, handler);
|
|
102
|
+
this.incrementConnections();
|
|
103
|
+
}
|
|
104
|
+
removeEventListener(eventName, handler) {
|
|
105
|
+
if (!this.wsProvider)
|
|
106
|
+
return;
|
|
107
|
+
this.wsProvider.removeListener(eventName, handler);
|
|
108
|
+
const handlers = this.eventListeners.get(eventName);
|
|
109
|
+
if (handlers) {
|
|
110
|
+
handlers.delete(handler);
|
|
111
|
+
if (handlers.size === 0) {
|
|
112
|
+
this.eventListeners.delete(eventName);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
this.decrementConnections();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.WebSocketManager = WebSocketManager;
|
|
119
|
+
WebSocketManager.instance = null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const _48CLUB_BUILDER_CONTROL_EOA = "0x4848489f0b2BEdd788c696e2D79b6b69D7484848";
|
|
2
|
+
export interface BundleParams {
|
|
3
|
+
txs: string[];
|
|
4
|
+
maxBlockNumber?: number;
|
|
5
|
+
maxTimestamp?: number;
|
|
6
|
+
revertingTxHashes?: string[];
|
|
7
|
+
noMerge?: boolean;
|
|
8
|
+
backrunTarget?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class _48ClubTrade {
|
|
11
|
+
private rpcUrl;
|
|
12
|
+
private soulPointSignature?;
|
|
13
|
+
private provider;
|
|
14
|
+
constructor(soulPointSignature?: string);
|
|
15
|
+
sendPrivateTransaction(signedTx: string): Promise<string>;
|
|
16
|
+
_sendPrivateTransactionWith48SP(signedTx: string): Promise<string>;
|
|
17
|
+
sendBundle(params: BundleParams): Promise<string>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports._48ClubTrade = exports._48CLUB_BUILDER_CONTROL_EOA = void 0;
|
|
16
|
+
const axios_1 = __importDefault(require("axios"));
|
|
17
|
+
const ethers_1 = require("ethers");
|
|
18
|
+
exports._48CLUB_BUILDER_CONTROL_EOA = '0x4848489f0b2BEdd788c696e2D79b6b69D7484848';
|
|
19
|
+
class _48ClubTrade {
|
|
20
|
+
constructor(soulPointSignature) {
|
|
21
|
+
this.rpcUrl = process.env._48CLUB_RPC_URL || 'https://puissant-builder.48.club/';
|
|
22
|
+
this.soulPointSignature = soulPointSignature;
|
|
23
|
+
this.provider = new ethers_1.ethers.providers.JsonRpcProvider(this.rpcUrl);
|
|
24
|
+
}
|
|
25
|
+
sendPrivateTransaction(signedTx) {
|
|
26
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
+
try {
|
|
28
|
+
const response = yield axios_1.default.post(this.rpcUrl, {
|
|
29
|
+
jsonrpc: "2.0",
|
|
30
|
+
id: "1",
|
|
31
|
+
method: "eth_sendRawTransaction",
|
|
32
|
+
params: [signedTx]
|
|
33
|
+
});
|
|
34
|
+
if (response.data.error) {
|
|
35
|
+
throw new Error(`RPC Error: ${response.data.error.message}`);
|
|
36
|
+
}
|
|
37
|
+
return response.data.result;
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (error instanceof Error) {
|
|
41
|
+
throw new Error(`48club sendPrivateTransaction failed!!! ${error.message}`);
|
|
42
|
+
}
|
|
43
|
+
throw new Error('48club sendPrivateTransaction failed!!! Unknown error');
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
_sendPrivateTransactionWith48SP(signedTx) {
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
try {
|
|
50
|
+
if (!this.soulPointSignature) {
|
|
51
|
+
throw new Error('48 SoulPoint signature is required');
|
|
52
|
+
}
|
|
53
|
+
const response = yield axios_1.default.post(this.rpcUrl, {
|
|
54
|
+
jsonrpc: "2.0",
|
|
55
|
+
id: "1",
|
|
56
|
+
method: "eth_sendPrivateTransactionWith48SP",
|
|
57
|
+
params: [signedTx, this.soulPointSignature]
|
|
58
|
+
});
|
|
59
|
+
if (response.data.error) {
|
|
60
|
+
throw new Error(`RPC Error: ${response.data.error.message}`);
|
|
61
|
+
}
|
|
62
|
+
return response.data.result;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (error instanceof Error) {
|
|
66
|
+
throw new Error(`48club sendPrivateTransactionWith48SP failed!!! ${error.message}`);
|
|
67
|
+
}
|
|
68
|
+
throw new Error('48club sendPrivateTransactionWith48SP failed!!! Unknown error');
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
sendBundle(params) {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
try {
|
|
75
|
+
const currentTimestamp = Math.floor(Date.now() / 1000);
|
|
76
|
+
const requestParams = Object.assign({ txs: params.txs, maxBlockNumber: params.maxBlockNumber, maxTimestamp: params.maxTimestamp || currentTimestamp + 30, revertingTxHashes: params.revertingTxHashes, noMerge: params.noMerge, backrunTarget: params.backrunTarget }, (this.soulPointSignature ? { '48spSign': this.soulPointSignature } : {}));
|
|
77
|
+
const response = yield axios_1.default.post(this.rpcUrl, {
|
|
78
|
+
jsonrpc: "2.0",
|
|
79
|
+
id: "1",
|
|
80
|
+
method: "eth_sendBundle",
|
|
81
|
+
params: [requestParams]
|
|
82
|
+
});
|
|
83
|
+
if (response.data.error) {
|
|
84
|
+
throw new Error(`RPC Error: ${response.data.error.message}`);
|
|
85
|
+
}
|
|
86
|
+
return response.data.result;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error instanceof Error) {
|
|
90
|
+
throw new Error(`48club sendBundle failed!!! ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
throw new Error('48club sendBundle failed!!! Unknown error');
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports._48ClubTrade = _48ClubTrade;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare class BlockRazorTrade {
|
|
2
|
+
private rpcUrl;
|
|
3
|
+
private authToken;
|
|
4
|
+
constructor();
|
|
5
|
+
sendPrivateTransaction(signedTx: string): Promise<string>;
|
|
6
|
+
sendBundle(transactions: string[], maxBlockNumber?: number, revertingTxHashes?: string[]): Promise<string>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.BlockRazorTrade = void 0;
|
|
16
|
+
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
17
|
+
const axios_1 = __importDefault(require("axios"));
|
|
18
|
+
class BlockRazorTrade {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.rpcUrl = process.env.BLOCKRAZOR_RPC_URL || 'https://rpc.blockrazor.builders';
|
|
21
|
+
this.authToken = process.env.BLOCKRAZOR_AUTH_TOKEN || '';
|
|
22
|
+
}
|
|
23
|
+
sendPrivateTransaction(signedTx) {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
try {
|
|
26
|
+
const response = yield axios_1.default.post(this.rpcUrl, {
|
|
27
|
+
jsonrpc: "2.0",
|
|
28
|
+
id: "1",
|
|
29
|
+
method: "eth_sendPrivateTransaction",
|
|
30
|
+
params: [signedTx]
|
|
31
|
+
}, {
|
|
32
|
+
headers: {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
'Authorization': this.authToken
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
if (response.data.error) {
|
|
38
|
+
throw new Error(`交易发送失败: ${response.data.error.code} - ${response.data.error.message}`);
|
|
39
|
+
}
|
|
40
|
+
return response.data.result;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
(0, dist_1.log_error)('blockrazor sendPrivateTransaction failed!!!', error);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
sendBundle(transactions, maxBlockNumber, revertingTxHashes) {
|
|
49
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
try {
|
|
51
|
+
const currentTimestamp = Math.floor(Date.now() / 1000);
|
|
52
|
+
const response = yield axios_1.default.post(this.rpcUrl, {
|
|
53
|
+
jsonrpc: "2.0",
|
|
54
|
+
id: "1",
|
|
55
|
+
method: "eth_sendBundle",
|
|
56
|
+
params: [{
|
|
57
|
+
txs: transactions,
|
|
58
|
+
minTimestamp: currentTimestamp + 1,
|
|
59
|
+
maxTimestamp: currentTimestamp + 30,
|
|
60
|
+
}]
|
|
61
|
+
}, {
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
'Authorization': this.authToken,
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
if (response.data.error) {
|
|
68
|
+
throw new Error(`Bundle 发送失败: ${response.data.error.message}`);
|
|
69
|
+
}
|
|
70
|
+
return response.data.result;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
(0, dist_1.log_error)('blockrazor sendBundle failed!!!', error);
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.BlockRazorTrade = BlockRazorTrade;
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.BscRpc = void 0;
|
|
13
|
+
class BscRpc {
|
|
14
|
+
constructor(rpc_endpoint) {
|
|
15
|
+
this.url = rpc_endpoint || process.env.BSC_RPC_ENDPOINT || '';
|
|
16
|
+
this.headers = new Headers();
|
|
17
|
+
this.headers.append("Content-Type", "application/json");
|
|
18
|
+
}
|
|
19
|
+
eth_sendRawTransaction(signedTx) {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
console.log(`Sending transaction to ${this.url}`);
|
|
22
|
+
const raw = JSON.stringify({
|
|
23
|
+
jsonrpc: "2.0",
|
|
24
|
+
method: "eth_sendRawTransaction",
|
|
25
|
+
params: [signedTx],
|
|
26
|
+
id: 1
|
|
27
|
+
});
|
|
28
|
+
const requestOptions = {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: this.headers,
|
|
31
|
+
body: raw,
|
|
32
|
+
redirect: 'follow'
|
|
33
|
+
};
|
|
34
|
+
const response = yield fetch(this.url, requestOptions);
|
|
35
|
+
const result = yield response.json();
|
|
36
|
+
if (result.error) {
|
|
37
|
+
throw new Error(result.error.message);
|
|
38
|
+
}
|
|
39
|
+
return result.result;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.BscRpc = BscRpc;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.BSC_EOA_ADDRESS = void 0;
|
|
18
|
+
__exportStar(require("./send_tx"), exports);
|
|
19
|
+
var BSC_EOA_ADDRESS;
|
|
20
|
+
(function (BSC_EOA_ADDRESS) {
|
|
21
|
+
BSC_EOA_ADDRESS["BLOCKRAZOR"] = "0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20";
|
|
22
|
+
BSC_EOA_ADDRESS["_48CLUB"] = "0x4848489f0b2BEdd788c696e2D79b6b69D7484848";
|
|
23
|
+
})(BSC_EOA_ADDRESS || (exports.BSC_EOA_ADDRESS = BSC_EOA_ADDRESS = {}));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AppConfig } from "@clonegod/ttd-core/dist";
|
|
2
|
+
export declare class TransactionSender {
|
|
3
|
+
private rpc;
|
|
4
|
+
private blockRazor;
|
|
5
|
+
private _48Club;
|
|
6
|
+
constructor(appConfig: AppConfig);
|
|
7
|
+
sendTransaction(signedMainTx: string, eoa_tip_transaction: (eoa_address: string) => Promise<string>, order_trace_id: string): Promise<string[]>;
|
|
8
|
+
}
|