1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.AbiDecoder = void 0;
|
4 | const ethereum_types_1 = require("ethereum-types");
|
5 | const ethers = require("ethers");
|
6 | const _ = require("lodash");
|
7 | const _1 = require(".");
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | class AbiDecoder {
|
13 | |
14 |
|
15 |
|
16 |
|
17 |
|
18 | constructor(abiArrays) {
|
19 | this._eventIds = {};
|
20 | this._selectorToFunctionInfo = {};
|
21 | _.each(abiArrays, abi => {
|
22 | this.addABI(abi);
|
23 | });
|
24 | }
|
25 | |
26 |
|
27 |
|
28 |
|
29 |
|
30 | static _getFunctionSelector(calldata) {
|
31 | const functionSelectorLength = 10;
|
32 | if (!calldata.startsWith('0x') || calldata.length < functionSelectorLength) {
|
33 | throw new Error(`Malformed calldata. Must include a hex prefix '0x' and 4-byte function selector. Got '${calldata}'`);
|
34 | }
|
35 | const functionSelector = calldata.substr(0, functionSelectorLength);
|
36 | return functionSelector;
|
37 | }
|
38 | |
39 |
|
40 |
|
41 |
|
42 |
|
43 | tryToDecodeLogOrNoop(log) {
|
44 |
|
45 | const eventId = log.topics[0];
|
46 | const numIndexedArgs = log.topics.length - 1;
|
47 | if (this._eventIds[eventId] === undefined || this._eventIds[eventId][numIndexedArgs] === undefined) {
|
48 | return log;
|
49 | }
|
50 | const event = this._eventIds[eventId][numIndexedArgs];
|
51 |
|
52 | const indexedDataDecoders = _.mapValues(_.filter(event.inputs, { indexed: true }), input =>
|
53 |
|
54 | _1.AbiEncoder.create(input));
|
55 |
|
56 | const decodedIndexedData = _.map(log.topics.slice(1),
|
57 | (input, i) => indexedDataDecoders[i].decode(input));
|
58 |
|
59 | const decodedNonIndexedData = _1.AbiEncoder.create(_.filter(event.inputs, { indexed: false })).decodeAsArray(log.data);
|
60 |
|
61 | const decodedArgs = {};
|
62 | let indexedOffset = 0;
|
63 | let nonIndexedOffset = 0;
|
64 | for (const param of event.inputs) {
|
65 | const value = param.indexed
|
66 | ? decodedIndexedData[indexedOffset++]
|
67 | : decodedNonIndexedData[nonIndexedOffset++];
|
68 | if (value === undefined) {
|
69 | return log;
|
70 | }
|
71 | decodedArgs[param.name] = value;
|
72 | }
|
73 |
|
74 | return Object.assign(Object.assign({}, log), { event: event.name, args: decodedArgs });
|
75 | }
|
76 | |
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | decodeCalldataOrThrow(calldata, contractName) {
|
83 | const functionSelector = AbiDecoder._getFunctionSelector(calldata);
|
84 | const candidateFunctionInfos = this._selectorToFunctionInfo[functionSelector];
|
85 | if (candidateFunctionInfos === undefined) {
|
86 | throw new Error(`No functions registered for selector '${functionSelector}'`);
|
87 | }
|
88 | const functionInfo = _.find(candidateFunctionInfos, candidateFunctionInfo => {
|
89 | return (contractName === undefined || _.toLower(contractName) === _.toLower(candidateFunctionInfo.contractName));
|
90 | });
|
91 | if (functionInfo === undefined) {
|
92 | throw new Error(`No function registered with selector ${functionSelector} and contract name ${contractName}.`);
|
93 | }
|
94 | else if (functionInfo.abiEncoder === undefined) {
|
95 | throw new Error(`Function ABI Encoder is not defined, for function registered with selector ${functionSelector} and contract name ${contractName}.`);
|
96 | }
|
97 | const functionName = functionInfo.abiEncoder.getDataItem().name;
|
98 | const functionSignature = functionInfo.abiEncoder.getSignatureType();
|
99 | const functionArguments = functionInfo.abiEncoder.decode(calldata);
|
100 | const decodedCalldata = {
|
101 | functionName,
|
102 | functionSignature,
|
103 | functionArguments,
|
104 | };
|
105 | return decodedCalldata;
|
106 | }
|
107 | |
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 | addABI(abiArray, contractName) {
|
118 | if (abiArray === undefined) {
|
119 | return;
|
120 | }
|
121 | const ethersInterface = new ethers.utils.Interface(abiArray);
|
122 | _.map(abiArray, (abi) => {
|
123 | switch (abi.type) {
|
124 | case ethereum_types_1.AbiType.Event:
|
125 |
|
126 | this._addEventABI(abi, ethersInterface);
|
127 | break;
|
128 | case ethereum_types_1.AbiType.Function:
|
129 |
|
130 | this._addMethodABI(abi, contractName);
|
131 | break;
|
132 | default:
|
133 |
|
134 | break;
|
135 | }
|
136 | });
|
137 | }
|
138 | _addEventABI(eventAbi, ethersInterface) {
|
139 | const topic = ethersInterface.events[eventAbi.name].topic;
|
140 | const numIndexedArgs = _.reduce(eventAbi.inputs, (sum, input) => (input.indexed ? sum + 1 : sum), 0);
|
141 | this._eventIds[topic] = Object.assign(Object.assign({}, this._eventIds[topic]), { [numIndexedArgs]: eventAbi });
|
142 | }
|
143 | _addMethodABI(methodAbi, contractName) {
|
144 | const abiEncoder = new _1.AbiEncoder.Method(methodAbi);
|
145 | const functionSelector = abiEncoder.getSelector();
|
146 | if (!(functionSelector in this._selectorToFunctionInfo)) {
|
147 | this._selectorToFunctionInfo[functionSelector] = [];
|
148 | }
|
149 |
|
150 | const functionSignature = abiEncoder.getSignature();
|
151 | this._selectorToFunctionInfo[functionSelector].push({
|
152 | functionSignature,
|
153 | abiEncoder,
|
154 | contractName,
|
155 | });
|
156 | }
|
157 | }
|
158 | exports.AbiDecoder = AbiDecoder;
|
159 |
|
\ | No newline at end of file |