@clonegod/ttd-bsc-common 3.1.5 → 3.1.12
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/bsc_env_args.d.ts +4 -4
- package/dist/config/bsc_env_args.js +21 -13
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/trade/abstract_dex_trade.d.ts +8 -4
- package/dist/trade/abstract_dex_trade.js +62 -13
- package/dist/trade/caller_manager.js +7 -7
- package/dist/types/config_types.d.ts +2 -3
- package/package.json +2 -2
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { EnvArgs } from '@clonegod/ttd-core';
|
|
2
2
|
export declare class BscEnvArgs extends EnvArgs {
|
|
3
|
-
large_trade_threshold_usd: number;
|
|
4
3
|
gas_price_gwei: number;
|
|
5
4
|
gas_limit: number;
|
|
6
5
|
tip_amount_gwei: number;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
send_tx_blockrazor_bundle: boolean;
|
|
7
|
+
send_tx_48club_bundle: boolean;
|
|
8
|
+
send_tx_48club_bundle_ws: boolean;
|
|
9
|
+
send_tx_blox_bundle_ws: boolean;
|
|
10
10
|
constructor();
|
|
11
11
|
print(moduleName?: string): void;
|
|
12
12
|
}
|
|
@@ -3,34 +3,42 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BscEnvArgs = void 0;
|
|
4
4
|
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
5
5
|
const BSC_KEYS = [
|
|
6
|
-
'large_trade_threshold_usd',
|
|
7
6
|
'gas_price_gwei',
|
|
8
7
|
'gas_limit',
|
|
9
8
|
'tip_amount_gwei',
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
9
|
+
'send_tx_blockrazor_bundle',
|
|
10
|
+
'send_tx_48club_bundle',
|
|
11
|
+
'send_tx_48club_bundle_ws',
|
|
12
|
+
'send_tx_blox_bundle_ws',
|
|
13
13
|
];
|
|
14
14
|
(0, ttd_core_1.registerEnvVars)({
|
|
15
|
-
large_trade_threshold_usd: { env: 'LARGE_TRADE_THRESHOLD_USD', type: 'number', default: 500, desc: '大额交易识别阈值(USD)' },
|
|
16
15
|
gas_price_gwei: { env: 'GAS_PRICE_GWEI', type: 'number', default: 1, desc: '交易 gas 价格(Gwei)' },
|
|
17
16
|
gas_limit: { env: 'GAS_LIMIT', type: 'number', default: 300000, desc: '交易 gas 上限' },
|
|
18
|
-
tip_amount_gwei: { env: '
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
tip_amount_gwei: { env: 'TIP_AMOUNT_GWEI', type: 'number', default: 10000, desc: 'Builder tip(Gwei)' },
|
|
18
|
+
send_tx_blockrazor_bundle: { env: 'SEND_TX_BLOCKRAZOR_BUNDLE', type: 'boolean', default: false, desc: 'BlockRazor Bundle 开关' },
|
|
19
|
+
send_tx_48club_bundle: { env: 'SEND_TX_48CLUB_BUNDLE', type: 'boolean', default: false, desc: '48Club Bundle HTTP 开关' },
|
|
20
|
+
send_tx_48club_bundle_ws: { env: 'SEND_TX_48CLUB_BUNDLE_WS', type: 'boolean', default: false, desc: '48Club Bundle WS 开关' },
|
|
21
|
+
send_tx_blox_bundle_ws: { env: 'SEND_TX_BLOX_BUNDLE_WS', type: 'boolean', default: false, desc: 'BloxRoute Bundle WS 开关' },
|
|
22
|
+
stream_quote_ws_host: { env: 'STREAM_QUOTE_WS_HOST', type: 'string', default: '127.0.0.1', desc: 'stream-quote WS 地址' },
|
|
23
|
+
min_quote_interval_ms: { env: 'MIN_QUOTE_INTERVAL_MS', type: 'number', default: 10000, desc: '最小询价间隔(ms)' },
|
|
24
|
+
skip_price_feed_yynode: { env: 'SKIP_PRICE_FEED_YYNODE', type: 'boolean', default: false, desc: '跳过 YYNode PriceFeed' },
|
|
25
|
+
push_price_feed: { env: 'PUSH_PRICE_FEED', type: 'boolean', default: false, desc: 'PriceFeed 推送开关' },
|
|
26
|
+
need_block_time_info: { env: 'NEED_BLOCK_TIME_INFO', type: 'boolean', default: false, desc: '区块时间信息开关' },
|
|
27
|
+
tick_cache_neighboring_words: { env: 'TICK_CACHE_NEIGHBORING_WORDS', type: 'number', default: 2, desc: 'tick 预加载 words 数' },
|
|
28
|
+
tick_cache_ttl: { env: 'TICK_CACHE_TTL', type: 'number', default: 30000, desc: 'tick 缓存 TTL(ms)' },
|
|
29
|
+
tick_cache_min_update_interval: { env: 'TICK_CACHE_MIN_UPDATE_INTERVAL', type: 'number', default: 3000, desc: 'tick 最小刷新间隔(ms)' },
|
|
22
30
|
});
|
|
23
31
|
class BscEnvArgs extends ttd_core_1.EnvArgs {
|
|
24
32
|
constructor() {
|
|
25
33
|
super();
|
|
26
34
|
const cfg = (0, ttd_core_1.loadEnvConfig)(BSC_KEYS);
|
|
27
|
-
this.large_trade_threshold_usd = cfg.large_trade_threshold_usd;
|
|
28
35
|
this.gas_price_gwei = cfg.gas_price_gwei;
|
|
29
36
|
this.gas_limit = cfg.gas_limit;
|
|
30
37
|
this.tip_amount_gwei = cfg.tip_amount_gwei;
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
38
|
+
this.send_tx_blockrazor_bundle = cfg.send_tx_blockrazor_bundle;
|
|
39
|
+
this.send_tx_48club_bundle = cfg.send_tx_48club_bundle;
|
|
40
|
+
this.send_tx_48club_bundle_ws = cfg.send_tx_48club_bundle_ws;
|
|
41
|
+
this.send_tx_blox_bundle_ws = cfg.send_tx_blox_bundle_ws;
|
|
34
42
|
}
|
|
35
43
|
print(moduleName) {
|
|
36
44
|
super.print(moduleName);
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -20,6 +20,5 @@ __exportStar(require("./quote"), exports);
|
|
|
20
20
|
__exportStar(require("./trade"), exports);
|
|
21
21
|
__exportStar(require("./types"), exports);
|
|
22
22
|
__exportStar(require("./utils"), exports);
|
|
23
|
-
__exportStar(require("./redis"), exports);
|
|
24
23
|
__exportStar(require("./ws"), exports);
|
|
25
24
|
__exportStar(require("./send-tx"), exports);
|
|
@@ -3,7 +3,7 @@ import { ethers } from "ethers";
|
|
|
3
3
|
import { EvmChainConfig } from "../types";
|
|
4
4
|
import { CallerManager } from "./caller_manager";
|
|
5
5
|
import { ITransactionSender } from "../send-tx";
|
|
6
|
-
import {
|
|
6
|
+
import { RedisClient } from "@clonegod/ttd-core";
|
|
7
7
|
export interface TradeConfig {
|
|
8
8
|
vaultAddress: string;
|
|
9
9
|
executorIds: Record<string, string>;
|
|
@@ -19,17 +19,21 @@ export declare abstract class AbstractDexTrade extends AbastrcatTrade {
|
|
|
19
19
|
protected provider: any;
|
|
20
20
|
protected transactionSender: ITransactionSender;
|
|
21
21
|
protected chainConfig: EvmChainConfig;
|
|
22
|
-
protected redisClient:
|
|
22
|
+
protected redisClient: RedisClient;
|
|
23
23
|
protected tradeConfig: TradeConfig;
|
|
24
24
|
protected vaultInterface: any;
|
|
25
25
|
protected chainNameLower: string;
|
|
26
26
|
constructor(appConfig: AppConfig);
|
|
27
|
-
protected
|
|
27
|
+
protected initConfigs(): void;
|
|
28
28
|
abstract encodeTradeData(context: TradeContext): TradeCalldata;
|
|
29
29
|
init(transactionSender?: ITransactionSender): Promise<void>;
|
|
30
30
|
execute(context: TradeContext): Promise<string>;
|
|
31
31
|
private scanCallerFiles;
|
|
32
|
-
protected
|
|
32
|
+
protected buildGasFields(context: TradeContext): {
|
|
33
|
+
mainGasFields: any;
|
|
34
|
+
tipGasPriceGwei: string;
|
|
35
|
+
};
|
|
36
|
+
protected getLegacyGasPriceGwei(context: TradeContext): string;
|
|
33
37
|
protected getBuilderTipAmoutGwei(context: TradeContext): string;
|
|
34
38
|
protected buildTipTransferTx(to: string, transfer_amount_gwei: string, gas_price_gwei: string, nonce: number, wallet: ethers.Wallet): Promise<string>;
|
|
35
39
|
protected determineInputOutputTokens(order_msg: any, pool_info: any): {
|
|
@@ -42,10 +42,11 @@ const ttd_core_1 = require("@clonegod/ttd-core");
|
|
|
42
42
|
const logger = (0, ttd_core_1.createLogger)(__filename);
|
|
43
43
|
const fs = __importStar(require("fs"));
|
|
44
44
|
const path = __importStar(require("path"));
|
|
45
|
+
const constants_1 = require("../common/constants");
|
|
45
46
|
const caller_manager_1 = require("./caller_manager");
|
|
46
47
|
const send_tx_1 = require("../send-tx");
|
|
47
48
|
const base_tx_result_checker_1 = require("./check/base_tx_result_checker");
|
|
48
|
-
const
|
|
49
|
+
const ttd_core_2 = require("@clonegod/ttd-core");
|
|
49
50
|
const trade_direction_1 = require("../utils/trade_direction");
|
|
50
51
|
const ethers_compat_1 = require("../utils/ethers_compat");
|
|
51
52
|
const fast_signer_1 = require("../utils/fast_signer");
|
|
@@ -79,9 +80,30 @@ class AbstractDexTrade extends ttd_core_1.AbastrcatTrade {
|
|
|
79
80
|
this.appConfig = appConfig;
|
|
80
81
|
this.initConfigs();
|
|
81
82
|
this.chainNameLower = this.appConfig.env_args.chain_id.toLowerCase();
|
|
82
|
-
this.redisClient = new
|
|
83
|
+
this.redisClient = new ttd_core_2.RedisClient(`${this.chainNameLower}:tx`);
|
|
83
84
|
this.vaultInterface = new ethers_compat_1.ethersCompat.Interface(VAULT_ABI);
|
|
84
85
|
}
|
|
86
|
+
initConfigs() {
|
|
87
|
+
const env = this.appConfig.env_args;
|
|
88
|
+
this.chainConfig = {
|
|
89
|
+
chainId: 56,
|
|
90
|
+
nativeCurrency: 'BNB',
|
|
91
|
+
wrappedNativeCurrencyAddress: constants_1.WBNB_ADDRESS,
|
|
92
|
+
chainName: 'Binance Smart Chain',
|
|
93
|
+
rpcEndpoint: env.rpc_endpoint,
|
|
94
|
+
blockConfirmations: 12,
|
|
95
|
+
gasOptions: {
|
|
96
|
+
gasLimit: env.gas_limit,
|
|
97
|
+
defaultGasPriceGwei: env.gas_price_gwei,
|
|
98
|
+
maxGasPriceGwei: 10,
|
|
99
|
+
maxFeePerGasGwei: 10,
|
|
100
|
+
maxPriorityFeePerGasGwei: 3,
|
|
101
|
+
defaultTipAmountGwei: env.tip_amount_gwei,
|
|
102
|
+
maxTipAmountGwei: 500000,
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
this.tradeConfig = buildTradeConfig();
|
|
106
|
+
}
|
|
85
107
|
async init(transactionSender) {
|
|
86
108
|
(0, fast_signer_1.patchEthersV5Signer)();
|
|
87
109
|
this.provider = new ethers_compat_1.ethersCompat.JsonRpcProvider(this.chainConfig.rpcEndpoint);
|
|
@@ -142,8 +164,7 @@ class AbstractDexTrade extends ttd_core_1.AbastrcatTrade {
|
|
|
142
164
|
trace.mark('encode');
|
|
143
165
|
const deadline = Math.floor(Date.now() / 1000) + 60;
|
|
144
166
|
const vaultCalldata = this.vaultInterface.encodeFunctionData('delegatecallExecutorToTrade', [executorId, data, deadline]);
|
|
145
|
-
const
|
|
146
|
-
const realGasPriceGwei = Math.min(Number(gasPriceGwei), this.chainConfig.gasOptions.maxGasPriceGwei).toString();
|
|
167
|
+
const { mainGasFields, tipGasPriceGwei } = this.buildGasFields(context);
|
|
147
168
|
const tipNonce = nonce + 1;
|
|
148
169
|
const transfer_amount_gwei = this.getBuilderTipAmoutGwei(context);
|
|
149
170
|
context.ui_tip_amount = new decimal_js_1.default(transfer_amount_gwei).div(10 ** 9).toNumber();
|
|
@@ -151,19 +172,22 @@ class AbstractDexTrade extends ttd_core_1.AbastrcatTrade {
|
|
|
151
172
|
to: this.tradeConfig.vaultAddress,
|
|
152
173
|
data: vaultCalldata,
|
|
153
174
|
gasLimit: this.chainConfig.gasOptions.gasLimit,
|
|
154
|
-
|
|
175
|
+
...mainGasFields,
|
|
155
176
|
nonce,
|
|
156
177
|
chainId: this.chainConfig.chainId,
|
|
157
178
|
value: 0,
|
|
158
179
|
};
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
send_tx_1.BSC_EOA_ADDRESS.
|
|
163
|
-
|
|
180
|
+
const env = this.appConfig.env_args;
|
|
181
|
+
const builderAddresses = [];
|
|
182
|
+
if (env.send_tx_blockrazor_bundle)
|
|
183
|
+
builderAddresses.push(send_tx_1.BSC_EOA_ADDRESS.BLOCKRAZOR);
|
|
184
|
+
if (env.send_tx_48club_bundle || env.send_tx_48club_bundle_ws)
|
|
185
|
+
builderAddresses.push(send_tx_1.BSC_EOA_ADDRESS._48CLUB);
|
|
186
|
+
if (env.send_tx_blox_bundle_ws)
|
|
187
|
+
builderAddresses.push(send_tx_1.BSC_EOA_ADDRESS.BLXR);
|
|
164
188
|
const [rawMainTx, ...rawTips] = await Promise.all([
|
|
165
189
|
caller.signTransaction(mainTx),
|
|
166
|
-
...builderAddresses.map(addr => this.buildTipTransferTx(addr, transfer_amount_gwei,
|
|
190
|
+
...builderAddresses.map(addr => this.buildTipTransferTx(addr, transfer_amount_gwei, tipGasPriceGwei, tipNonce, caller)),
|
|
167
191
|
]);
|
|
168
192
|
trace.mark('sign');
|
|
169
193
|
const ensure0x = (tx) => tx.startsWith('0x') ? tx : `0x${tx}`;
|
|
@@ -174,7 +198,7 @@ class AbstractDexTrade extends ttd_core_1.AbastrcatTrade {
|
|
|
174
198
|
builderAddresses.forEach((addr, idx) => tipTxMap.set(addr, signedTips[idx]));
|
|
175
199
|
trace.set('txid', txid);
|
|
176
200
|
trace.set('nonce', nonce);
|
|
177
|
-
trace.set('
|
|
201
|
+
trace.set('gasType', mainGasFields.type === 2 ? 'EIP1559' : 'Legacy');
|
|
178
202
|
trace.set('tip', transfer_amount_gwei);
|
|
179
203
|
const only_bundle = order_msg.is_dex_maker;
|
|
180
204
|
await this.transactionSender.sendTransaction(signedMainTx, tipTxMap, order_trace_id, pair, only_bundle, trace);
|
|
@@ -228,7 +252,32 @@ class AbstractDexTrade extends ttd_core_1.AbastrcatTrade {
|
|
|
228
252
|
.sort();
|
|
229
253
|
return files.map(f => f.replace('.json', ''));
|
|
230
254
|
}
|
|
231
|
-
|
|
255
|
+
buildGasFields(context) {
|
|
256
|
+
const strategy = context.trade_runtime.settings.strategy;
|
|
257
|
+
const opts = this.chainConfig.gasOptions;
|
|
258
|
+
const legacyGasPrice = this.getLegacyGasPriceGwei(context);
|
|
259
|
+
const tipGasPriceGwei = Math.min(Number(legacyGasPrice), opts.maxGasPriceGwei).toString();
|
|
260
|
+
if (strategy.evm_tx_type === 2) {
|
|
261
|
+
const maxFee = Math.min(strategy.evm_max_fee_per_gas_gwei || opts.maxFeePerGasGwei, opts.maxFeePerGasGwei);
|
|
262
|
+
const maxPriority = Math.min(strategy.evm_max_priority_fee_per_gas_gwei ?? 0, opts.maxPriorityFeePerGasGwei);
|
|
263
|
+
return {
|
|
264
|
+
mainGasFields: {
|
|
265
|
+
type: 2,
|
|
266
|
+
maxFeePerGas: ethers_compat_1.ethersCompat.parseUnits(maxFee.toString(), 'gwei'),
|
|
267
|
+
maxPriorityFeePerGas: ethers_compat_1.ethersCompat.parseUnits(maxPriority.toString(), 'gwei'),
|
|
268
|
+
},
|
|
269
|
+
tipGasPriceGwei,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
const realGasPrice = Math.min(Number(legacyGasPrice), opts.maxGasPriceGwei).toString();
|
|
273
|
+
return {
|
|
274
|
+
mainGasFields: {
|
|
275
|
+
gasPrice: ethers_compat_1.ethersCompat.parseUnits(realGasPrice, 'gwei'),
|
|
276
|
+
},
|
|
277
|
+
tipGasPriceGwei,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
getLegacyGasPriceGwei(context) {
|
|
232
281
|
let { evm_gas_price_gwei } = context.trade_runtime.settings.strategy;
|
|
233
282
|
if (!evm_gas_price_gwei || evm_gas_price_gwei <= 0) {
|
|
234
283
|
evm_gas_price_gwei = this.chainConfig.gasOptions.defaultGasPriceGwei;
|
|
@@ -4,7 +4,7 @@ exports.CallerManager = void 0;
|
|
|
4
4
|
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
5
5
|
const logger = (0, dist_1.createLogger)(__filename);
|
|
6
6
|
const ethers_1 = require("ethers");
|
|
7
|
-
const
|
|
7
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
8
8
|
const CALLER_NONCE_KEY = 'caller:nonce';
|
|
9
9
|
const CALLER_LAST_USED_KEY = 'caller:last_used';
|
|
10
10
|
const VAULT_CALLERS_KEY = 'vault:callers';
|
|
@@ -12,13 +12,13 @@ class CallerManager {
|
|
|
12
12
|
constructor(config) {
|
|
13
13
|
this.callers = [];
|
|
14
14
|
this.config = config;
|
|
15
|
-
this.redis = new
|
|
15
|
+
this.redis = new ttd_core_1.RedisClient(`${config.chainName}:caller`);
|
|
16
16
|
}
|
|
17
17
|
async init() {
|
|
18
18
|
const walletInfos = (0, dist_1.load_wallet_multi)(this.config.callerGroupIds, false);
|
|
19
19
|
const allWallets = walletInfos.map(info => new ethers_1.ethers.Wallet(info.private_key, this.config.provider));
|
|
20
20
|
const vaultCallersKey = `${this.config.chainName}:${VAULT_CALLERS_KEY}`;
|
|
21
|
-
const rawJson = await this.redis.
|
|
21
|
+
const rawJson = await this.redis.hget(vaultCallersKey, this.config.groupId);
|
|
22
22
|
let allowedAddresses = [];
|
|
23
23
|
if (rawJson) {
|
|
24
24
|
try {
|
|
@@ -96,13 +96,13 @@ class CallerManager {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
callerAddr = addresses[selectedIdx];
|
|
99
|
-
await this.redis.
|
|
99
|
+
await this.redis.hset(lastUsedKey, callerAddr, String(Date.now()), 24 * 60 * 60);
|
|
100
100
|
}
|
|
101
101
|
finally {
|
|
102
102
|
this.redis.releaseLock(lockKey, lockValue).catch(() => { });
|
|
103
103
|
}
|
|
104
104
|
const nonceKey = this.getNonceRedisKey();
|
|
105
|
-
const nonceStr = await this.redis.
|
|
105
|
+
const nonceStr = await this.redis.hget(nonceKey, callerAddr);
|
|
106
106
|
if (nonceStr === null || nonceStr === undefined) {
|
|
107
107
|
throw new Error(`Caller ${callerAddr} nonce not found in Redis, stream-trade may not be running`);
|
|
108
108
|
}
|
|
@@ -140,7 +140,7 @@ class CallerManager {
|
|
|
140
140
|
}
|
|
141
141
|
async getNonce(address) {
|
|
142
142
|
const nonceKey = this.getNonceRedisKey();
|
|
143
|
-
const val = await this.redis.
|
|
143
|
+
const val = await this.redis.hget(nonceKey, address.toLowerCase());
|
|
144
144
|
if (val !== null && val !== undefined) {
|
|
145
145
|
return parseInt(val, 10);
|
|
146
146
|
}
|
|
@@ -148,7 +148,7 @@ class CallerManager {
|
|
|
148
148
|
}
|
|
149
149
|
async setNonce(address, nonce) {
|
|
150
150
|
const nonceKey = this.getNonceRedisKey();
|
|
151
|
-
await this.redis.
|
|
151
|
+
await this.redis.hset(nonceKey, address.toLowerCase(), String(nonce), 24 * 60 * 60);
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
exports.CallerManager = CallerManager;
|
|
@@ -2,11 +2,10 @@ export interface GasOptions {
|
|
|
2
2
|
gasLimit: number;
|
|
3
3
|
defaultGasPriceGwei: number;
|
|
4
4
|
maxGasPriceGwei: number;
|
|
5
|
+
maxFeePerGasGwei: number;
|
|
6
|
+
maxPriorityFeePerGasGwei: number;
|
|
5
7
|
defaultTipAmountGwei: number;
|
|
6
8
|
maxTipAmountGwei: number;
|
|
7
|
-
txStatusCheckIntervalMills: number;
|
|
8
|
-
txCancelTimeoutMills: number;
|
|
9
|
-
txCancelGasBoostFactor: number;
|
|
10
9
|
}
|
|
11
10
|
export interface EvmChainConfig {
|
|
12
11
|
chainId: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clonegod/ttd-bsc-common",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.12",
|
|
4
4
|
"description": "BSC common library",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"push": "npm run build && npm publish"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@clonegod/ttd-core": "
|
|
17
|
+
"@clonegod/ttd-core": "3.1.12",
|
|
18
18
|
"axios": "^1.12.0",
|
|
19
19
|
"dotenv": "^16.4.7",
|
|
20
20
|
"ethers": "^5.8.0",
|