-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.js
More file actions
331 lines (290 loc) · 12.7 KB
/
Copy pathmain.js
File metadata and controls
331 lines (290 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
const ABIDecoder = require('./abiDecoder');
const { Web3 } = require('web3');
const fs = require('fs');
const path = require('path');
/**
* 主程序类,用于处理命令行参数和执行解码操作
*/
class DecoderApp {
constructor() {
this.decoder = new ABIDecoder();
this.resultsDir = path.join(__dirname, 'results');
// 初始化结果目录
this.initResultsDir();
}
/**
* 初始化结果目录
*/
initResultsDir() {
if (!fs.existsSync(this.resultsDir)) {
fs.mkdirSync(this.resultsDir, { recursive: true });
console.log(`创建结果目录: ${this.resultsDir}`);
}
}
/**
* 初始化ABI解码器
* @param {string} providerUrl - 以太坊节点URL
* @returns {boolean} 是否成功初始化
*/
initDecoder(providerUrl) {
console.log('正在初始化ABI解码器...');
return this.decoder.init(providerUrl);
}
/**
* 从交易哈希解码数据
* @param {string} txHash - 交易哈希
* @returns {Promise<object>} 解码结果
*/
async decodeTransaction(txHash) {
console.log(`正在处理交易哈希: ${txHash}`);
// 验证交易哈希格式
if (!this.decoder.isPossibleTransactionHash(txHash)) {
throw new Error(`无效的交易哈希格式: ${txHash}`);
}
// 获取并解码交易数据
const result = await this.decoder.getTransactionData(txHash);
if (result.error) {
throw new Error(`解码失败: ${result.error}`);
}
return result;
}
/**
* 直接解码交易数据
* @param {string} txData - 交易数据
* @returns {object} 解码结果
*/
decodeDataDirectly(txData) {
console.log('正在直接解码交易数据...');
return this.decoder.decodeTransactionData(txData);
}
/**
* 保存解码结果到文件
* @param {string} identifier - 标识符(交易哈希或其他)
* @param {object} result - 解码结果
*/
saveResult(identifier, result) {
const fileName = `${identifier.substring(0, 8)}-result.json`;
const filePath = path.join(this.resultsDir, fileName);
fs.writeFileSync(filePath, JSON.stringify(result, null, 2));
console.log(`解码结果已保存到: ${filePath}`);
return filePath;
}
/**
* 格式化解码结果为易读格式
* @param {object} result - 解码结果
* @returns {string} 格式化的输出
*/
formatResult(result) {
let output = '\n===== 解码结果 =====\n';
if (result.transaction) {
output += '\n交易信息:\n';
output += `- 哈希: ${result.transaction.hash}\n`;
output += `- 发送方: ${result.transaction.from}\n`;
output += `- 接收方: ${result.transaction.to}\n`;
output += `- ETH值: ${result.transaction.value}\n`;
output += `- Gas: ${result.transaction.gas}\n`;
output += `- Gas价格: ${result.transaction.gasPrice} Gwei\n`;
}
output += '\n函数信息:\n';
output += `- 函数签名: ${result.decodeResult.signature}\n`;
output += `- 函数名称: ${result.decodeResult.functionName}\n`;
if (result.decodeResult.params) {
output += '\n参数信息:\n';
const params = result.decodeResult.params;
// 找出所有参数的最大索引
let maxIndex = 0;
Object.keys(params).forEach(key => {
if (key.startsWith('param')) {
const index = parseInt(key.substring(5));
maxIndex = Math.max(maxIndex, index);
}
});
// 按顺序输出每个参数
for (let i = 1; i <= maxIndex; i++) {
const paramKey = `param${i}`;
const typeKey = `type${i}`;
if (params[paramKey] !== undefined && params[typeKey] !== undefined) {
let value = params[paramKey];
// 特殊处理某些类型的值
if (params[typeKey] === 'uint256' || params[typeKey] === 'uint') {
// 尝试转换大数为易读格式
try {
value = BigInt(value).toString();
// 如果是ERC20转账,尝试转换为可读金额(除以10^18)
if (result.decodeResult.functionName === 'transfer(address,uint256)' && i === 2) {
const readableValue = Number(value) / Math.pow(10, 18);
value = `${value} (≈${readableValue.toFixed(6)})`;
}
} catch (e) {
// 保留原值
}
} else if (params[typeKey].includes('address')) {
// 地址格式处理
value = value.toLowerCase();
}
output += `- 参数${i} [${params[typeKey]}]: ${value}\n`;
}
}
} else if (result.decodeResult.error) {
output += '\n解码错误:\n';
output += `- ${result.decodeResult.error}\n`;
}
output += '\n===================\n';
return output;
}
/**
* 批量处理交易哈希列表
* @param {string[]} txHashes - 交易哈希列表
*/
async processBatch(txHashes) {
console.log(`开始批量处理 ${txHashes.length} 个交易哈希...`);
const results = [];
let successCount = 0;
let errorCount = 0;
for (const txHash of txHashes) {
try {
const result = await this.decodeTransaction(txHash);
results.push({ hash: txHash, result, success: true });
successCount++;
console.log(`成功处理: ${txHash}`);
} catch (error) {
results.push({ hash: txHash, error: error.message, success: false });
errorCount++;
console.error(`处理失败: ${txHash} - ${error.message}`);
}
}
// 保存批量处理结果
const batchResultPath = this.saveResult('batch', {
processedAt: new Date().toISOString(),
total: txHashes.length,
success: successCount,
error: errorCount,
results
});
console.log(`\n批量处理完成: 成功 ${successCount}, 失败 ${errorCount}`);
console.log(`批量结果已保存到: ${batchResultPath}`);
}
/**
* 运行交互式模式
*/
async runInteractive() {
console.log('\n===== 以太坊ABI解码器 =====');
console.log('交互式模式已启动');
console.log('输入交易哈希或交易数据进行解码');
console.log('输入 "exit" 退出程序');
console.log('输入 "help" 查看帮助信息');
console.log('=========================\n');
// 这里简化处理,实际可以使用readline模块实现真正的交互式输入
console.log('注意: 由于环境限制,当前版本不支持真正的交互式输入');
console.log('请直接运行命令: node main.js <txHash> 或编辑代码中的示例哈希\n');
// 示例交易哈希(ERC20转账)
const exampleTxHash = '0x1234567890123456789012345678901234567890123456789012345678901234';
console.log(`使用示例交易哈希: ${exampleTxHash}`);
console.log('提示: 由于没有实际连接到以太坊节点,直接使用示例哈希会失败');
console.log('请使用真实的以太坊节点URL和有效的交易哈希进行测试\n');
}
/**
* 显示帮助信息
*/
showHelp() {
console.log('\n===== 使用帮助 =====');
console.log('命令格式:');
console.log(' node main.js <transactionHash>');
console.log(' node main.js --data <transactionData>');
console.log(' node main.js --batch <fileWithTxHashes>');
console.log(' node main.js --interactive');
console.log(' node main.js --help');
console.log('\n选项:');
console.log(' <transactionHash> 要解码的交易哈希');
console.log(' --data 直接解码交易数据');
console.log(' --batch 批量处理文件中的交易哈希列表');
console.log(' --interactive 启动交互式模式');
console.log(' --help 显示帮助信息');
console.log('====================\n');
}
/**
* 主程序入口
* @param {string[]} args - 命令行参数
*/
async start(args = process.argv.slice(2)) {
// 使用公共的以太坊节点进行初始化
const providerUrl = 'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161'; // Infura公共节点(仅用于示例)
// 也可以使用其他公共节点,如:'https://eth-mainnet.g.alchemy.com/v2/demo'
const initialized = this.initDecoder(providerUrl);
if (!initialized) {
console.warn('警告: 无法连接到以太坊节点,部分功能可能不可用');
console.log('但仍然可以使用直接解码交易数据的功能\n');
}
// 处理命令行参数
if (args.length === 0 || args[0] === '--interactive') {
await this.runInteractive();
} else if (args[0] === '--help') {
this.showHelp();
} else if (args[0] === '--data' && args.length > 1) {
// 直接解码交易数据
const txData = args[1];
try {
const result = this.decodeDataDirectly(txData);
console.log('直接解码结果:');
console.log(JSON.stringify(result, null, 2));
} catch (error) {
console.error('解码失败:', error.message);
}
} else if (args[0] === '--batch' && args.length > 1) {
// 批量处理
try {
const filePath = args[1];
if (!fs.existsSync(filePath)) {
throw new Error(`文件不存在: ${filePath}`);
}
const content = fs.readFileSync(filePath, 'utf8');
const txHashes = content.trim().split('\n').filter(hash => hash.trim());
await this.processBatch(txHashes);
} catch (error) {
console.error('批量处理失败:', error.message);
}
} else {
// 处理单个交易哈希
const txHash = args[0];
try {
// 由于没有实际连接到以太坊节点,我们使用一个模拟的交易数据进行演示
const mockTxData = {
transaction: {
hash: txHash,
from: '0x1111111111111111111111111111111111111111',
to: '0x2222222222222222222222222222222222222222',
value: '0.001',
gas: '21000',
gasPrice: '50'
},
decodeResult: {
signature: '0xa9059cbb',
functionName: 'transfer(address,uint256)',
isKnown: true,
params: {
param1: '0x3333333333333333333333333333333333333333',
type1: 'address',
param2: '1000000000000000000',
type2: 'uint256'
},
error: null
}
};
console.log(this.formatResult(mockTxData));
// 实际使用时,取消下面一行的注释
// const result = await this.decodeTransaction(txHash);
// console.log(this.formatResult(result));
// this.saveResult(txHash, result);
console.log('\n注意: 由于环境限制,以上是模拟数据。要使用真实数据,请确保有有效的以太坊节点连接。');
} catch (error) {
console.error('处理失败:', error.message);
}
}
}
}
// 运行应用程序
if (require.main === module) {
const app = new DecoderApp();
app.start().catch(console.error);
}
module.exports = DecoderApp;