-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathabiDecoder.js
More file actions
221 lines (190 loc) · 7.72 KB
/
Copy pathabiDecoder.js
File metadata and controls
221 lines (190 loc) · 7.72 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
const { Web3 } = require('web3');
/**
* ABI解码器类,用于解析以太坊交易数据
*/
class ABIDecoder {
constructor() {
this.web3 = null;
// 一些常见的ERC20函数签名映射
this.functionSignatures = {
'0xa9059cbb': 'transfer(address,uint256)',
'0x23b872dd': 'transferFrom(address,address,uint256)',
'0x70a08231': 'balanceOf(address)',
'0x095ea7b3': 'approve(address,uint256)',
'0xdd62ed3e': 'allowance(address,address)',
'0x18160ddd': 'totalSupply()',
'0x38ed1739': 'swapExactTokensForETH(uint256,uint256,address[],address,uint256)',
'0x18cbafe5': 'swapExactETHForTokens(uint256,address[],address,uint256)',
'0x7ff36ab5': 'swapExactTokensForTokens(uint256,uint256,address[],address,uint256)',
'0x02751cec': 'multicall(bytes[])' // Multicall函数
};
}
/**
* 初始化Web3连接
* @param {string} providerUrl - 以太坊节点URL
*/
init(providerUrl = 'https://mainnet.infura.io/v3/your-project-id') {
try {
this.web3 = new Web3(providerUrl);
console.log('Web3初始化成功');
return true;
} catch (error) {
console.error('Web3初始化失败:', error.message);
// 在没有实际节点连接的情况下,仍然可以使用基本的解码功能
this.web3 = new Web3();
return false;
}
}
/**
* 解码交易数据的函数签名
* @param {string} data - 交易的data字段
* @returns {object} 解码结果
*/
decodeFunctionSignature(data) {
if (!data || typeof data !== 'string' || !data.startsWith('0x')) {
return { error: '无效的交易数据格式' };
}
// 提取函数签名(前4个字节+0x前缀)
const signature = data.slice(0, 10).toLowerCase();
// 查找函数签名
const functionSignature = this.functionSignatures[signature];
return {
signature,
functionName: functionSignature || '未知函数',
isKnown: !!functionSignature
};
}
/**
* 解析交易数据中的参数
* @param {string} data - 交易的data字段
* @param {string} functionSignature - 函数签名,如 'transfer(address,uint256)'
* @returns {object} 包含解析后参数的对象
*/
decodeParameters(data, functionSignature) {
try {
// 提取参数部分(去除函数签名)
let paramsData = data.slice(10);
// 确保十六进制字符串长度为偶数
if (paramsData.length % 2 !== 0) {
paramsData = '0' + paramsData;
}
paramsData = '0x' + paramsData;
// 提取参数类型
const paramTypes = functionSignature
.match(/\(([^)]+)\)/)[1]
.split(',')
.map(type => type.trim());
// 使用web3的abi.decodeParameters进行解码
const decoded = this.web3.eth.abi.decodeParameters(paramTypes, paramsData);
// 构建更友好的参数对象
const params = {};
paramTypes.forEach((type, index) => {
// 对于地址类型,确保格式正确
let value = decoded[index];
// 处理BigInt类型,转换为字符串以避免序列化问题
if (typeof value === 'bigint') {
value = value.toString();
}
// 处理地址类型
if (type.includes('address') && typeof value === 'string') {
value = value.toLowerCase();
}
params[`param${index + 1}`] = value;
params[`type${index + 1}`] = type;
});
// 移除raw字段以避免BigInt序列化问题
return { success: true, params };
} catch (error) {
return { success: false, error: error.message };
}
}
/**
* 综合解码交易数据
* @param {string} data - 交易的data字段
* @returns {object} 完整的解码结果
*/
decodeTransactionData(data) {
// 解码函数签名
const sigResult = this.decodeFunctionSignature(data);
if (sigResult.error) {
return sigResult;
}
let paramsResult = { success: false, error: '未知函数签名,无法解析参数' };
// 如果是已知函数,尝试解析参数
if (sigResult.isKnown) {
paramsResult = this.decodeParameters(data, sigResult.functionName);
}
return {
...sigResult,
params: paramsResult.success ? paramsResult.params : null,
error: paramsResult.error
};
}
/**
* 从交易哈希获取并解码交易数据
* 注意:这需要有效的以太坊节点连接
* @param {string} txHash - 交易哈希
* @returns {Promise<object>} 解码结果
*/
async getTransactionData(txHash) {
if (!this.web3.provider || !txHash.startsWith('0x')) {
return { error: '无效的交易哈希或未连接到以太坊节点' };
}
try {
const tx = await this.web3.eth.getTransaction(txHash);
if (!tx || !tx.input) {
return { error: '无法获取交易数据' };
}
const decodeResult = this.decodeTransactionData(tx.input);
return {
transaction: {
hash: tx.hash,
from: tx.from,
to: tx.to,
value: this.web3.utils.fromWei(tx.value, 'ether'),
gas: tx.gas,
gasPrice: this.web3.utils.fromWei(tx.gasPrice, 'gwei')
},
decodeResult
};
} catch (error) {
return { error: error.message };
}
}
/**
* 添加自定义函数签名
* @param {object} signatures - 函数签名映射对象
*/
addFunctionSignatures(signatures) {
this.functionSignatures = { ...this.functionSignatures, ...signatures };
}
/**
* 分析哈希值是否可能是交易哈希
* @param {string} hash - 要检查的哈希值
* @returns {boolean} 是否可能是交易哈希
*/
isPossibleTransactionHash(hash) {
return typeof hash === 'string' &&
hash.length === 66 &&
hash.startsWith('0x') &&
/^0x[0-9a-fA-F]{64}$/.test(hash);
}
}
// 导出解码器实例
module.exports = ABIDecoder;
// 示例用法
if (require.main === module) {
async function runExample() {
const decoder = new ABIDecoder();
decoder.init(); // 初始化,即使没有有效的provider也可以使用基本解码功能
console.log('ABI解码器初始化完成!');
console.log('示例用法:');
console.log('1. 解析交易数据: decoder.decodeTransactionData(txData)');
console.log('2. 从交易哈希获取数据: await decoder.getTransactionData(txHash)');
// 示例:解析一个ERC20转账交易数据
const exampleTransferData = '0xa9059cbb0000000000000000000000001234567890123456789012345678901234567890000000000000000000000000000000000000000000000000de0b6b3a7640000';
console.log('\n示例ERC20转账解码结果:');
console.log(JSON.stringify(decoder.decodeTransactionData(exampleTransferData), null, 2));
}
runExample().catch(console.error);
}