@clonegod/ttd-bsc-common 1.0.90 → 2.0.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/config/BscQuoteAppConfig.d.ts +11 -0
- package/dist/config/BscQuoteAppConfig.js +62 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +1 -0
- package/dist/quote/event/index.d.ts +1 -0
- package/dist/quote/event/index.js +1 -0
- package/dist/quote/event/swap_debouncer.d.ts +22 -0
- package/dist/quote/event/swap_debouncer.js +92 -0
- package/dist/quote/index.d.ts +1 -0
- package/dist/quote/index.js +1 -0
- package/dist/quote/pricing/index.d.ts +2 -0
- package/dist/quote/pricing/index.js +2 -0
- package/dist/quote/pricing/pool_state_initializer.d.ts +8 -0
- package/dist/quote/pricing/pool_state_initializer.js +142 -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/tick/clmm_tick_cache.d.ts +40 -0
- package/dist/quote/tick/clmm_tick_cache.js +219 -0
- package/dist/quote/tick/index.d.ts +2 -0
- package/dist/{trade/send → quote/tick}/index.js +2 -8
- package/dist/quote/tick/tick_lens_loaders.d.ts +25 -0
- package/dist/quote/tick/tick_lens_loaders.js +170 -0
- package/dist/redis/redis_client.d.ts +1 -0
- package/dist/redis/redis_client.js +6 -0
- package/dist/trade/abstract_dex_trade.d.ts +30 -16
- package/dist/trade/abstract_dex_trade.js +223 -108
- package/dist/trade/caller_manager.d.ts +35 -0
- package/dist/trade/caller_manager.js +178 -0
- package/dist/trade/index.d.ts +1 -2
- package/dist/trade/index.js +1 -2
- package/dist/types/pool_state.d.ts +31 -0
- package/package.json +3 -2
- package/dist/trade/abstract_dex_trade_plus.d.ts +0 -44
- package/dist/trade/abstract_dex_trade_plus.js +0 -449
- package/dist/trade/send/48club.d.ts +0 -17
- package/dist/trade/send/48club.js +0 -123
- package/dist/trade/send/48club_member.d.ts +0 -1
- package/dist/trade/send/48club_member.js +0 -25
- package/dist/trade/send/48club_sp.d.ts +0 -9
- package/dist/trade/send/48club_sp.js +0 -137
- package/dist/trade/send/blockrazor.d.ts +0 -7
- package/dist/trade/send/blockrazor.js +0 -78
- package/dist/trade/send/blxr.d.ts +0 -0
- package/dist/trade/send/blxr.js +0 -0
- package/dist/trade/send/bsc_rpc.d.ts +0 -6
- package/dist/trade/send/bsc_rpc.js +0 -47
- package/dist/trade/send/index.d.ts +0 -6
- package/dist/trade/send/send_bundle_proxy.d.ts +0 -7
- package/dist/trade/send/send_bundle_proxy.js +0 -30
- package/dist/trade/send/send_bundle_ws.d.ts +0 -7
- package/dist/trade/send/send_bundle_ws.js +0 -30
- package/dist/trade/send/send_tx.d.ts +0 -9
- package/dist/trade/send/send_tx.js +0 -119
- package/dist/ws/bsc_stream_ws_client.d.ts +0 -10
- package/dist/ws/bsc_stream_ws_client.js +0 -95
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { AbastrcatTrade, AppConfig, TradeContext } from "@clonegod/ttd-core/dist";
|
|
2
|
-
import { ethers } from "ethers";
|
|
3
|
-
import { DexConfig, EvmChainConfig } from "../types";
|
|
4
|
-
import { SimpleRedisClient } from "../redis";
|
|
5
|
-
export declare abstract class AbstractEvmDexTradePlus extends AbastrcatTrade {
|
|
6
|
-
protected appConfig: AppConfig;
|
|
7
|
-
protected group_wallets: ethers.Wallet[];
|
|
8
|
-
protected wallet: ethers.Wallet;
|
|
9
|
-
protected provider: ethers.providers.JsonRpcProvider;
|
|
10
|
-
protected walletMode: 'single' | 'multi';
|
|
11
|
-
protected approvedTokens: Map<string, Map<string, boolean>>;
|
|
12
|
-
protected pairContracts: Map<string, ethers.Contract>;
|
|
13
|
-
protected tokenContracts: Map<string, ethers.Contract>;
|
|
14
|
-
protected routerContract: ethers.Contract;
|
|
15
|
-
protected chainConfig: EvmChainConfig;
|
|
16
|
-
protected dexConfig: DexConfig;
|
|
17
|
-
protected redisClient: SimpleRedisClient;
|
|
18
|
-
protected chainNameLower: string;
|
|
19
|
-
constructor(appConfig: AppConfig);
|
|
20
|
-
protected abstract initConfigs(): void;
|
|
21
|
-
init(): Promise<void>;
|
|
22
|
-
protected getWalletMode(): 'single' | 'multi';
|
|
23
|
-
protected getSingleWallet(): ethers.Wallet;
|
|
24
|
-
protected getGroupWallets(): ethers.Wallet[];
|
|
25
|
-
protected getTokenContract(tokenAddress: string): ethers.Contract;
|
|
26
|
-
protected getTokenContractWithWallet(tokenAddress: string, wallet: ethers.Wallet): ethers.Contract;
|
|
27
|
-
protected getGasPriceGwei(context: TradeContext): string;
|
|
28
|
-
protected getBuilderTipAmoutGwei(context: TradeContext): string;
|
|
29
|
-
preApproveAllWallets(token_list?: string[]): Promise<void>;
|
|
30
|
-
private approveTokenIfNeeded;
|
|
31
|
-
protected checkTradeTokenApprove(context: TradeContext, wallet: ethers.Wallet, routerAddress?: string): Promise<void>;
|
|
32
|
-
protected checkTokenApprove(tokenAddress: string, tokenSymbol: string, tokenContract: ethers.Contract, spenderAddress: string, wallet: ethers.Wallet): Promise<boolean>;
|
|
33
|
-
protected isNonceRelatedError(error: any): boolean;
|
|
34
|
-
protected isNativeCurrency(symbol: string): boolean;
|
|
35
|
-
protected getWrappedNativeAddress(): string;
|
|
36
|
-
abstract execute(context: TradeContext, retryCount?: number): Promise<string>;
|
|
37
|
-
protected buildTipTransferTx(to: string, transfer_amount_gwei: string, gas_price_gwei: string, transfer_nonce: number, wallet: ethers.Wallet): Promise<string>;
|
|
38
|
-
protected chooseWallet(context: TradeContext): Promise<ethers.Wallet>;
|
|
39
|
-
protected getWalletNextNonce(walletAddress: string): Promise<any>;
|
|
40
|
-
protected determineInputOutputTokens(order_msg: any, pool_info: any): {
|
|
41
|
-
inputToken: any;
|
|
42
|
-
outputToken: any;
|
|
43
|
-
};
|
|
44
|
-
}
|
|
@@ -1,449 +0,0 @@
|
|
|
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.AbstractEvmDexTradePlus = void 0;
|
|
16
|
-
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
17
|
-
const ethers_1 = require("ethers");
|
|
18
|
-
const common_1 = require("../common");
|
|
19
|
-
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
20
|
-
const redis_1 = require("../redis");
|
|
21
|
-
class AbstractEvmDexTradePlus extends dist_1.AbastrcatTrade {
|
|
22
|
-
constructor(appConfig) {
|
|
23
|
-
super();
|
|
24
|
-
this.appConfig = appConfig;
|
|
25
|
-
this.walletMode = 'multi';
|
|
26
|
-
this.approvedTokens = new Map();
|
|
27
|
-
this.pairContracts = new Map();
|
|
28
|
-
this.tokenContracts = new Map();
|
|
29
|
-
this.approvedTokens = new Map();
|
|
30
|
-
this.pairContracts = new Map();
|
|
31
|
-
this.tokenContracts = new Map();
|
|
32
|
-
this.initConfigs();
|
|
33
|
-
this.chainNameLower = this.appConfig.env_args.chain_id.toLowerCase();
|
|
34
|
-
this.redisClient = new redis_1.SimpleRedisClient(`${this.chainNameLower}:tx`);
|
|
35
|
-
}
|
|
36
|
-
init() {
|
|
37
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
-
this.provider = new ethers_1.ethers.providers.JsonRpcProvider(this.chainConfig.rpcEndpoint);
|
|
39
|
-
const walletGroupIds = process.env.WALLET_GROUP_IDS || '';
|
|
40
|
-
this.walletMode = walletGroupIds && walletGroupIds.trim().split(',').length > 0 ? 'multi' : 'single';
|
|
41
|
-
if (this.walletMode === 'multi') {
|
|
42
|
-
let wallet_infos = (0, dist_1.load_wallet_multi)(walletGroupIds.split(','), false);
|
|
43
|
-
this.group_wallets = wallet_infos.map(info => new ethers_1.ethers.Wallet(info.private_key, this.provider));
|
|
44
|
-
(0, dist_1.log_info)(`组钱包已初始化,数量: ${this.group_wallets.length}`, this.group_wallets.map(e => e.address));
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
this.wallet = new ethers_1.ethers.Wallet(this.appConfig.trade_runtime.wallet.private_key, this.provider);
|
|
48
|
-
(0, dist_1.log_info)(`单钱包已初始化,walletAddress= ${this.wallet.address}`);
|
|
49
|
-
}
|
|
50
|
-
this.routerContract = new ethers_1.ethers.Contract(this.dexConfig.routerAddress, this.dexConfig.routerAbi, this.provider);
|
|
51
|
-
(0, dist_1.log_info)(`${this.dexConfig.dexName} Router已初始化, routerAddress= ${this.dexConfig.routerAddress}`);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
getWalletMode() {
|
|
55
|
-
return this.walletMode;
|
|
56
|
-
}
|
|
57
|
-
getSingleWallet() {
|
|
58
|
-
if (this.walletMode !== 'single' || !this.wallet) {
|
|
59
|
-
throw new Error('Single wallet not available in multi-wallet mode or not initialized');
|
|
60
|
-
}
|
|
61
|
-
return this.wallet;
|
|
62
|
-
}
|
|
63
|
-
getGroupWallets() {
|
|
64
|
-
if (this.walletMode !== 'multi' || !this.group_wallets) {
|
|
65
|
-
throw new Error('Group wallets not available in single-wallet mode or not initialized');
|
|
66
|
-
}
|
|
67
|
-
return this.group_wallets;
|
|
68
|
-
}
|
|
69
|
-
getTokenContract(tokenAddress) {
|
|
70
|
-
if (!this.tokenContracts.has(tokenAddress)) {
|
|
71
|
-
const tokenContract = new ethers_1.ethers.Contract(tokenAddress, common_1.ERC20_ABI, this.provider);
|
|
72
|
-
this.tokenContracts.set(tokenAddress, tokenContract);
|
|
73
|
-
}
|
|
74
|
-
return this.tokenContracts.get(tokenAddress);
|
|
75
|
-
}
|
|
76
|
-
getTokenContractWithWallet(tokenAddress, wallet) {
|
|
77
|
-
const tokenContract = this.getTokenContract(tokenAddress);
|
|
78
|
-
return tokenContract.connect(wallet);
|
|
79
|
-
}
|
|
80
|
-
getGasPriceGwei(context) {
|
|
81
|
-
let { evm_gas_limit, evm_gas_price_gwei, evm_tip_amount_gwei } = context.trade_runtime.settings.strategy;
|
|
82
|
-
if (evm_gas_price_gwei === undefined || evm_gas_price_gwei === null || evm_gas_price_gwei <= 0) {
|
|
83
|
-
evm_gas_price_gwei = this.chainConfig.gasOptions.defaultGasPriceGwei;
|
|
84
|
-
}
|
|
85
|
-
(0, dist_1.log_info)(`getGasPriceGwei: ${evm_gas_price_gwei} Gwei`);
|
|
86
|
-
return evm_gas_price_gwei.toString();
|
|
87
|
-
}
|
|
88
|
-
getBuilderTipAmoutGwei(context) {
|
|
89
|
-
let { evm_gas_limit, evm_gas_price_gwei, evm_tip_amount_gwei } = context.trade_runtime.settings.strategy;
|
|
90
|
-
if (evm_tip_amount_gwei === undefined || evm_tip_amount_gwei === null || evm_tip_amount_gwei <= 0) {
|
|
91
|
-
evm_tip_amount_gwei = this.chainConfig.gasOptions.defaultTipAmountGwei;
|
|
92
|
-
}
|
|
93
|
-
(0, dist_1.log_info)(`getGasTipAmoutGwei: ${evm_tip_amount_gwei} Gwei`);
|
|
94
|
-
return evm_tip_amount_gwei.toString();
|
|
95
|
-
}
|
|
96
|
-
preApproveAllWallets() {
|
|
97
|
-
return __awaiter(this, arguments, void 0, function* (token_list = []) {
|
|
98
|
-
if (this.walletMode === 'single') {
|
|
99
|
-
if (!this.wallet) {
|
|
100
|
-
(0, dist_1.log_info)('Single wallet not available for pre-approval');
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (!token_list || token_list.length === 0) {
|
|
104
|
-
token_list = Array.from(this.tokenContracts.keys());
|
|
105
|
-
}
|
|
106
|
-
(0, dist_1.log_info)(`Pre-approve: single wallet ${this.wallet.address}, ${token_list.length} tokens`, token_list);
|
|
107
|
-
const tokenAddresses = new Set();
|
|
108
|
-
for (const token_address of token_list) {
|
|
109
|
-
tokenAddresses.add(token_address.toLowerCase());
|
|
110
|
-
}
|
|
111
|
-
const tokens = Array.from(tokenAddresses);
|
|
112
|
-
try {
|
|
113
|
-
(0, dist_1.log_info)(`Pre-approve: wallet ${this.wallet.address}`);
|
|
114
|
-
for (const tokenAddress of tokens) {
|
|
115
|
-
try {
|
|
116
|
-
yield this.approveTokenIfNeeded(this.wallet, tokenAddress);
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
console.warn(`Failed to pre-approve token ${tokenAddress} for wallet ${this.wallet.address}:`, error);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
(0, dist_1.log_info)(`Pre-approve: wallet ${this.wallet.address} completed`);
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
125
|
-
console.warn(`Failed to pre-approve tokens for wallet ${this.wallet.address}:`, error);
|
|
126
|
-
}
|
|
127
|
-
(0, dist_1.log_info)('Pre-approve: single wallet completed');
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
if (!this.group_wallets || this.group_wallets.length === 0) {
|
|
131
|
-
(0, dist_1.log_info)('No wallets available for pre-approval');
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
if (!token_list || token_list.length === 0) {
|
|
135
|
-
token_list = Array.from(this.tokenContracts.keys());
|
|
136
|
-
}
|
|
137
|
-
(0, dist_1.log_info)(`Pre-approve: ${this.group_wallets.length} wallets, ${token_list.length} tokens`, token_list);
|
|
138
|
-
const tokenAddresses = new Set();
|
|
139
|
-
for (const token_address of token_list) {
|
|
140
|
-
tokenAddresses.add(token_address.toLowerCase());
|
|
141
|
-
}
|
|
142
|
-
const tokens = Array.from(tokenAddresses);
|
|
143
|
-
for (const wallet of this.group_wallets) {
|
|
144
|
-
try {
|
|
145
|
-
(0, dist_1.log_info)(`Pre-approve: wallet ${wallet.address}`);
|
|
146
|
-
for (const tokenAddress of tokens) {
|
|
147
|
-
try {
|
|
148
|
-
yield this.approveTokenIfNeeded(wallet, tokenAddress);
|
|
149
|
-
}
|
|
150
|
-
catch (error) {
|
|
151
|
-
console.warn(`Failed to pre-approve token ${tokenAddress} for wallet ${wallet.address}:`, error);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
(0, dist_1.log_info)(`Pre-approve: wallet ${wallet.address} completed`);
|
|
155
|
-
}
|
|
156
|
-
catch (error) {
|
|
157
|
-
console.warn(`Failed to pre-approve tokens for wallet ${wallet.address}:`, error);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
(0, dist_1.log_info)('Pre-approve: all wallets completed');
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
approveTokenIfNeeded(wallet, tokenAddress) {
|
|
165
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
166
|
-
try {
|
|
167
|
-
const tokenContract = this.getTokenContract(tokenAddress);
|
|
168
|
-
const routerAddress = this.dexConfig.routerAddress;
|
|
169
|
-
const currentAllowance = yield tokenContract.allowance(wallet.address, routerAddress);
|
|
170
|
-
if (currentAllowance.gt(0)) {
|
|
171
|
-
(0, dist_1.log_info)(`Pre-approve: token ${tokenAddress} already approved`);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
const maxAllowance = ethers_1.ethers.constants.MaxUint256;
|
|
175
|
-
const approveTx = yield tokenContract.connect(wallet).approve(routerAddress, maxAllowance);
|
|
176
|
-
(0, dist_1.log_info)(`Pre-approve: token ${tokenAddress} approve success, tx: ${approveTx.hash}`);
|
|
177
|
-
yield approveTx.wait();
|
|
178
|
-
(0, dist_1.log_info)(`Pre-approve: token ${tokenAddress} approved successfully`);
|
|
179
|
-
}
|
|
180
|
-
catch (error) {
|
|
181
|
-
console.warn(`Failed to approve token ${tokenAddress} for wallet ${wallet.address}:`, error);
|
|
182
|
-
throw error;
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
checkTradeTokenApprove(context, wallet, routerAddress) {
|
|
187
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
188
|
-
const { pool_info } = context;
|
|
189
|
-
const { tokenA, tokenB } = pool_info;
|
|
190
|
-
const router = routerAddress || this.dexConfig.routerAddress;
|
|
191
|
-
if (!this.approvedTokens.has(wallet.address)) {
|
|
192
|
-
this.approvedTokens.set(wallet.address, new Map());
|
|
193
|
-
}
|
|
194
|
-
yield Promise.all([tokenA, tokenB].map((_a) => __awaiter(this, [_a], void 0, function* ({ symbol, address }) {
|
|
195
|
-
const tokenContract = this.getTokenContractWithWallet(address, wallet);
|
|
196
|
-
yield this.checkTokenApprove(address, symbol, tokenContract, router, wallet);
|
|
197
|
-
})));
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
checkTokenApprove(tokenAddress, tokenSymbol, tokenContract, spenderAddress, wallet) {
|
|
201
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
202
|
-
if (!this.approvedTokens.has(wallet.address)) {
|
|
203
|
-
this.approvedTokens.set(wallet.address, new Map());
|
|
204
|
-
}
|
|
205
|
-
if (this.approvedTokens.get(wallet.address).get(tokenAddress)) {
|
|
206
|
-
(0, dist_1.log_info)(`钱包 ${wallet.address} 的代币 ${tokenSymbol} 已授权,跳过授权检查`);
|
|
207
|
-
return true;
|
|
208
|
-
}
|
|
209
|
-
const allowance = yield tokenContract.allowance(wallet.address, spenderAddress);
|
|
210
|
-
const maxAllowance = ethers_1.ethers.constants.MaxUint256.div(2);
|
|
211
|
-
(0, dist_1.log_info)(`代币授权检查:`, {
|
|
212
|
-
walletAddress: wallet.address,
|
|
213
|
-
tokenSymbol,
|
|
214
|
-
currentAllowance: allowance.toString(),
|
|
215
|
-
maxAllowance: maxAllowance.toString(),
|
|
216
|
-
isSufficient: allowance.gte(maxAllowance)
|
|
217
|
-
});
|
|
218
|
-
if (allowance.lt(maxAllowance)) {
|
|
219
|
-
const maxApprovalAmount = ethers_1.ethers.utils.formatUnits(ethers_1.ethers.constants.MaxUint256, yield tokenContract.decimals());
|
|
220
|
-
const tokenSymbolDisplay = tokenSymbol || `${tokenAddress.substring(0, 6)}...`;
|
|
221
|
-
(0, dist_1.log_info)(`正在为钱包 ${wallet.address} 授权代币 ${tokenSymbolDisplay}`, {
|
|
222
|
-
walletAddress: wallet.address,
|
|
223
|
-
tokenAddress,
|
|
224
|
-
tokenSymbol: tokenSymbolDisplay,
|
|
225
|
-
currentAllowance: ethers_1.ethers.utils.formatUnits(allowance, yield tokenContract.decimals()),
|
|
226
|
-
newAllowance: maxApprovalAmount
|
|
227
|
-
});
|
|
228
|
-
const tx = yield tokenContract.approve(spenderAddress, ethers_1.ethers.constants.MaxUint256, {
|
|
229
|
-
gasLimit: this.chainConfig.gasOptions.gasLimit
|
|
230
|
-
});
|
|
231
|
-
yield tx.wait();
|
|
232
|
-
this.approvedTokens.get(wallet.address).set(tokenAddress, true);
|
|
233
|
-
(0, dist_1.log_info)(`钱包 ${wallet.address} 的代币 ${tokenSymbolDisplay} 授权完成,数量: ${maxApprovalAmount} ${tokenSymbolDisplay}, 交易哈希: ${tx.hash}`);
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
this.approvedTokens.get(wallet.address).set(tokenAddress, true);
|
|
238
|
-
return true;
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
isNonceRelatedError(error) {
|
|
243
|
-
if (!error)
|
|
244
|
-
return false;
|
|
245
|
-
const errorMessage = error.message ? error.message.toLowerCase() : '';
|
|
246
|
-
const nonceErrorKeywords = [
|
|
247
|
-
'nonce',
|
|
248
|
-
];
|
|
249
|
-
return nonceErrorKeywords.some(keyword => errorMessage.includes(keyword));
|
|
250
|
-
}
|
|
251
|
-
isNativeCurrency(symbol) {
|
|
252
|
-
return symbol.toUpperCase() === this.chainConfig.nativeCurrency;
|
|
253
|
-
}
|
|
254
|
-
getWrappedNativeAddress() {
|
|
255
|
-
return this.chainConfig.wrappedNativeCurrencyAddress;
|
|
256
|
-
}
|
|
257
|
-
buildTipTransferTx(to, transfer_amount_gwei, gas_price_gwei, transfer_nonce, wallet) {
|
|
258
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
259
|
-
let real_transfer_amount_gwei = Math.min(Number(transfer_amount_gwei), this.chainConfig.gasOptions.maxTipAmountGwei).toString();
|
|
260
|
-
let real_gas_price_gwei = Math.min(Number(gas_price_gwei), this.chainConfig.gasOptions.maxGasPriceGwei).toString();
|
|
261
|
-
let tx_data = {
|
|
262
|
-
from: wallet.address,
|
|
263
|
-
to,
|
|
264
|
-
value: ethers_1.ethers.utils.parseUnits(real_transfer_amount_gwei, 'gwei'),
|
|
265
|
-
gasLimit: 42000,
|
|
266
|
-
gasPrice: ethers_1.ethers.utils.parseUnits(real_gas_price_gwei, 'gwei'),
|
|
267
|
-
nonce: transfer_nonce,
|
|
268
|
-
chainId: this.chainConfig.chainId
|
|
269
|
-
};
|
|
270
|
-
let signedTx = yield wallet.signTransaction(tx_data);
|
|
271
|
-
(0, dist_1.log_info)(`构建转账交易: `, Object.assign(Object.assign({}, tx_data), { real_transfer_amount_gwei,
|
|
272
|
-
real_gas_price_gwei, txhash: ethers_1.ethers.utils.keccak256(signedTx) }));
|
|
273
|
-
return signedTx;
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
chooseWallet(context) {
|
|
277
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
278
|
-
var _a, _b;
|
|
279
|
-
if (this.getWalletMode() === 'single') {
|
|
280
|
-
return this.getSingleWallet();
|
|
281
|
-
}
|
|
282
|
-
const lockIdentifier = `choose_wallet`;
|
|
283
|
-
const lockKey = `${this.chainNameLower}:lock:${lockIdentifier}`;
|
|
284
|
-
const lockValue = `${Math.random().toString(36).substring(2, 15)}`;
|
|
285
|
-
let lockAcquired = false;
|
|
286
|
-
const start_time = Date.now();
|
|
287
|
-
const maxRetryTime = 1000;
|
|
288
|
-
const retryDelay = 100;
|
|
289
|
-
do {
|
|
290
|
-
lockAcquired = yield this.redisClient.acquireLock(lockKey, lockValue, 1);
|
|
291
|
-
if (lockAcquired) {
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
294
|
-
yield (0, dist_1.sleep)(retryDelay);
|
|
295
|
-
} while (!lockAcquired && Date.now() - start_time < maxRetryTime);
|
|
296
|
-
if (!lockAcquired) {
|
|
297
|
-
throw new Error(`choose wallet, acquire lock failed: ${lockKey}, took ${Date.now() - start_time}ms`);
|
|
298
|
-
}
|
|
299
|
-
(0, dist_1.log_info)(`choose wallet, acquire lock success: ${lockKey}, took ${Date.now() - start_time}ms`);
|
|
300
|
-
try {
|
|
301
|
-
const { inputToken } = this.determineInputOutputTokens(context.order_msg, context.pool_info);
|
|
302
|
-
const requiredAmount = new decimal_js_1.default(context.order_msg.amount);
|
|
303
|
-
const wallet_assets = yield this.redisClient.hgetall(`${this.chainNameLower}:wallet:assets`);
|
|
304
|
-
if (!wallet_assets || Object.keys(wallet_assets).length === 0) {
|
|
305
|
-
throw new Error('没有找到钱包资产信息,请确保钱包监控服务正在运行');
|
|
306
|
-
}
|
|
307
|
-
const wallet_assets_map = {};
|
|
308
|
-
for (const [walletAddress, assetsJson] of Object.entries(wallet_assets)) {
|
|
309
|
-
try {
|
|
310
|
-
wallet_assets_map[walletAddress] = JSON.parse(assetsJson);
|
|
311
|
-
}
|
|
312
|
-
catch (error) {
|
|
313
|
-
console.warn(`解析钱包资产数据失败: ${walletAddress}`, error);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
const availableWallets = [];
|
|
317
|
-
const allWallets = [];
|
|
318
|
-
for (const [walletAddress, assets] of Object.entries(wallet_assets_map)) {
|
|
319
|
-
try {
|
|
320
|
-
const wallet = (_a = this.group_wallets) === null || _a === void 0 ? void 0 : _a.find(w => w.address.toLowerCase() === walletAddress.toLowerCase());
|
|
321
|
-
if (!wallet) {
|
|
322
|
-
continue;
|
|
323
|
-
}
|
|
324
|
-
const tokenAsset = (_b = assets.tokens) === null || _b === void 0 ? void 0 : _b.find((token) => token.tokenaddr.toLowerCase() === inputToken.address.toLowerCase());
|
|
325
|
-
let tokenBalance = (tokenAsset === null || tokenAsset === void 0 ? void 0 : tokenAsset.balance) || '0';
|
|
326
|
-
const balanceDecimal = new decimal_js_1.default(tokenBalance);
|
|
327
|
-
if (balanceDecimal.gte(requiredAmount)) {
|
|
328
|
-
availableWallets.push({
|
|
329
|
-
wallet,
|
|
330
|
-
balance: tokenBalance
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
console.log(`钱包 ${walletAddress} 余额不足: ${tokenBalance} ${inputToken.symbol} < ${requiredAmount} ${inputToken.symbol}`);
|
|
335
|
-
}
|
|
336
|
-
allWallets.push({
|
|
337
|
-
wallet,
|
|
338
|
-
balance: tokenBalance
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
catch (error) {
|
|
342
|
-
console.warn(`处理钱包资产时出错: ${walletAddress}`, error);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
if (availableWallets.length === 0) {
|
|
346
|
-
let error_msg = `all wallets are insufficient, required: ${requiredAmount} ${inputToken.symbol}`;
|
|
347
|
-
(0, dist_1.log_warn)(error_msg);
|
|
348
|
-
allWallets.forEach((walletInfo, index) => {
|
|
349
|
-
const shortAddress = walletInfo.wallet.address.substring(0, 6) + '...' + walletInfo.wallet.address.substring(walletInfo.wallet.address.length - 4);
|
|
350
|
-
console.log(`${index + 1}. ${shortAddress}: ${walletInfo.balance} ${inputToken.symbol}`);
|
|
351
|
-
});
|
|
352
|
-
throw new Error(error_msg);
|
|
353
|
-
}
|
|
354
|
-
console.log('availableWallets', availableWallets.map(w => w.wallet.address));
|
|
355
|
-
const wallet_last_used = yield this.redisClient.hgetall(`${this.chainNameLower}:wallet:last_used`);
|
|
356
|
-
for (const availableWallet of availableWallets) {
|
|
357
|
-
const lastUsedData = wallet_last_used === null || wallet_last_used === void 0 ? void 0 : wallet_last_used[availableWallet.wallet.address];
|
|
358
|
-
if (lastUsedData) {
|
|
359
|
-
try {
|
|
360
|
-
const lastUsedInfo = JSON.parse(lastUsedData);
|
|
361
|
-
availableWallet.lastUsedAt = lastUsedInfo.lastUsedAt;
|
|
362
|
-
}
|
|
363
|
-
catch (error) {
|
|
364
|
-
console.warn(`解析钱包最后使用时间失败: ${availableWallet.wallet.address}`, error);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
else {
|
|
368
|
-
yield this.redisClient.hsetValue(`${this.chainNameLower}:wallet:last_used`, availableWallet.wallet.address, JSON.stringify({
|
|
369
|
-
lastUsedAt: 0,
|
|
370
|
-
lastUsedTime: new Date(0).toISOString()
|
|
371
|
-
}), 24 * 60 * 60);
|
|
372
|
-
availableWallet.lastUsedAt = 0;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
let selectedWallet;
|
|
376
|
-
let selectionStrategy;
|
|
377
|
-
if (availableWallets.length === 1) {
|
|
378
|
-
selectedWallet = availableWallets[0].wallet;
|
|
379
|
-
selectionStrategy = 'OnlyOne';
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
const hasUsageHistory = wallet_last_used && Object.keys(wallet_last_used).length > 0;
|
|
383
|
-
if (hasUsageHistory) {
|
|
384
|
-
availableWallets.sort((a, b) => {
|
|
385
|
-
const aTime = a.lastUsedAt || 0;
|
|
386
|
-
const bTime = b.lastUsedAt || 0;
|
|
387
|
-
return aTime - bTime;
|
|
388
|
-
});
|
|
389
|
-
selectedWallet = availableWallets[0].wallet;
|
|
390
|
-
selectionStrategy = 'MaxLastUsed';
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
availableWallets.sort((a, b) => {
|
|
394
|
-
const aBalance = new decimal_js_1.default(a.balance);
|
|
395
|
-
const bBalance = new decimal_js_1.default(b.balance);
|
|
396
|
-
return bBalance.cmp(aBalance);
|
|
397
|
-
});
|
|
398
|
-
selectedWallet = availableWallets[0].wallet;
|
|
399
|
-
selectionStrategy = 'MaxBalance';
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
yield this.redisClient.hsetValue(`${this.chainNameLower}:wallet:last_used`, selectedWallet.address, JSON.stringify({
|
|
403
|
-
lastUsedAt: Date.now(),
|
|
404
|
-
lastUsedTime: new Date().toISOString()
|
|
405
|
-
}), 24 * 60 * 60);
|
|
406
|
-
const selectedWalletInfo = availableWallets.find(w => w.wallet.address === selectedWallet.address);
|
|
407
|
-
(0, dist_1.log_info)(`Choose wallet: ${selectionStrategy} -> ${selectedWallet.address}, balance: ${selectedWalletInfo === null || selectedWalletInfo === void 0 ? void 0 : selectedWalletInfo.balance} ${inputToken.symbol}, required: ${requiredAmount} ${inputToken.symbol}`);
|
|
408
|
-
return selectedWallet;
|
|
409
|
-
}
|
|
410
|
-
finally {
|
|
411
|
-
yield this.redisClient.releaseLock(lockKey, lockValue);
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
getWalletNextNonce(walletAddress) {
|
|
416
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
417
|
-
try {
|
|
418
|
-
const wallet_assets = yield this.redisClient.hgetvalue(`${this.chainNameLower}:wallet:assets`, walletAddress.toLowerCase());
|
|
419
|
-
if (!wallet_assets) {
|
|
420
|
-
return 0;
|
|
421
|
-
}
|
|
422
|
-
const wallet_assets_map = JSON.parse(wallet_assets);
|
|
423
|
-
return wallet_assets_map.nonce;
|
|
424
|
-
}
|
|
425
|
-
catch (error) {
|
|
426
|
-
console.warn(`获取钱包下一个nonce失败: ${walletAddress}`, error);
|
|
427
|
-
return 0;
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
determineInputOutputTokens(order_msg, pool_info) {
|
|
432
|
-
const { aToB } = order_msg;
|
|
433
|
-
const { tokenA, tokenB, quote_token } = pool_info;
|
|
434
|
-
const quoteToken = [tokenA, tokenB].find(token => token.symbol === quote_token);
|
|
435
|
-
const nonQuoteToken = [tokenA, tokenB].find(token => token.symbol !== quote_token);
|
|
436
|
-
const inputToken = aToB ? nonQuoteToken : quoteToken;
|
|
437
|
-
const outputToken = aToB ? quoteToken : nonQuoteToken;
|
|
438
|
-
if (!inputToken || !outputToken) {
|
|
439
|
-
throw new Error(`无法确定输入/输出代币: ${JSON.stringify({
|
|
440
|
-
tokenA: tokenA.symbol,
|
|
441
|
-
tokenB: tokenB.symbol,
|
|
442
|
-
quote: quote_token,
|
|
443
|
-
aToB
|
|
444
|
-
})}`);
|
|
445
|
-
}
|
|
446
|
-
return { inputToken, outputToken };
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
exports.AbstractEvmDexTradePlus = AbstractEvmDexTradePlus;
|
|
@@ -1,17 +0,0 @@
|
|
|
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
|
-
constructor();
|
|
13
|
-
sendPrivateTransaction(signedTx: string): Promise<string>;
|
|
14
|
-
get48SPSignature(txs: string[]): Promise<string>;
|
|
15
|
-
sendPrivateTransactionWith48SP(signedTx: string): Promise<string>;
|
|
16
|
-
sendBundle(params: BundleParams): Promise<string>;
|
|
17
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
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 _48club_sp_1 = require("./48club_sp");
|
|
18
|
-
const _48club_member_1 = require("./48club_member");
|
|
19
|
-
exports._48CLUB_BUILDER_CONTROL_EOA = '0x4848489f0b2BEdd788c696e2D79b6b69D7484848';
|
|
20
|
-
class _48ClubTrade {
|
|
21
|
-
constructor() {
|
|
22
|
-
this.rpcUrl = process.env._48CLUB_RPC_URL || 'https://puissant-builder.48.club/';
|
|
23
|
-
}
|
|
24
|
-
sendPrivateTransaction(signedTx) {
|
|
25
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
-
try {
|
|
27
|
-
const response = yield axios_1.default.post(this.rpcUrl, {
|
|
28
|
-
jsonrpc: "2.0",
|
|
29
|
-
id: "1",
|
|
30
|
-
method: "eth_sendRawTransaction",
|
|
31
|
-
params: [signedTx]
|
|
32
|
-
});
|
|
33
|
-
if (response.data.error) {
|
|
34
|
-
throw new Error(`RPC Error: ${response.data.error.message}`);
|
|
35
|
-
}
|
|
36
|
-
return response.data.result;
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
if (error instanceof Error) {
|
|
40
|
-
throw new Error(`48club sendPrivateTransaction failed!!! ${error.message}`);
|
|
41
|
-
}
|
|
42
|
-
throw new Error('48club sendPrivateTransaction failed!!! Unknown error');
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
get48SPSignature(txs) {
|
|
47
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
48
|
-
const spPrivateKey = (0, _48club_member_1.get_48club_sp_private_key)();
|
|
49
|
-
if (!spPrivateKey) {
|
|
50
|
-
throw new Error('48 SoulPoint private key is required. Please set _48CLUB_SP_WALLET_ID environment variable.');
|
|
51
|
-
}
|
|
52
|
-
return _48club_sp_1.SoulPointSignature.generate48SPSignature(spPrivateKey, txs);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
sendPrivateTransactionWith48SP(signedTx) {
|
|
56
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
-
try {
|
|
58
|
-
const spPrivateKey = (0, _48club_member_1.get_48club_sp_private_key)();
|
|
59
|
-
if (!spPrivateKey) {
|
|
60
|
-
throw new Error('48 SoulPoint private key is required. Please set _48CLUB_SP_WALLET_ID environment variable.');
|
|
61
|
-
}
|
|
62
|
-
const spSignature = _48club_sp_1.SoulPointSignature.generate48SPSignature(spPrivateKey, [signedTx]);
|
|
63
|
-
const response = yield axios_1.default.post(this.rpcUrl, {
|
|
64
|
-
jsonrpc: "2.0",
|
|
65
|
-
id: "1",
|
|
66
|
-
method: "eth_sendPrivateTransactionWith48SP",
|
|
67
|
-
params: [signedTx, spSignature]
|
|
68
|
-
});
|
|
69
|
-
if (response.data.error) {
|
|
70
|
-
throw new Error(`RPC Error: ${response.data.error.message}`);
|
|
71
|
-
}
|
|
72
|
-
return response.data.result;
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
if (error instanceof Error) {
|
|
76
|
-
throw new Error(`48club sendPrivateTransactionWith48SP failed!!! ${error.message}`);
|
|
77
|
-
}
|
|
78
|
-
throw new Error('48club sendPrivateTransactionWith48SP failed!!! Unknown error');
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
sendBundle(params) {
|
|
83
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
-
try {
|
|
85
|
-
const currentTimestamp = Math.floor(Date.now() / 1000);
|
|
86
|
-
const requestParams = {
|
|
87
|
-
txs: params.txs,
|
|
88
|
-
maxTimestamp: params.maxTimestamp || currentTimestamp + 2,
|
|
89
|
-
revertingTxHashes: params.revertingTxHashes,
|
|
90
|
-
noMerge: params.noMerge,
|
|
91
|
-
backrunTarget: params.backrunTarget,
|
|
92
|
-
};
|
|
93
|
-
const spPrivateKey = (0, _48club_member_1.get_48club_sp_private_key)();
|
|
94
|
-
if (spPrivateKey) {
|
|
95
|
-
try {
|
|
96
|
-
const spSignature = _48club_sp_1.SoulPointSignature.generate48SPSignature(spPrivateKey, params.txs);
|
|
97
|
-
requestParams['48spSign'] = spSignature;
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
console.warn(`[48CLUB] 生成 48SP 签名失败,将发送不带签名的 Bundle: ${error.message}`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
const response = yield axios_1.default.post(this.rpcUrl, {
|
|
104
|
-
jsonrpc: "2.0",
|
|
105
|
-
id: "1",
|
|
106
|
-
method: "eth_sendBundle",
|
|
107
|
-
params: [requestParams]
|
|
108
|
-
});
|
|
109
|
-
if (response.data.error) {
|
|
110
|
-
throw new Error(`RPC Error: ${response.data.error.message}`);
|
|
111
|
-
}
|
|
112
|
-
return response.data.result;
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
if (error instanceof Error) {
|
|
116
|
-
throw new Error(`48club sendBundle failed!!! ${error.message}`);
|
|
117
|
-
}
|
|
118
|
-
throw new Error('48club sendBundle failed!!! Unknown error');
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
exports._48ClubTrade = _48ClubTrade;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const get_48club_sp_private_key: () => string | null;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.get_48club_sp_private_key = void 0;
|
|
4
|
-
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
5
|
-
var _48club_sp_wallet_private_key = null;
|
|
6
|
-
const get_48club_sp_private_key = () => {
|
|
7
|
-
if (_48club_sp_wallet_private_key) {
|
|
8
|
-
return _48club_sp_wallet_private_key;
|
|
9
|
-
}
|
|
10
|
-
try {
|
|
11
|
-
const wallet = (0, dist_1.load_wallet)(process.env._48CLUB_SP_WALLET_ID || 'TTD-PAYMENT', false);
|
|
12
|
-
console.log('Load 48club SP wallet success, wallet address:', wallet.public_key);
|
|
13
|
-
_48club_sp_wallet_private_key = wallet.private_key;
|
|
14
|
-
return _48club_sp_wallet_private_key;
|
|
15
|
-
}
|
|
16
|
-
catch (error) {
|
|
17
|
-
console.error('Load 48club SP wallet failed:' + error.message);
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
exports.get_48club_sp_private_key = get_48club_sp_private_key;
|
|
22
|
-
if (require.main === module) {
|
|
23
|
-
const privateKey = (0, exports.get_48club_sp_private_key)();
|
|
24
|
-
console.log('48club SP wallet private key:', privateKey);
|
|
25
|
-
}
|