UNPKG

4.4 kBJavaScriptView Raw
1const { assert, format } = require('../util');
2const HexStream = require('../util/HexStream');
3const namedTuple = require('../util/namedTuple');
4const abiCoder = require('../abi');
5const { signature, formatSignature, formatFullName } = require('./signature');
6
7class EventCoder {
8 /**
9 * Event coder
10 *
11 * @param options {object}
12 * @param options.anonymous {boolean}
13 * @param options.name {string}
14 * @param options.inputs {array}
15 *
16 * @example
17 * > abi = {
18 name: 'EventName',
19 anonymous: false,
20 inputs: [
21 {
22 indexed: true,
23 name: 'account',
24 type: 'address',
25 },
26 {
27 indexed: false,
28 name: 'number',
29 type: 'uint',
30 },
31 ],
32 }
33 * > coder = new EventCoder(abi)
34 EventCoder {
35 anonymous: false,
36 name: 'EventName',
37 inputs: [
38 { indexed: true, name: 'account', type: 'address' },
39 { indexed: false, name: 'number', type: 'uint' }
40 ],
41 type: 'EventName(address,uint256)',
42 NamedTuple: [Function: NamedTuple(account,number)]
43 }
44 */
45 constructor({ anonymous, name, inputs = [] } = {}) {
46 this.anonymous = anonymous;
47 this.name = name; // example: "Event"
48 this.fullName = formatFullName({ name, inputs }); // example: "Event(address)"
49 this.type = formatSignature({ name, inputs }); // example: "Event(address indexed account)"
50 this.signature = signature(this.type); // example: "0x50d7c806d0f7913f321946784dee176a42aa55b5dd83371fc57dcedf659085e0"
51
52 this.inputs = inputs;
53 this.dataCoder = abiCoder({ type: 'tuple', components: inputs.filter(component => !component.indexed) });
54
55 this.NamedTuple = namedTuple(...inputs.map((input, index) => input.name || `${index}`));
56 }
57
58 /**
59 * Encode topics by params
60 *
61 * @param array {*[]}
62 * @return {string[]}
63 * @example
64 * > coder = new EventCoder(abi)
65 * > coder.encodeTopics(['0x0123456789012345678901234567890123456789', null])
66 ['0x0000000000000000000000000123456789012345678901234567890123456789']
67 */
68 encodeTopics(array) {
69 assert(array.length === this.inputs.length, {
70 message: 'length not match',
71 expect: this.inputs.length,
72 got: array.length,
73 coder: this,
74 });
75
76 const topics = [];
77 this.inputs.forEach((component, index) => {
78 if (component.indexed) {
79 const value = array[index];
80
81 topics.push(value === null ? null : format.hex(abiCoder(component).encodeIndex(value)));
82 }
83 });
84
85 return topics;
86 }
87
88 /**
89 * Decode log
90 *
91 * @param topics {array} - Array of hex sting
92 * @param data {string} - Hex string
93 * @return {array} NamedTuple
94 *
95 * @example
96 * > coder = new EventCoder(abi)
97 * > result = coder.decodeLog({
98 data: '0x000000000000000000000000000000000000000000000000000000000000000a',
99 topics: [
100 '0xb0333e0e3a6b99318e4e2e0d7e5e5f93646f9cbf62da1587955a4092bf7df6e7',
101 '0x0000000000000000000000000123456789012345678901234567890123456789',
102 ],
103 })
104 NamedTuple(account,number) [ '0x0123456789012345678901234567890123456789', 10n ]
105 * > console.log([...result])
106 [ 0x0123456789012345678901234567890123456789, 10n ]
107 * > console.log(result.account) // `account` a field name in abi
108 "0x0123456789012345678901234567890123456789"
109 * > console.log(result.number) // `number` a field name in abi
110 10n
111 */
112 decodeLog({ topics, data }) {
113 assert(this.anonymous || topics[0] === this.signature, {
114 message: 'decodeLog unexpected topic',
115 expect: this.signature,
116 got: topics[0],
117 coder: this,
118 });
119
120 const stream = new HexStream(data);
121 const notIndexedNamedTuple = this.dataCoder.decode(stream);
122
123 assert(stream.eof(), {
124 message: 'hex length to large',
125 expect: `${stream.string.length}`,
126 got: stream.index,
127 coder: this,
128 });
129
130 let offset = this.anonymous ? 0 : 1;
131 const array = this.inputs.map(component => {
132 if (component.indexed) {
133 return abiCoder(component).decodeIndex(topics[offset++]); // eslint-disable-line no-plusplus
134 } else {
135 return notIndexedNamedTuple[component.name];
136 }
137 });
138
139 return new this.NamedTuple(...array);
140 }
141}
142
143module.exports = EventCoder;