@clonegod/ttd-base-common 1.0.0
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/common/abi.d.ts +1 -0
- package/dist/common/abi.js +14 -0
- package/dist/common/constants.d.ts +18 -0
- package/dist/common/constants.js +34 -0
- package/dist/common/index.d.ts +2 -0
- package/dist/common/index.js +18 -0
- package/dist/config/base_env_args.d.ts +11 -0
- package/dist/config/base_env_args.js +19 -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/index.d.ts +7 -0
- package/dist/index.js +23 -0
- package/dist/quote/event/index.d.ts +1 -0
- package/dist/quote/event/index.js +17 -0
- package/dist/quote/event/pool_event_listener.d.ts +53 -0
- package/dist/quote/event/pool_event_listener.js +334 -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/index.d.ts +2 -0
- package/dist/quote/index.js +18 -0
- package/dist/quote/pricing/index.d.ts +2 -0
- package/dist/quote/pricing/index.js +18 -0
- package/dist/quote/pricing/pool.d.ts +13 -0
- package/dist/quote/pricing/pool.js +21 -0
- package/dist/quote/pricing/token_price_cache.d.ts +10 -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.d.ts +27 -0
- package/dist/trade/abstract_dex_trade.js +153 -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/index.d.ts +1 -0
- package/dist/trade/check/index.js +17 -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/index.d.ts +5 -0
- package/dist/trade/index.js +21 -0
- package/dist/trade/parse/abstract_parser.d.ts +8 -0
- package/dist/trade/parse/abstract_parser.js +2 -0
- package/dist/trade/parse/base_parser.d.ts +19 -0
- package/dist/trade/parse/base_parser.js +69 -0
- package/dist/trade/parse/index.d.ts +1 -0
- package/dist/trade/parse/index.js +5 -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/types/config_types.d.ts +28 -0
- package/dist/types/config_types.js +2 -0
- package/dist/types/event_types.d.ts +30 -0
- package/dist/types/event_types.js +2 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +20 -0
- package/dist/types/quote_types.d.ts +27 -0
- package/dist/types/quote_types.js +2 -0
- package/dist/types/trade_types.d.ts +38 -0
- package/dist/types/trade_types.js +8 -0
- package/dist/utils/gas_helper.d.ts +5 -0
- package/dist/utils/gas_helper.js +72 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.js +43 -0
- package/package.json +30 -0
|
@@ -0,0 +1,178 @@
|
|
|
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
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
16
|
+
dotenv_1.default.config();
|
|
17
|
+
const ethers_1 = require("ethers");
|
|
18
|
+
const constants_1 = require("../../common/constants");
|
|
19
|
+
const log = (level, msg, data) => {
|
|
20
|
+
const timestamp = new Date().toISOString();
|
|
21
|
+
console.log(`${timestamp} [${level}] ${msg}`);
|
|
22
|
+
if (data)
|
|
23
|
+
console.log(data);
|
|
24
|
+
};
|
|
25
|
+
function verifySwapEventParsing() {
|
|
26
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
+
const wsEndpoint = process.env.WS_ENDPOINT || '';
|
|
28
|
+
const poolAddress = '0xc1a780989734a0e5df875cebe410748562e1c5e6';
|
|
29
|
+
const poolName = 'B2/BNB';
|
|
30
|
+
log('INFO', `正在连接到WebSocket: ${wsEndpoint}`);
|
|
31
|
+
const wsProvider = new ethers_1.ethers.providers.WebSocketProvider(wsEndpoint);
|
|
32
|
+
yield wsProvider.ready;
|
|
33
|
+
log('INFO', '连接成功,准备测试事件解析');
|
|
34
|
+
const updatedSwapHash = ethers_1.ethers.utils.id(constants_1.EVENT_SIGNATURES.SWAP_RAW);
|
|
35
|
+
log('INFO', `更新后的Swap事件哈希: ${updatedSwapHash}`);
|
|
36
|
+
const capturedSwapHash = '0x19b47279256b2a23a1665c810c8d55a1758940ee09377d4f8d26497a3577dc83';
|
|
37
|
+
log('INFO', `之前捕获的Swap事件哈希: ${capturedSwapHash}`);
|
|
38
|
+
if (updatedSwapHash === capturedSwapHash) {
|
|
39
|
+
log('INFO', '✅ 事件哈希匹配,签名修改正确');
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
log('WARN', '⚠️ 事件哈希不匹配,可能需要调整签名格式');
|
|
43
|
+
}
|
|
44
|
+
const methods = [
|
|
45
|
+
{
|
|
46
|
+
name: "方法1:直接使用更新后的SWAP_RAW签名",
|
|
47
|
+
createInterface: () => new ethers_1.ethers.utils.Interface([constants_1.EVENT_SIGNATURES.SWAP_RAW])
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "方法2:添加event前缀(推荐方式)",
|
|
51
|
+
createInterface: () => new ethers_1.ethers.utils.Interface([`event ${constants_1.EVENT_SIGNATURES.SWAP_RAW}`])
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "方法3:带错误处理的解析方式(新增)",
|
|
55
|
+
parse: (log) => {
|
|
56
|
+
try {
|
|
57
|
+
const completeSwapDefinition = "event " + constants_1.EVENT_SIGNATURES.SWAP_RAW;
|
|
58
|
+
const iface = new ethers_1.ethers.utils.Interface([completeSwapDefinition]);
|
|
59
|
+
try {
|
|
60
|
+
return iface.parseLog(log);
|
|
61
|
+
}
|
|
62
|
+
catch (parseError) {
|
|
63
|
+
if (parseError.code === 'BUFFER_OVERRUN') {
|
|
64
|
+
console.log('\x1b[33m%s\x1b[0m', `检测到BUFFER_OVERRUN错误,触发备用解析流程`);
|
|
65
|
+
return {
|
|
66
|
+
args: {
|
|
67
|
+
sender: "备用解析流程",
|
|
68
|
+
recipient: "备用解析流程",
|
|
69
|
+
amount0: ethers_1.ethers.BigNumber.from(0),
|
|
70
|
+
amount1: ethers_1.ethers.BigNumber.from(0),
|
|
71
|
+
sqrtPriceX96: ethers_1.ethers.BigNumber.from(0),
|
|
72
|
+
tick: 0,
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
throw parseError;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
log('INFO', `设置原始事件监听器,只按地址过滤`);
|
|
86
|
+
const filter = { address: poolAddress };
|
|
87
|
+
wsProvider.on(filter, (log) => {
|
|
88
|
+
var _a, _b, _c, _d;
|
|
89
|
+
try {
|
|
90
|
+
const isSwapEvent = (log.topics[0] === capturedSwapHash || log.topics[0] === updatedSwapHash);
|
|
91
|
+
if (isSwapEvent) {
|
|
92
|
+
console.log('\x1b[32m%s\x1b[0m', `收到Swap事件: 交易哈希=${log.transactionHash}, 区块=${log.blockNumber}`);
|
|
93
|
+
for (const method of methods) {
|
|
94
|
+
try {
|
|
95
|
+
let parsedEvent;
|
|
96
|
+
if (method.parse) {
|
|
97
|
+
parsedEvent = method.parse(log);
|
|
98
|
+
}
|
|
99
|
+
else if (method.createInterface) {
|
|
100
|
+
const iface = method.createInterface();
|
|
101
|
+
parsedEvent = iface.parseLog(log);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
throw new Error("方法未正确定义");
|
|
105
|
+
}
|
|
106
|
+
console.log('\x1b[32m%s\x1b[0m', `${method.name} - 解析成功!`);
|
|
107
|
+
console.log({
|
|
108
|
+
sender: parsedEvent.args.sender,
|
|
109
|
+
recipient: parsedEvent.args.recipient,
|
|
110
|
+
amount0: (_a = parsedEvent.args.amount0) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
111
|
+
amount1: (_b = parsedEvent.args.amount1) === null || _b === void 0 ? void 0 : _b.toString(),
|
|
112
|
+
sqrtPriceX96: (_c = parsedEvent.args.sqrtPriceX96) === null || _c === void 0 ? void 0 : _c.toString(),
|
|
113
|
+
tick: (_d = parsedEvent.args.tick) === null || _d === void 0 ? void 0 : _d.toString()
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.log('\x1b[31m%s\x1b[0m', `${method.name} - 解析失败: ${error.message}`);
|
|
118
|
+
console.log(error.code ? `错误代码: ${error.code}` : '无错误代码');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
console.log('----------------------------');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
log('ERROR', `处理事件出错:`, error);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
log('INFO', '测试脚本启动成功,等待Swap事件...');
|
|
129
|
+
const interval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
|
|
130
|
+
try {
|
|
131
|
+
const blockNumber = yield wsProvider.getBlockNumber();
|
|
132
|
+
log('DEBUG', `心跳检测成功,当前区块: ${blockNumber}`);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
log('ERROR', '心跳检测失败,可能断开连接');
|
|
136
|
+
}
|
|
137
|
+
}), 15000);
|
|
138
|
+
process.on('SIGINT', () => {
|
|
139
|
+
log('INFO', '收到退出信号,正在清理...');
|
|
140
|
+
clearInterval(interval);
|
|
141
|
+
wsProvider.removeAllListeners();
|
|
142
|
+
if (wsProvider._websocket)
|
|
143
|
+
wsProvider._websocket.close();
|
|
144
|
+
log('INFO', '已清理,退出程序');
|
|
145
|
+
process.exit(0);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function updateCurrentPoolState(wsProvider, poolAddress, poolName) {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
log('INFO', `[${poolName}] 模拟备用方法:通过直接查询获取池状态`);
|
|
152
|
+
try {
|
|
153
|
+
const poolAbi = [
|
|
154
|
+
"function slot0() external view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked)",
|
|
155
|
+
"function liquidity() external view returns (uint128)"
|
|
156
|
+
];
|
|
157
|
+
const poolContract = new ethers_1.ethers.Contract(poolAddress, poolAbi, wsProvider);
|
|
158
|
+
const [slot0Data, liquidityData] = yield Promise.all([
|
|
159
|
+
poolContract.slot0(),
|
|
160
|
+
poolContract.liquidity()
|
|
161
|
+
]);
|
|
162
|
+
const [sqrtPriceX96, tick] = slot0Data;
|
|
163
|
+
const liquidity = liquidityData;
|
|
164
|
+
log('INFO', `[${poolName}] 备用方法成功: tick=${tick}, liquidity=${liquidity.toString()}`);
|
|
165
|
+
return { sqrtPriceX96, tick, liquidity };
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
log('ERROR', `[${poolName}] 备用方法失败:`, error);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
verifySwapEventParsing()
|
|
174
|
+
.then(() => log('INFO', '验证脚本启动成功'))
|
|
175
|
+
.catch(error => {
|
|
176
|
+
log('ERROR', '验证脚本启动失败:', error);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
__exportStar(require("./event"), exports);
|
|
18
|
+
__exportStar(require("./pricing"), exports);
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
__exportStar(require("./token_price_cache"), exports);
|
|
18
|
+
__exportStar(require("./pool"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
export declare class Pool {
|
|
3
|
+
readonly address: string;
|
|
4
|
+
readonly token0: string;
|
|
5
|
+
readonly token1: string;
|
|
6
|
+
readonly fee: number;
|
|
7
|
+
sqrtPriceX96: ethers.BigNumber;
|
|
8
|
+
liquidity: ethers.BigNumber;
|
|
9
|
+
tickCurrent: number;
|
|
10
|
+
readonly tickSpacing: number;
|
|
11
|
+
constructor(address: string, token0: string, token1: string, fee: number, sqrtPriceX96: ethers.BigNumber, tickCurrent: number, liquidity: ethers.BigNumber, tickSpacing: number);
|
|
12
|
+
updateState(sqrtPriceX96: ethers.BigNumber, tickCurrent: number, liquidity: ethers.BigNumber): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Pool = void 0;
|
|
4
|
+
class Pool {
|
|
5
|
+
constructor(address, token0, token1, fee, sqrtPriceX96, tickCurrent, liquidity, tickSpacing) {
|
|
6
|
+
this.address = address;
|
|
7
|
+
this.token0 = token0;
|
|
8
|
+
this.token1 = token1;
|
|
9
|
+
this.fee = fee;
|
|
10
|
+
this.sqrtPriceX96 = sqrtPriceX96;
|
|
11
|
+
this.liquidity = liquidity;
|
|
12
|
+
this.tickCurrent = tickCurrent;
|
|
13
|
+
this.tickSpacing = tickSpacing;
|
|
14
|
+
}
|
|
15
|
+
updateState(sqrtPriceX96, tickCurrent, liquidity) {
|
|
16
|
+
this.sqrtPriceX96 = sqrtPriceX96;
|
|
17
|
+
this.tickCurrent = tickCurrent;
|
|
18
|
+
this.liquidity = liquidity;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.Pool = Pool;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { EvmChainInfoType } from "@clonegod/ttd-core/dist/chains";
|
|
2
|
+
export declare class TokenPriceCache {
|
|
3
|
+
private tokenPriceCache;
|
|
4
|
+
private readonly PRICE_CACHE_TIMEOUT_MILLS;
|
|
5
|
+
constructor();
|
|
6
|
+
getTokenPrice(evm_chain_info: EvmChainInfoType, tokenAddress: string): Promise<{
|
|
7
|
+
price: string;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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.TokenPriceCache = void 0;
|
|
13
|
+
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
14
|
+
class TokenPriceCache {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.tokenPriceCache = new Map();
|
|
17
|
+
this.PRICE_CACHE_TIMEOUT_MILLS = parseInt(process.env.PRICE_CACHE_TIMEOUT_MILLS || Number(1000 * 60 * 60 * 1).toString());
|
|
18
|
+
(0, dist_1.log_info)(`代币价格缓存超时时间: ${this.PRICE_CACHE_TIMEOUT_MILLS} ms`);
|
|
19
|
+
}
|
|
20
|
+
getTokenPrice(evm_chain_info, tokenAddress) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
tokenAddress = tokenAddress.toLowerCase();
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
const cachedData = this.tokenPriceCache.get(tokenAddress);
|
|
25
|
+
if (cachedData && (now - cachedData.timestamp) < this.PRICE_CACHE_TIMEOUT_MILLS) {
|
|
26
|
+
(0, dist_1.log_debug)(`use cached token price: ${tokenAddress}, price: ${cachedData.price}`, '');
|
|
27
|
+
return cachedData;
|
|
28
|
+
}
|
|
29
|
+
const priceMap = yield (0, dist_1.get_eth_token_price_info)(evm_chain_info, [tokenAddress]);
|
|
30
|
+
const tokenPrice = priceMap.get(tokenAddress);
|
|
31
|
+
if (!tokenPrice || !tokenPrice.price || Number(tokenPrice.price) <= 0) {
|
|
32
|
+
throw new Error(`无法获取代币 ${tokenAddress} 的有效USD价格`);
|
|
33
|
+
}
|
|
34
|
+
const newPrice = { price: tokenPrice.price, timestamp: now };
|
|
35
|
+
this.tokenPriceCache.set(tokenAddress, newPrice);
|
|
36
|
+
return newPrice;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.TokenPriceCache = TokenPriceCache;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./redis_client";
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
__exportStar(require("./redis_client"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { RedisClientType } from "redis";
|
|
2
|
+
export declare class SimpleRedisClient {
|
|
3
|
+
private lock_prefix;
|
|
4
|
+
private redisClient;
|
|
5
|
+
private lockMaxRetries;
|
|
6
|
+
private lockRetryDelayMs;
|
|
7
|
+
private lockExpireSeconds;
|
|
8
|
+
constructor(lock_prefix: string);
|
|
9
|
+
getRedisClient(): Promise<RedisClientType>;
|
|
10
|
+
private getLockKey;
|
|
11
|
+
private acquireLock;
|
|
12
|
+
private releaseLock;
|
|
13
|
+
withLock<T>(lock_identifier: string, callback: () => Promise<T>, release_lock_delay_ms?: number): Promise<T>;
|
|
14
|
+
getValue(key: string): Promise<string>;
|
|
15
|
+
setValue(key: string, value: string, expireSeconds: number): Promise<any>;
|
|
16
|
+
hsetValue(key: string, field: string, value: string, expireSeconds: number): Promise<any>;
|
|
17
|
+
hgetvalue(key: string, field: string): Promise<any>;
|
|
18
|
+
hkeys(key: string): Promise<any>;
|
|
19
|
+
hgetall(key: string): Promise<any>;
|
|
20
|
+
del(key: string, field?: string): Promise<any>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
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.SimpleRedisClient = void 0;
|
|
13
|
+
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
14
|
+
class SimpleRedisClient {
|
|
15
|
+
constructor(lock_prefix) {
|
|
16
|
+
this.lock_prefix = lock_prefix;
|
|
17
|
+
this.redisClient = null;
|
|
18
|
+
this.lockMaxRetries = 10;
|
|
19
|
+
this.lockRetryDelayMs = 300;
|
|
20
|
+
this.lockExpireSeconds = 3;
|
|
21
|
+
}
|
|
22
|
+
getRedisClient() {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
if (!this.redisClient) {
|
|
25
|
+
this.redisClient = yield (0, dist_1.getRedisCache)();
|
|
26
|
+
}
|
|
27
|
+
return this.redisClient;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
getLockKey(lock_identifier) {
|
|
31
|
+
return `${this.lock_prefix}:lock:${lock_identifier}`;
|
|
32
|
+
}
|
|
33
|
+
acquireLock(lock_key_1, lock_value_1) {
|
|
34
|
+
return __awaiter(this, arguments, void 0, function* (lock_key, lock_value, expireSeconds = this.lockExpireSeconds) {
|
|
35
|
+
const redisClient = yield this.getRedisClient();
|
|
36
|
+
const result = yield redisClient.set(lock_key, lock_value, {
|
|
37
|
+
NX: true,
|
|
38
|
+
EX: expireSeconds
|
|
39
|
+
});
|
|
40
|
+
(0, dist_1.log_info)(`try acquireLock: lock_key=${lock_key}, lock_value=${lock_value}, expireSeconds=${expireSeconds}, result=${result}`);
|
|
41
|
+
const success = result === 'OK';
|
|
42
|
+
if (success) {
|
|
43
|
+
(0, dist_1.log_info)(`acquire lock success: lock_key=${lock_key}, lock_value=${lock_value}`);
|
|
44
|
+
}
|
|
45
|
+
return success;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
releaseLock(lock_key, lock_value) {
|
|
49
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
const redisClient = yield this.getRedisClient();
|
|
51
|
+
const script = `
|
|
52
|
+
if redis.call('get', KEYS[1]) == ARGV[1] then
|
|
53
|
+
return redis.call('del', KEYS[1])
|
|
54
|
+
else
|
|
55
|
+
return 0
|
|
56
|
+
end
|
|
57
|
+
`;
|
|
58
|
+
const result = yield redisClient.eval(script, {
|
|
59
|
+
keys: [lock_key],
|
|
60
|
+
arguments: [lock_value]
|
|
61
|
+
});
|
|
62
|
+
const success = Number(result) === 1;
|
|
63
|
+
if (success) {
|
|
64
|
+
(0, dist_1.log_info)(`release lock success: lock_key=${lock_key}, lock_value=${lock_value}`);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
(0, dist_1.log_info)(`release lock failed: lock_key=${lock_key}, lock_value=${lock_value}, maybe expired or locked by other process`);
|
|
68
|
+
}
|
|
69
|
+
return success;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
withLock(lock_identifier_1, callback_1) {
|
|
73
|
+
return __awaiter(this, arguments, void 0, function* (lock_identifier, callback, release_lock_delay_ms = 2000) {
|
|
74
|
+
const lock_key = this.getLockKey(lock_identifier);
|
|
75
|
+
const lock_value = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
76
|
+
let retries = 0;
|
|
77
|
+
let acquired = false;
|
|
78
|
+
let first_try_time = Date.now();
|
|
79
|
+
let get_lock_time = 0;
|
|
80
|
+
try {
|
|
81
|
+
while (retries < this.lockMaxRetries) {
|
|
82
|
+
acquired = yield this.acquireLock(lock_key, lock_value);
|
|
83
|
+
if (acquired)
|
|
84
|
+
break;
|
|
85
|
+
yield new Promise(resolve => setTimeout(resolve, this.lockRetryDelayMs));
|
|
86
|
+
retries++;
|
|
87
|
+
}
|
|
88
|
+
if (acquired) {
|
|
89
|
+
get_lock_time = Date.now();
|
|
90
|
+
return yield callback();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
if (acquired) {
|
|
95
|
+
const release_delay = parseInt(process.env.NONCE_LOCK_RELEASE_DELAY_MS || String(release_lock_delay_ms));
|
|
96
|
+
yield (0, dist_1.sleep)(release_delay);
|
|
97
|
+
yield this.releaseLock(lock_key, lock_value);
|
|
98
|
+
(0, dist_1.log_info)(`withLock success: lock_key=${lock_key}, lock_value=${lock_value}, retry times=${retries}, get lock take ${get_lock_time - first_try_time}ms, release_delay=${release_delay}ms, hold lock ${Date.now() - get_lock_time}ms`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
(0, dist_1.log_warn)(`withLock failed: lock_key=${lock_key}, lock_value=${lock_value}, retry times=${retries}, took ${Date.now() - first_try_time}ms`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
getValue(key) {
|
|
107
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
108
|
+
const redisClient = yield this.getRedisClient();
|
|
109
|
+
return yield redisClient.get(key);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
setValue(key, value, expireSeconds) {
|
|
113
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
114
|
+
const redisClient = yield this.getRedisClient();
|
|
115
|
+
return yield redisClient.set(key, value, {
|
|
116
|
+
EX: expireSeconds
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
hsetValue(key, field, value, expireSeconds) {
|
|
121
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
const redisClient = yield this.getRedisClient();
|
|
123
|
+
yield redisClient.hSet(key, field, value);
|
|
124
|
+
yield redisClient.hExpire(key, field, expireSeconds);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
hgetvalue(key, field) {
|
|
128
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
129
|
+
const redisClient = yield this.getRedisClient();
|
|
130
|
+
return yield redisClient.hGet(key, field);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
hkeys(key) {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
const redisClient = yield this.getRedisClient();
|
|
136
|
+
return yield redisClient.hKeys(key);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
hgetall(key) {
|
|
140
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
+
const redisClient = yield this.getRedisClient();
|
|
142
|
+
return yield redisClient.hGetAll(key);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
del(key_1) {
|
|
146
|
+
return __awaiter(this, arguments, void 0, function* (key, field = '') {
|
|
147
|
+
const redisClient = yield this.getRedisClient();
|
|
148
|
+
if (field) {
|
|
149
|
+
return yield redisClient.hDel(key, field);
|
|
150
|
+
}
|
|
151
|
+
return yield redisClient.del(key);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
exports.SimpleRedisClient = SimpleRedisClient;
|
|
@@ -0,0 +1,27 @@
|
|
|
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 AbstractEvmDexTrade extends AbastrcatTrade {
|
|
5
|
+
protected appConfig: AppConfig;
|
|
6
|
+
protected wallet: ethers.Wallet;
|
|
7
|
+
protected provider: ethers.providers.JsonRpcProvider;
|
|
8
|
+
protected approvedTokens: 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 getGasPriceGwei(context: TradeContext): string;
|
|
19
|
+
protected getBuilderTipAmoutGwei(context: TradeContext): string;
|
|
20
|
+
protected checkTradeTokenApprove(context: TradeContext, routerAddress?: string): Promise<void>;
|
|
21
|
+
protected checkTokenApprove(tokenAddress: string, tokenSymbol: string, tokenContract: ethers.Contract, spenderAddress: string): Promise<boolean>;
|
|
22
|
+
protected isNonceRelatedError(error: any): boolean;
|
|
23
|
+
protected isNativeCurrency(symbol: string): boolean;
|
|
24
|
+
protected getWrappedNativeAddress(): string;
|
|
25
|
+
abstract execute(context: TradeContext, retryCount?: number): Promise<string>;
|
|
26
|
+
protected buildTipTransferTx(to: string, transfer_amount_gwei: string, gas_price_gwei: string, transfer_nonce: number): Promise<string>;
|
|
27
|
+
}
|