-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
155 lines (131 loc) · 5.7 KB
/
index.js
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
const Bridge = require('@rsksmart/rsk-precompiled-abis').bridge;
const BridgeTx = require("./BridgeTx");
const BridgeMethod = require("./BridgeMethod");
const BridgeEvent = require("./BridgeEvent");
const utils = require("./utils");
class BridgeTransactionParser {
constructor(web3Client) {
if (!web3Client) {
throw new Error(`web3Client is required`);
}
this.web3Client = web3Client;
this.bridge = Bridge.build(this.web3Client);
this.jsonInterfaceMap = this.bridge._jsonInterface.reduce((map, item) => {
map[item.signature] = item;
return map;
}, {});
}
getBridgeTransactionByTxHash = async (transactionHash) => {
utils.verifyHashOrBlockNumber(transactionHash);
let transaction;
const txReceipt = await this.web3Client.eth.getTransactionReceipt(transactionHash);
if (txReceipt?.to === Bridge.address) {
const tx = await this.web3Client.eth.getTransaction(txReceipt.transactionHash);
transaction = await this.createBridgeTx(tx, txReceipt);
}
return transaction;
}
getBridgeTransactionsInThisBlock = async (blockHashOrBlockNumber) => {
utils.verifyHashOrBlockNumber(blockHashOrBlockNumber);
const block = await this.web3Client.eth.getBlock(blockHashOrBlockNumber);
if (!block) {
throw new Error(`Block ${blockHashOrBlockNumber} not found`);
}
const bridgeTxs = [];
for (let txHash of block.transactions) {
const transaction = await this.getBridgeTransactionByTxHash(txHash);
if (transaction) {
bridgeTxs.push(transaction);
}
}
return bridgeTxs;
}
getBridgeTransactionsSinceThisBlock = async (startingBlockHashOrBlockNumber, blocksToSearch) => {
utils.verifyHashOrBlockNumber(startingBlockHashOrBlockNumber);
if (isNaN(blocksToSearch) || blocksToSearch > 100 || blocksToSearch <= 0) {
throw new Error('blocksToSearch must be greater than 0 or less than 100');
}
const startingBlockNumber = typeof startingBlockHashOrBlockNumber === 'string' && startingBlockHashOrBlockNumber.indexOf('0x') === 0 ?
(await this.web3Client.eth.getBlock(startingBlockHashOrBlockNumber)).number : startingBlockHashOrBlockNumber;
const bridgeTxs = [];
for (let i = 0; i < blocksToSearch; i++) {
const blockNumber = parseInt(startingBlockNumber) + i;
const blockBridgeTxs = await this.getBridgeTransactionsInThisBlock(blockNumber);
if (blockBridgeTxs.length) {
bridgeTxs.push(...blockBridgeTxs);
}
}
return bridgeTxs;
}
decodeBridgeTransaction = async (bridgeTx, bridgeTxReceipt) => {
if (bridgeTx.hash !== bridgeTxReceipt.transactionHash) {
throw new Error(`Given bridgeTx(${bridgeTx.hash}) and bridgeTxReceipt(${bridgeTxReceipt.transactionHash}) should belong to the same transaction.`);
}
if (bridgeTxReceipt.to !== Bridge.address) {
throw new Error(`Given bridgeTxReceipt is not a bridge transaction`);
}
return this.createBridgeTx(bridgeTx, bridgeTxReceipt);
}
decodeBridgeMethodParameters = (methodName, data) => {
const abi = Bridge.abi.find(m => m.name === methodName);
if (!abi) {
throw new Error(`${methodName} does not exist in Bridge abi`);
}
const argumentsData = data.substring(10); // Remove the signature bits from the data
const dataDecoded = this.web3Client.eth.abi.decodeParameters(abi.inputs, argumentsData);
// TODO: the parsing of the arguments is not tested
const args = {};
for (let input of abi.inputs) {
args[input.name] = dataDecoded[input.name];
}
return args;
}
createBridgeTx = async (tx, txReceipt) => {
const txData = tx.input;
const method = this.jsonInterfaceMap[txData.substring(0, 10)];
const events = this.decodeLogs(txReceipt);
const block = await this.web3Client.eth.getBlock(txReceipt.blockNumber);
let bridgeMethod = '';
if (method) {
const args = await this.decodeBridgeMethodParameters(method.name, txData);
bridgeMethod = new BridgeMethod(method.name, method.signature, args);
}
return new BridgeTx(
txReceipt.transactionHash,
bridgeMethod,
events,
txReceipt.from,
txReceipt.blockNumber,
block.timestamp
);
};
decodeLogs = (txReceipt) => {
const events = [];
for (let log of txReceipt.logs) {
const abiElement = this.jsonInterfaceMap[log.topics[0]];
if(!abiElement) {
continue;
}
const event = this.decodeLog(log, abiElement);
events.push(event);
}
return events;
}
decodeLog = (log, abiElement) => {
const args = {};
const dataDecoded = this.web3Client.eth.abi.decodeParameters(abiElement.inputs.filter(i => !i.indexed), log.data);
let topicIndex = 1;
for (let input of abiElement.inputs) {
let value;
if (input.indexed) {
value = this.web3Client.eth.abi.decodeParameter(input.type, log.topics[topicIndex]);
topicIndex++;
} else {
value = dataDecoded[input.name];
}
args[input.name] = value;
}
return new BridgeEvent(abiElement.name, abiElement.signature, args);
}
}
module.exports = BridgeTransactionParser;