@clonegod/ttd-bsc-common 1.0.25 → 1.0.26
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.
|
@@ -4,7 +4,6 @@ import { DexConfig, EvmChainConfig } from "../types";
|
|
|
4
4
|
export declare abstract class AbstractEvmDexTrade extends AbastrcatTrade {
|
|
5
5
|
protected appConfig: AppConfig;
|
|
6
6
|
protected wallet: ethers.Wallet;
|
|
7
|
-
protected group_wallets: ethers.Wallet[];
|
|
8
7
|
protected provider: ethers.providers.JsonRpcProvider;
|
|
9
8
|
protected approvedTokens: Map<string, boolean>;
|
|
10
9
|
protected pairContracts: Map<string, ethers.Contract>;
|
|
@@ -24,5 +23,5 @@ export declare abstract class AbstractEvmDexTrade extends AbastrcatTrade {
|
|
|
24
23
|
protected isNativeCurrency(symbol: string): boolean;
|
|
25
24
|
protected getWrappedNativeAddress(): string;
|
|
26
25
|
abstract execute(context: TradeContext, retryCount?: number): Promise<string>;
|
|
27
|
-
protected buildTipTransferTx(to: string, transfer_amount_gwei: string, gas_price_gwei: string, transfer_nonce: number
|
|
26
|
+
protected buildTipTransferTx(to: string, transfer_amount_gwei: string, gas_price_gwei: string, transfer_nonce: number): Promise<string>;
|
|
28
27
|
}
|
|
@@ -27,15 +27,9 @@ class AbstractEvmDexTrade extends dist_1.AbastrcatTrade {
|
|
|
27
27
|
}
|
|
28
28
|
init() {
|
|
29
29
|
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
-
var _a;
|
|
31
30
|
this.provider = new ethers_1.ethers.providers.JsonRpcProvider(this.chainConfig.rpcEndpoint);
|
|
32
31
|
this.wallet = new ethers_1.ethers.Wallet(this.appConfig.trade_runtime.wallet.private_key, this.provider);
|
|
33
32
|
(0, dist_1.log_info)(`钱包已初始化,地址: ${this.wallet.address}`);
|
|
34
|
-
if (process.env.WALLET_GROUP_IDS) {
|
|
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
|
-
}
|
|
39
33
|
this.routerContract = new ethers_1.ethers.Contract(this.dexConfig.routerAddress, this.dexConfig.routerAbi, this.wallet);
|
|
40
34
|
(0, dist_1.log_info)(`${this.dexConfig.dexName} Router已初始化, 地址: ${this.dexConfig.routerAddress}`);
|
|
41
35
|
});
|
|
@@ -136,13 +130,12 @@ class AbstractEvmDexTrade extends dist_1.AbastrcatTrade {
|
|
|
136
130
|
getWrappedNativeAddress() {
|
|
137
131
|
return this.chainConfig.wrappedNativeCurrencyAddress;
|
|
138
132
|
}
|
|
139
|
-
buildTipTransferTx(to, transfer_amount_gwei, gas_price_gwei, transfer_nonce
|
|
133
|
+
buildTipTransferTx(to, transfer_amount_gwei, gas_price_gwei, transfer_nonce) {
|
|
140
134
|
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
-
const targetWallet = wallet || this.wallet;
|
|
142
135
|
let real_transfer_amount_gwei = Math.min(Number(transfer_amount_gwei), this.chainConfig.gasOptions.maxTipAmountGwei).toString();
|
|
143
136
|
let real_gas_price_gwei = Math.min(Number(gas_price_gwei), this.chainConfig.gasOptions.maxGasPriceGwei).toString();
|
|
144
137
|
let tx_data = {
|
|
145
|
-
from:
|
|
138
|
+
from: this.wallet.address,
|
|
146
139
|
to,
|
|
147
140
|
value: ethers_1.ethers.utils.parseUnits(real_transfer_amount_gwei, 'gwei'),
|
|
148
141
|
gasLimit: 21000,
|
|
@@ -150,7 +143,7 @@ class AbstractEvmDexTrade extends dist_1.AbastrcatTrade {
|
|
|
150
143
|
nonce: transfer_nonce,
|
|
151
144
|
chainId: this.chainConfig.chainId
|
|
152
145
|
};
|
|
153
|
-
let signedTx = yield
|
|
146
|
+
let signedTx = yield this.wallet.signTransaction(tx_data);
|
|
154
147
|
(0, dist_1.log_info)(`构建转账交易: `, Object.assign(Object.assign({}, tx_data), { real_transfer_amount_gwei,
|
|
155
148
|
real_gas_price_gwei, txhash: ethers_1.ethers.utils.keccak256(signedTx) }));
|
|
156
149
|
return signedTx;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AbastrcatTrade, AppConfig, TradeContext } from "@clonegod/ttd-core/dist";
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
import { DexConfig, EvmChainConfig } from "../types";
|
|
4
|
+
export declare abstract class AbstractEvmDexTradePlus extends AbastrcatTrade {
|
|
5
|
+
protected appConfig: AppConfig;
|
|
6
|
+
protected group_wallets: ethers.Wallet[];
|
|
7
|
+
protected provider: ethers.providers.JsonRpcProvider;
|
|
8
|
+
protected approvedTokens: Map<string, Map<string, boolean>>;
|
|
9
|
+
protected pairContracts: Map<string, ethers.Contract>;
|
|
10
|
+
protected tokenContracts: Map<string, ethers.Contract>;
|
|
11
|
+
protected routerContract: ethers.Contract;
|
|
12
|
+
protected chainConfig: EvmChainConfig;
|
|
13
|
+
protected dexConfig: DexConfig;
|
|
14
|
+
constructor(appConfig: AppConfig);
|
|
15
|
+
protected abstract initConfigs(): void;
|
|
16
|
+
init(): Promise<void>;
|
|
17
|
+
protected getTokenContract(tokenAddress: string): ethers.Contract;
|
|
18
|
+
protected getTokenContractWithWallet(tokenAddress: string, wallet: ethers.Wallet): ethers.Contract;
|
|
19
|
+
protected getGasPriceGwei(context: TradeContext): string;
|
|
20
|
+
protected getBuilderTipAmoutGwei(context: TradeContext): string;
|
|
21
|
+
protected checkTradeTokenApprove(context: TradeContext, wallet: ethers.Wallet, routerAddress?: string): Promise<void>;
|
|
22
|
+
protected checkTokenApprove(tokenAddress: string, tokenSymbol: string, tokenContract: ethers.Contract, spenderAddress: string, wallet: ethers.Wallet): Promise<boolean>;
|
|
23
|
+
protected isNonceRelatedError(error: any): boolean;
|
|
24
|
+
protected isNativeCurrency(symbol: string): boolean;
|
|
25
|
+
protected getWrappedNativeAddress(): string;
|
|
26
|
+
abstract execute(context: TradeContext, retryCount?: number): Promise<string>;
|
|
27
|
+
protected buildTipTransferTx(to: string, transfer_amount_gwei: string, gas_price_gwei: string, transfer_nonce: number, wallet: ethers.Wallet): Promise<string>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
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
|
+
checkTradeTokenApprove(context, wallet, routerAddress) {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const { pool_info } = context;
|
|
72
|
+
const { tokenA, tokenB } = pool_info;
|
|
73
|
+
const router = routerAddress || this.dexConfig.routerAddress;
|
|
74
|
+
if (!this.approvedTokens.has(wallet.address)) {
|
|
75
|
+
this.approvedTokens.set(wallet.address, new Map());
|
|
76
|
+
}
|
|
77
|
+
yield Promise.all([tokenA, tokenB].map((_a) => __awaiter(this, [_a], void 0, function* ({ symbol, address }) {
|
|
78
|
+
const tokenContract = this.getTokenContractWithWallet(address, wallet);
|
|
79
|
+
yield this.checkTokenApprove(address, symbol, tokenContract, router, wallet);
|
|
80
|
+
})));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
checkTokenApprove(tokenAddress, tokenSymbol, tokenContract, spenderAddress, wallet) {
|
|
84
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
85
|
+
if (!this.approvedTokens.has(wallet.address)) {
|
|
86
|
+
this.approvedTokens.set(wallet.address, new Map());
|
|
87
|
+
}
|
|
88
|
+
if (this.approvedTokens.get(wallet.address).get(tokenAddress)) {
|
|
89
|
+
(0, dist_1.log_info)(`钱包 ${wallet.address} 的代币 ${tokenSymbol} 已授权,跳过授权检查`);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
const allowance = yield tokenContract.allowance(wallet.address, spenderAddress);
|
|
93
|
+
const maxAllowance = ethers_1.ethers.constants.MaxUint256.div(2);
|
|
94
|
+
(0, dist_1.log_info)(`代币授权检查:`, {
|
|
95
|
+
walletAddress: wallet.address,
|
|
96
|
+
tokenSymbol,
|
|
97
|
+
currentAllowance: allowance.toString(),
|
|
98
|
+
maxAllowance: maxAllowance.toString(),
|
|
99
|
+
isSufficient: allowance.gte(maxAllowance)
|
|
100
|
+
});
|
|
101
|
+
if (allowance.lt(maxAllowance)) {
|
|
102
|
+
const maxApprovalAmount = ethers_1.ethers.utils.formatUnits(ethers_1.ethers.constants.MaxUint256, yield tokenContract.decimals());
|
|
103
|
+
const tokenSymbolDisplay = tokenSymbol || `${tokenAddress.substring(0, 6)}...`;
|
|
104
|
+
(0, dist_1.log_info)(`正在为钱包 ${wallet.address} 授权代币 ${tokenSymbolDisplay}`, {
|
|
105
|
+
walletAddress: wallet.address,
|
|
106
|
+
tokenAddress,
|
|
107
|
+
tokenSymbol: tokenSymbolDisplay,
|
|
108
|
+
currentAllowance: ethers_1.ethers.utils.formatUnits(allowance, yield tokenContract.decimals()),
|
|
109
|
+
newAllowance: maxApprovalAmount
|
|
110
|
+
});
|
|
111
|
+
const tx = yield tokenContract.approve(spenderAddress, ethers_1.ethers.constants.MaxUint256, {
|
|
112
|
+
gasLimit: this.chainConfig.gasOptions.gasLimit
|
|
113
|
+
});
|
|
114
|
+
yield tx.wait();
|
|
115
|
+
this.approvedTokens.get(wallet.address).set(tokenAddress, true);
|
|
116
|
+
(0, dist_1.log_info)(`钱包 ${wallet.address} 的代币 ${tokenSymbolDisplay} 授权完成,数量: ${maxApprovalAmount} ${tokenSymbolDisplay}, 交易哈希: ${tx.hash}`);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this.approvedTokens.get(wallet.address).set(tokenAddress, true);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
isNonceRelatedError(error) {
|
|
126
|
+
if (!error)
|
|
127
|
+
return false;
|
|
128
|
+
const errorMessage = error.message ? error.message.toLowerCase() : '';
|
|
129
|
+
const nonceErrorKeywords = [
|
|
130
|
+
'nonce',
|
|
131
|
+
'nonce too low',
|
|
132
|
+
'nonce too high',
|
|
133
|
+
'nonce has already been used',
|
|
134
|
+
'already known',
|
|
135
|
+
'replacement transaction underpriced',
|
|
136
|
+
'transaction with same nonce',
|
|
137
|
+
'transaction nonce is too low',
|
|
138
|
+
'invalid transaction nonce',
|
|
139
|
+
'insufficient funds',
|
|
140
|
+
'sign bundle to get failure details',
|
|
141
|
+
];
|
|
142
|
+
return nonceErrorKeywords.some(keyword => errorMessage.includes(keyword));
|
|
143
|
+
}
|
|
144
|
+
isNativeCurrency(symbol) {
|
|
145
|
+
return symbol.toUpperCase() === this.chainConfig.nativeCurrency;
|
|
146
|
+
}
|
|
147
|
+
getWrappedNativeAddress() {
|
|
148
|
+
return this.chainConfig.wrappedNativeCurrencyAddress;
|
|
149
|
+
}
|
|
150
|
+
buildTipTransferTx(to, transfer_amount_gwei, gas_price_gwei, transfer_nonce, wallet) {
|
|
151
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
let real_transfer_amount_gwei = Math.min(Number(transfer_amount_gwei), this.chainConfig.gasOptions.maxTipAmountGwei).toString();
|
|
153
|
+
let real_gas_price_gwei = Math.min(Number(gas_price_gwei), this.chainConfig.gasOptions.maxGasPriceGwei).toString();
|
|
154
|
+
let tx_data = {
|
|
155
|
+
from: wallet.address,
|
|
156
|
+
to,
|
|
157
|
+
value: ethers_1.ethers.utils.parseUnits(real_transfer_amount_gwei, 'gwei'),
|
|
158
|
+
gasLimit: 21000,
|
|
159
|
+
gasPrice: ethers_1.ethers.utils.parseUnits(real_gas_price_gwei, 'gwei'),
|
|
160
|
+
nonce: transfer_nonce,
|
|
161
|
+
chainId: this.chainConfig.chainId
|
|
162
|
+
};
|
|
163
|
+
let signedTx = yield wallet.signTransaction(tx_data);
|
|
164
|
+
(0, dist_1.log_info)(`构建转账交易: `, Object.assign(Object.assign({}, tx_data), { real_transfer_amount_gwei,
|
|
165
|
+
real_gas_price_gwei, txhash: ethers_1.ethers.utils.keccak256(signedTx) }));
|
|
166
|
+
return signedTx;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
exports.AbstractEvmDexTradePlus = AbstractEvmDexTradePlus;
|