@qevm/abi 5.7.1 → 5.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/lib/_version.d.ts +1 -1
- package/lib/_version.js +1 -1
- package/lib/abi-coder.d.ts.map +1 -1
- package/lib/abi-coder.js +12 -7
- package/lib/abi-coder.js.map +1 -1
- package/lib/coders/abstract-coder.d.ts.map +1 -1
- package/lib/coders/abstract-coder.js +20 -9
- package/lib/coders/abstract-coder.js.map +1 -1
- package/lib/coders/address.d.ts.map +1 -1
- package/lib/coders/address.js +2 -2
- package/lib/coders/address.js.map +1 -1
- package/lib/coders/array.d.ts.map +1 -1
- package/lib/coders/array.js +17 -11
- package/lib/coders/array.js.map +1 -1
- package/lib/coders/boolean.d.ts.map +1 -1
- package/lib/coders/boolean.js.map +1 -1
- package/lib/coders/bytes.js.map +1 -1
- package/lib/coders/fixed-bytes.d.ts.map +1 -1
- package/lib/coders/fixed-bytes.js +1 -1
- package/lib/coders/fixed-bytes.js.map +1 -1
- package/lib/coders/function.d.ts +8 -0
- package/lib/coders/function.d.ts.map +1 -0
- package/lib/coders/function.js +46 -0
- package/lib/coders/function.js.map +1 -0
- package/lib/coders/null.d.ts.map +1 -1
- package/lib/coders/null.js.map +1 -1
- package/lib/coders/number.js +1 -1
- package/lib/coders/number.js.map +1 -1
- package/lib/coders/string.d.ts.map +1 -1
- package/lib/coders/string.js +1 -1
- package/lib/coders/string.js.map +1 -1
- package/lib/coders/tuple.d.ts.map +1 -1
- package/lib/coders/tuple.js +1 -1
- package/lib/coders/tuple.js.map +1 -1
- package/lib/fragments.d.ts.map +1 -1
- package/lib/fragments.js +127 -65
- package/lib/fragments.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/interface.d.ts +1 -1
- package/lib/interface.d.ts.map +1 -1
- package/lib/interface.js +84 -44
- package/lib/interface.js.map +1 -1
- package/package.json +35 -31
- package/src.ts/_version.ts +1 -1
- package/src.ts/abi-coder.ts +64 -26
- package/src.ts/coders/abstract-coder.ts +78 -33
- package/src.ts/coders/address.ts +3 -5
- package/src.ts/coders/array.ts +90 -47
- package/src.ts/coders/boolean.ts +1 -3
- package/src.ts/coders/bytes.ts +1 -3
- package/src.ts/coders/fixed-bytes.ts +7 -2
- package/src.ts/coders/function.ts +64 -0
- package/src.ts/coders/null.ts +4 -3
- package/src.ts/coders/number.ts +1 -2
- package/src.ts/coders/string.ts +1 -2
- package/src.ts/coders/tuple.ts +31 -16
- package/src.ts/fragments.ts +411 -178
- package/src.ts/index.ts +20 -8
- package/src.ts/interface.ts +405 -153
package/src.ts/interface.ts
CHANGED
|
@@ -2,16 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
import { getAddress } from "@qevm/address";
|
|
4
4
|
import { BigNumber, BigNumberish } from "@qevm/bignumber";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
arrayify,
|
|
7
|
+
BytesLike,
|
|
8
|
+
concat,
|
|
9
|
+
hexDataSlice,
|
|
10
|
+
hexlify,
|
|
11
|
+
hexZeroPad,
|
|
12
|
+
isHexString,
|
|
13
|
+
} from "@qevm/bytes";
|
|
6
14
|
import { id } from "@qevm/hash";
|
|
7
|
-
import { keccak256 } from "@
|
|
8
|
-
import { defineReadOnly, Description, getStatic } from "@
|
|
15
|
+
import { keccak256 } from "@qevm/keccak256";
|
|
16
|
+
import { defineReadOnly, Description, getStatic } from "@qevm/properties";
|
|
9
17
|
|
|
10
18
|
import { AbiCoder, defaultAbiCoder } from "./abi-coder";
|
|
11
19
|
import { checkResultErrors, Result } from "./coders/abstract-coder";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
import {
|
|
21
|
+
ConstructorFragment,
|
|
22
|
+
ErrorFragment,
|
|
23
|
+
EventFragment,
|
|
24
|
+
FormatTypes,
|
|
25
|
+
Fragment,
|
|
26
|
+
FunctionFragment,
|
|
27
|
+
JsonFragment,
|
|
28
|
+
ParamType,
|
|
29
|
+
} from "./fragments";
|
|
30
|
+
|
|
31
|
+
import { Logger } from "@qevm/logger";
|
|
15
32
|
import { version } from "./_version";
|
|
16
33
|
const logger = new Logger(version);
|
|
17
34
|
|
|
@@ -22,7 +39,7 @@ export class LogDescription extends Description<LogDescription> {
|
|
|
22
39
|
readonly name: string;
|
|
23
40
|
readonly signature: string;
|
|
24
41
|
readonly topic: string;
|
|
25
|
-
readonly args: Result
|
|
42
|
+
readonly args: Result;
|
|
26
43
|
}
|
|
27
44
|
|
|
28
45
|
export class TransactionDescription extends Description<TransactionDescription> {
|
|
@@ -51,13 +68,27 @@ export class Indexed extends Description<Indexed> {
|
|
|
51
68
|
}
|
|
52
69
|
}
|
|
53
70
|
|
|
54
|
-
const BuiltinErrors: Record<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
const BuiltinErrors: Record<
|
|
72
|
+
string,
|
|
73
|
+
{ signature: string; inputs: Array<string>; name: string; reason?: boolean }
|
|
74
|
+
> = {
|
|
75
|
+
"0x08c379a0": {
|
|
76
|
+
signature: "Error(string)",
|
|
77
|
+
name: "Error",
|
|
78
|
+
inputs: ["string"],
|
|
79
|
+
reason: true,
|
|
80
|
+
},
|
|
81
|
+
"0x4e487b71": {
|
|
82
|
+
signature: "Panic(uint256)",
|
|
83
|
+
name: "Panic",
|
|
84
|
+
inputs: ["uint256"],
|
|
85
|
+
},
|
|
86
|
+
};
|
|
58
87
|
|
|
59
88
|
function wrapAccessError(property: string, error: Error): Error {
|
|
60
|
-
const wrap = new Error(
|
|
89
|
+
const wrap = new Error(
|
|
90
|
+
`deferred error during ABI decoding triggered accessing ${property}`,
|
|
91
|
+
);
|
|
61
92
|
(<any>wrap).error = error;
|
|
62
93
|
return wrap;
|
|
63
94
|
}
|
|
@@ -78,10 +109,10 @@ function checkNames(fragment: Fragment, type: "input" | "output", params: Array<
|
|
|
78
109
|
export class Interface {
|
|
79
110
|
readonly fragments: ReadonlyArray<Fragment>;
|
|
80
111
|
|
|
81
|
-
readonly errors: { [
|
|
82
|
-
readonly events: { [
|
|
83
|
-
readonly functions: { [
|
|
84
|
-
readonly structs: { [
|
|
112
|
+
readonly errors: { [name: string]: ErrorFragment };
|
|
113
|
+
readonly events: { [name: string]: EventFragment };
|
|
114
|
+
readonly functions: { [name: string]: FunctionFragment };
|
|
115
|
+
readonly structs: { [name: string]: any };
|
|
85
116
|
|
|
86
117
|
readonly deploy: ConstructorFragment;
|
|
87
118
|
|
|
@@ -89,28 +120,40 @@ export class Interface {
|
|
|
89
120
|
|
|
90
121
|
readonly _isInterface: boolean;
|
|
91
122
|
|
|
92
|
-
constructor(
|
|
93
|
-
|
|
94
|
-
|
|
123
|
+
constructor(
|
|
124
|
+
fragments: string | ReadonlyArray<Fragment | JsonFragment | string>,
|
|
125
|
+
) {
|
|
126
|
+
let abi: ReadonlyArray<Fragment | JsonFragment | string> = [];
|
|
127
|
+
if (typeof fragments === "string") {
|
|
95
128
|
abi = JSON.parse(fragments);
|
|
96
129
|
} else {
|
|
97
130
|
abi = fragments;
|
|
98
131
|
}
|
|
99
132
|
|
|
100
|
-
defineReadOnly(
|
|
101
|
-
|
|
102
|
-
|
|
133
|
+
defineReadOnly(
|
|
134
|
+
this,
|
|
135
|
+
"fragments",
|
|
136
|
+
abi
|
|
137
|
+
.map((fragment) => {
|
|
138
|
+
return Fragment.from(fragment);
|
|
139
|
+
})
|
|
140
|
+
.filter((fragment) => fragment != null),
|
|
141
|
+
);
|
|
103
142
|
|
|
104
|
-
defineReadOnly(
|
|
143
|
+
defineReadOnly(
|
|
144
|
+
this,
|
|
145
|
+
"_abiCoder",
|
|
146
|
+
getStatic<() => AbiCoder>(new.target, "getAbiCoder")(),
|
|
147
|
+
);
|
|
105
148
|
|
|
106
|
-
defineReadOnly(this, "functions", {
|
|
107
|
-
defineReadOnly(this, "errors", {
|
|
108
|
-
defineReadOnly(this, "events", {
|
|
109
|
-
defineReadOnly(this, "structs", {
|
|
149
|
+
defineReadOnly(this, "functions", {});
|
|
150
|
+
defineReadOnly(this, "errors", {});
|
|
151
|
+
defineReadOnly(this, "events", {});
|
|
152
|
+
defineReadOnly(this, "structs", {});
|
|
110
153
|
|
|
111
154
|
// Add all fragments by their signature
|
|
112
155
|
this.fragments.forEach((fragment) => {
|
|
113
|
-
let bucket: { [
|
|
156
|
+
let bucket: { [name: string]: Fragment } = null;
|
|
114
157
|
switch (fragment.type) {
|
|
115
158
|
case "constructor":
|
|
116
159
|
if (this.deploy) {
|
|
@@ -118,7 +161,11 @@ export class Interface {
|
|
|
118
161
|
return;
|
|
119
162
|
}
|
|
120
163
|
//checkNames(fragment, "input", fragment.inputs);
|
|
121
|
-
defineReadOnly(
|
|
164
|
+
defineReadOnly(
|
|
165
|
+
this,
|
|
166
|
+
"deploy",
|
|
167
|
+
<ConstructorFragment>fragment,
|
|
168
|
+
);
|
|
122
169
|
return;
|
|
123
170
|
case "function":
|
|
124
171
|
//checkNames(fragment, "input", fragment.inputs);
|
|
@@ -147,26 +194,36 @@ export class Interface {
|
|
|
147
194
|
|
|
148
195
|
// If we do not have a constructor add a default
|
|
149
196
|
if (!this.deploy) {
|
|
150
|
-
defineReadOnly(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
197
|
+
defineReadOnly(
|
|
198
|
+
this,
|
|
199
|
+
"deploy",
|
|
200
|
+
ConstructorFragment.from({
|
|
201
|
+
payable: false,
|
|
202
|
+
type: "constructor",
|
|
203
|
+
}),
|
|
204
|
+
);
|
|
154
205
|
}
|
|
155
206
|
|
|
156
207
|
defineReadOnly(this, "_isInterface", true);
|
|
157
208
|
}
|
|
158
209
|
|
|
159
210
|
format(format?: string): string | Array<string> {
|
|
160
|
-
if (!format) {
|
|
211
|
+
if (!format) {
|
|
212
|
+
format = FormatTypes.full;
|
|
213
|
+
}
|
|
161
214
|
if (format === FormatTypes.sighash) {
|
|
162
|
-
logger.throwArgumentError(
|
|
215
|
+
logger.throwArgumentError(
|
|
216
|
+
"interface does not support formatting sighash",
|
|
217
|
+
"format",
|
|
218
|
+
format,
|
|
219
|
+
);
|
|
163
220
|
}
|
|
164
221
|
|
|
165
222
|
const abi = this.fragments.map((fragment) => fragment.format(format));
|
|
166
223
|
|
|
167
224
|
// We need to re-bundle the JSON fragments a bit
|
|
168
225
|
if (format === FormatTypes.json) {
|
|
169
|
-
|
|
226
|
+
return JSON.stringify(abi.map((j) => JSON.parse(j)));
|
|
170
227
|
}
|
|
171
228
|
|
|
172
229
|
return abi;
|
|
@@ -197,26 +254,43 @@ export class Interface {
|
|
|
197
254
|
return this.functions[name];
|
|
198
255
|
}
|
|
199
256
|
}
|
|
200
|
-
logger.throwArgumentError(
|
|
257
|
+
logger.throwArgumentError(
|
|
258
|
+
"no matching function",
|
|
259
|
+
"sighash",
|
|
260
|
+
nameOrSignatureOrSighash,
|
|
261
|
+
);
|
|
201
262
|
}
|
|
202
263
|
|
|
203
264
|
// It is a bare name, look up the function (will return null if ambiguous)
|
|
204
265
|
if (nameOrSignatureOrSighash.indexOf("(") === -1) {
|
|
205
266
|
const name = nameOrSignatureOrSighash.trim();
|
|
206
|
-
const matching = Object.keys(this.functions).filter(
|
|
267
|
+
const matching = Object.keys(this.functions).filter(
|
|
268
|
+
(f) => f.split("(" /* fix:) */)[0] === name,
|
|
269
|
+
);
|
|
207
270
|
if (matching.length === 0) {
|
|
208
271
|
logger.throwArgumentError("no matching function", "name", name);
|
|
209
272
|
} else if (matching.length > 1) {
|
|
210
|
-
logger.throwArgumentError(
|
|
273
|
+
logger.throwArgumentError(
|
|
274
|
+
"multiple matching functions",
|
|
275
|
+
"name",
|
|
276
|
+
name,
|
|
277
|
+
);
|
|
211
278
|
}
|
|
212
279
|
|
|
213
280
|
return this.functions[matching[0]];
|
|
214
281
|
}
|
|
215
282
|
|
|
216
283
|
// Normalize the signature and lookup the function
|
|
217
|
-
const result =
|
|
284
|
+
const result =
|
|
285
|
+
this.functions[
|
|
286
|
+
FunctionFragment.fromString(nameOrSignatureOrSighash).format()
|
|
287
|
+
];
|
|
218
288
|
if (!result) {
|
|
219
|
-
logger.throwArgumentError(
|
|
289
|
+
logger.throwArgumentError(
|
|
290
|
+
"no matching function",
|
|
291
|
+
"signature",
|
|
292
|
+
nameOrSignatureOrSighash,
|
|
293
|
+
);
|
|
220
294
|
}
|
|
221
295
|
return result;
|
|
222
296
|
}
|
|
@@ -230,26 +304,43 @@ export class Interface {
|
|
|
230
304
|
return this.events[name];
|
|
231
305
|
}
|
|
232
306
|
}
|
|
233
|
-
logger.throwArgumentError(
|
|
307
|
+
logger.throwArgumentError(
|
|
308
|
+
"no matching event",
|
|
309
|
+
"topichash",
|
|
310
|
+
topichash,
|
|
311
|
+
);
|
|
234
312
|
}
|
|
235
313
|
|
|
236
314
|
// It is a bare name, look up the function (will return null if ambiguous)
|
|
237
315
|
if (nameOrSignatureOrTopic.indexOf("(") === -1) {
|
|
238
316
|
const name = nameOrSignatureOrTopic.trim();
|
|
239
|
-
const matching = Object.keys(this.events).filter(
|
|
317
|
+
const matching = Object.keys(this.events).filter(
|
|
318
|
+
(f) => f.split("(" /* fix:) */)[0] === name,
|
|
319
|
+
);
|
|
240
320
|
if (matching.length === 0) {
|
|
241
321
|
logger.throwArgumentError("no matching event", "name", name);
|
|
242
322
|
} else if (matching.length > 1) {
|
|
243
|
-
logger.throwArgumentError(
|
|
323
|
+
logger.throwArgumentError(
|
|
324
|
+
"multiple matching events",
|
|
325
|
+
"name",
|
|
326
|
+
name,
|
|
327
|
+
);
|
|
244
328
|
}
|
|
245
329
|
|
|
246
330
|
return this.events[matching[0]];
|
|
247
331
|
}
|
|
248
332
|
|
|
249
333
|
// Normalize the signature and lookup the function
|
|
250
|
-
const result =
|
|
334
|
+
const result =
|
|
335
|
+
this.events[
|
|
336
|
+
EventFragment.fromString(nameOrSignatureOrTopic).format()
|
|
337
|
+
];
|
|
251
338
|
if (!result) {
|
|
252
|
-
logger.throwArgumentError(
|
|
339
|
+
logger.throwArgumentError(
|
|
340
|
+
"no matching event",
|
|
341
|
+
"signature",
|
|
342
|
+
nameOrSignatureOrTopic,
|
|
343
|
+
);
|
|
253
344
|
}
|
|
254
345
|
return result;
|
|
255
346
|
}
|
|
@@ -257,40 +348,59 @@ export class Interface {
|
|
|
257
348
|
// Find a function definition by any means necessary (unless it is ambiguous)
|
|
258
349
|
getError(nameOrSignatureOrSighash: string): ErrorFragment {
|
|
259
350
|
if (isHexString(nameOrSignatureOrSighash)) {
|
|
260
|
-
const getSighash = getStatic<
|
|
351
|
+
const getSighash = getStatic<
|
|
352
|
+
(f: ErrorFragment | FunctionFragment) => string
|
|
353
|
+
>(this.constructor, "getSighash");
|
|
261
354
|
for (const name in this.errors) {
|
|
262
355
|
const error = this.errors[name];
|
|
263
356
|
if (nameOrSignatureOrSighash === getSighash(error)) {
|
|
264
357
|
return this.errors[name];
|
|
265
358
|
}
|
|
266
359
|
}
|
|
267
|
-
logger.throwArgumentError(
|
|
360
|
+
logger.throwArgumentError(
|
|
361
|
+
"no matching error",
|
|
362
|
+
"sighash",
|
|
363
|
+
nameOrSignatureOrSighash,
|
|
364
|
+
);
|
|
268
365
|
}
|
|
269
366
|
|
|
270
367
|
// It is a bare name, look up the function (will return null if ambiguous)
|
|
271
368
|
if (nameOrSignatureOrSighash.indexOf("(") === -1) {
|
|
272
369
|
const name = nameOrSignatureOrSighash.trim();
|
|
273
|
-
const matching = Object.keys(this.errors).filter(
|
|
370
|
+
const matching = Object.keys(this.errors).filter(
|
|
371
|
+
(f) => f.split("(" /* fix:) */)[0] === name,
|
|
372
|
+
);
|
|
274
373
|
if (matching.length === 0) {
|
|
275
374
|
logger.throwArgumentError("no matching error", "name", name);
|
|
276
375
|
} else if (matching.length > 1) {
|
|
277
|
-
logger.throwArgumentError(
|
|
376
|
+
logger.throwArgumentError(
|
|
377
|
+
"multiple matching errors",
|
|
378
|
+
"name",
|
|
379
|
+
name,
|
|
380
|
+
);
|
|
278
381
|
}
|
|
279
382
|
|
|
280
383
|
return this.errors[matching[0]];
|
|
281
384
|
}
|
|
282
385
|
|
|
283
386
|
// Normalize the signature and lookup the function
|
|
284
|
-
const result =
|
|
387
|
+
const result =
|
|
388
|
+
this.errors[
|
|
389
|
+
FunctionFragment.fromString(nameOrSignatureOrSighash).format()
|
|
390
|
+
];
|
|
285
391
|
if (!result) {
|
|
286
|
-
logger.throwArgumentError(
|
|
392
|
+
logger.throwArgumentError(
|
|
393
|
+
"no matching error",
|
|
394
|
+
"signature",
|
|
395
|
+
nameOrSignatureOrSighash,
|
|
396
|
+
);
|
|
287
397
|
}
|
|
288
398
|
return result;
|
|
289
399
|
}
|
|
290
400
|
|
|
291
401
|
// Get the sighash (the bytes4 selector) used by Solidity to identify a function
|
|
292
402
|
getSighash(fragment: ErrorFragment | FunctionFragment | string): string {
|
|
293
|
-
if (typeof
|
|
403
|
+
if (typeof fragment === "string") {
|
|
294
404
|
try {
|
|
295
405
|
fragment = this.getFunction(fragment);
|
|
296
406
|
} catch (error) {
|
|
@@ -302,86 +412,121 @@ export class Interface {
|
|
|
302
412
|
}
|
|
303
413
|
}
|
|
304
414
|
|
|
305
|
-
return getStatic<(f: ErrorFragment | FunctionFragment) => string>(
|
|
415
|
+
return getStatic<(f: ErrorFragment | FunctionFragment) => string>(
|
|
416
|
+
this.constructor,
|
|
417
|
+
"getSighash",
|
|
418
|
+
)(fragment);
|
|
306
419
|
}
|
|
307
420
|
|
|
308
421
|
// Get the topic (the bytes32 hash) used by Solidity to identify an event
|
|
309
422
|
getEventTopic(eventFragment: EventFragment | string): string {
|
|
310
|
-
if (typeof
|
|
423
|
+
if (typeof eventFragment === "string") {
|
|
311
424
|
eventFragment = this.getEvent(eventFragment);
|
|
312
425
|
}
|
|
313
426
|
|
|
314
|
-
return getStatic<(e: EventFragment) => string>(
|
|
427
|
+
return getStatic<(e: EventFragment) => string>(
|
|
428
|
+
this.constructor,
|
|
429
|
+
"getEventTopic",
|
|
430
|
+
)(eventFragment);
|
|
315
431
|
}
|
|
316
432
|
|
|
317
|
-
|
|
318
433
|
_decodeParams(params: ReadonlyArray<ParamType>, data: BytesLike): Result {
|
|
319
|
-
return this._abiCoder.decode(params, data)
|
|
434
|
+
return this._abiCoder.decode(params, data);
|
|
320
435
|
}
|
|
321
436
|
|
|
322
|
-
_encodeParams(
|
|
323
|
-
|
|
437
|
+
_encodeParams(
|
|
438
|
+
params: ReadonlyArray<ParamType>,
|
|
439
|
+
values: ReadonlyArray<any>,
|
|
440
|
+
): string {
|
|
441
|
+
return this._abiCoder.encode(params, values);
|
|
324
442
|
}
|
|
325
443
|
|
|
326
444
|
encodeDeploy(values?: ReadonlyArray<any>): string {
|
|
327
|
-
return this._encodeParams(this.deploy.inputs, values || [
|
|
445
|
+
return this._encodeParams(this.deploy.inputs, values || []);
|
|
328
446
|
}
|
|
329
447
|
|
|
330
|
-
decodeErrorResult(
|
|
331
|
-
|
|
448
|
+
decodeErrorResult(
|
|
449
|
+
fragment: ErrorFragment | string,
|
|
450
|
+
data: BytesLike,
|
|
451
|
+
): Result {
|
|
452
|
+
if (typeof fragment === "string") {
|
|
332
453
|
fragment = this.getError(fragment);
|
|
333
454
|
}
|
|
334
455
|
|
|
335
456
|
const bytes = arrayify(data);
|
|
336
457
|
|
|
337
458
|
if (hexlify(bytes.slice(0, 4)) !== this.getSighash(fragment)) {
|
|
338
|
-
logger.throwArgumentError(
|
|
459
|
+
logger.throwArgumentError(
|
|
460
|
+
`data signature does not match error ${fragment.name}.`,
|
|
461
|
+
"data",
|
|
462
|
+
hexlify(bytes),
|
|
463
|
+
);
|
|
339
464
|
}
|
|
340
465
|
|
|
341
466
|
return this._decodeParams(fragment.inputs, bytes.slice(4));
|
|
342
467
|
}
|
|
343
468
|
|
|
344
|
-
encodeErrorResult(
|
|
345
|
-
|
|
469
|
+
encodeErrorResult(
|
|
470
|
+
fragment: ErrorFragment | string,
|
|
471
|
+
values?: ReadonlyArray<any>,
|
|
472
|
+
): string {
|
|
473
|
+
if (typeof fragment === "string") {
|
|
346
474
|
fragment = this.getError(fragment);
|
|
347
475
|
}
|
|
348
476
|
|
|
349
|
-
return hexlify(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
477
|
+
return hexlify(
|
|
478
|
+
concat([
|
|
479
|
+
this.getSighash(fragment),
|
|
480
|
+
this._encodeParams(fragment.inputs, values || []),
|
|
481
|
+
]),
|
|
482
|
+
);
|
|
353
483
|
}
|
|
354
484
|
|
|
355
485
|
// Decode the data for a function call (e.g. tx.data)
|
|
356
|
-
decodeFunctionData(
|
|
357
|
-
|
|
486
|
+
decodeFunctionData(
|
|
487
|
+
functionFragment: FunctionFragment | string,
|
|
488
|
+
data: BytesLike,
|
|
489
|
+
): Result {
|
|
490
|
+
if (typeof functionFragment === "string") {
|
|
358
491
|
functionFragment = this.getFunction(functionFragment);
|
|
359
492
|
}
|
|
360
493
|
|
|
361
494
|
const bytes = arrayify(data);
|
|
362
495
|
|
|
363
496
|
if (hexlify(bytes.slice(0, 4)) !== this.getSighash(functionFragment)) {
|
|
364
|
-
logger.throwArgumentError(
|
|
497
|
+
logger.throwArgumentError(
|
|
498
|
+
`data signature does not match function ${functionFragment.name}.`,
|
|
499
|
+
"data",
|
|
500
|
+
hexlify(bytes),
|
|
501
|
+
);
|
|
365
502
|
}
|
|
366
503
|
|
|
367
504
|
return this._decodeParams(functionFragment.inputs, bytes.slice(4));
|
|
368
505
|
}
|
|
369
506
|
|
|
370
507
|
// Encode the data for a function call (e.g. tx.data)
|
|
371
|
-
encodeFunctionData(
|
|
372
|
-
|
|
508
|
+
encodeFunctionData(
|
|
509
|
+
functionFragment: FunctionFragment | string,
|
|
510
|
+
values?: ReadonlyArray<any>,
|
|
511
|
+
): string {
|
|
512
|
+
if (typeof functionFragment === "string") {
|
|
373
513
|
functionFragment = this.getFunction(functionFragment);
|
|
374
514
|
}
|
|
375
515
|
|
|
376
|
-
return hexlify(
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
516
|
+
return hexlify(
|
|
517
|
+
concat([
|
|
518
|
+
this.getSighash(functionFragment),
|
|
519
|
+
this._encodeParams(functionFragment.inputs, values || []),
|
|
520
|
+
]),
|
|
521
|
+
);
|
|
380
522
|
}
|
|
381
523
|
|
|
382
524
|
// Decode the result from a function call (e.g. from eth_call)
|
|
383
|
-
decodeFunctionResult(
|
|
384
|
-
|
|
525
|
+
decodeFunctionResult(
|
|
526
|
+
functionFragment: FunctionFragment | string,
|
|
527
|
+
data: BytesLike,
|
|
528
|
+
): Result {
|
|
529
|
+
if (typeof functionFragment === "string") {
|
|
385
530
|
functionFragment = this.getFunction(functionFragment);
|
|
386
531
|
}
|
|
387
532
|
|
|
@@ -395,75 +540,108 @@ export class Interface {
|
|
|
395
540
|
switch (bytes.length % this._abiCoder._getWordSize()) {
|
|
396
541
|
case 0:
|
|
397
542
|
try {
|
|
398
|
-
return this._abiCoder.decode(
|
|
399
|
-
|
|
543
|
+
return this._abiCoder.decode(
|
|
544
|
+
functionFragment.outputs,
|
|
545
|
+
bytes,
|
|
546
|
+
);
|
|
547
|
+
} catch (error) {}
|
|
400
548
|
break;
|
|
401
549
|
|
|
402
550
|
case 4: {
|
|
403
551
|
const selector = hexlify(bytes.slice(0, 4));
|
|
404
552
|
const builtin = BuiltinErrors[selector];
|
|
405
553
|
if (builtin) {
|
|
406
|
-
errorArgs = this._abiCoder.decode(
|
|
554
|
+
errorArgs = this._abiCoder.decode(
|
|
555
|
+
builtin.inputs,
|
|
556
|
+
bytes.slice(4),
|
|
557
|
+
);
|
|
407
558
|
errorName = builtin.name;
|
|
408
559
|
errorSignature = builtin.signature;
|
|
409
|
-
if (builtin.reason) {
|
|
560
|
+
if (builtin.reason) {
|
|
561
|
+
reason = errorArgs[0];
|
|
562
|
+
}
|
|
410
563
|
if (errorName === "Error") {
|
|
411
|
-
message = `; VM Exception while processing transaction: reverted with reason string ${
|
|
564
|
+
message = `; VM Exception while processing transaction: reverted with reason string ${JSON.stringify(errorArgs[0])}`;
|
|
412
565
|
} else if (errorName === "Panic") {
|
|
413
|
-
message = `; VM Exception while processing transaction: reverted with panic code ${
|
|
566
|
+
message = `; VM Exception while processing transaction: reverted with panic code ${errorArgs[0]}`;
|
|
414
567
|
}
|
|
415
568
|
} else {
|
|
416
569
|
try {
|
|
417
570
|
const error = this.getError(selector);
|
|
418
|
-
errorArgs = this._abiCoder.decode(
|
|
571
|
+
errorArgs = this._abiCoder.decode(
|
|
572
|
+
error.inputs,
|
|
573
|
+
bytes.slice(4),
|
|
574
|
+
);
|
|
419
575
|
errorName = error.name;
|
|
420
576
|
errorSignature = error.format();
|
|
421
|
-
} catch (error) {
|
|
577
|
+
} catch (error) {}
|
|
422
578
|
}
|
|
423
579
|
break;
|
|
424
580
|
}
|
|
425
581
|
}
|
|
426
582
|
|
|
427
|
-
return logger.throwError(
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
583
|
+
return logger.throwError(
|
|
584
|
+
"call revert exception" + message,
|
|
585
|
+
Logger.errors.CALL_EXCEPTION,
|
|
586
|
+
{
|
|
587
|
+
method: functionFragment.format(),
|
|
588
|
+
data: hexlify(data),
|
|
589
|
+
errorArgs,
|
|
590
|
+
errorName,
|
|
591
|
+
errorSignature,
|
|
592
|
+
reason,
|
|
593
|
+
},
|
|
594
|
+
);
|
|
431
595
|
}
|
|
432
596
|
|
|
433
597
|
// Encode the result for a function call (e.g. for eth_call)
|
|
434
|
-
encodeFunctionResult(
|
|
435
|
-
|
|
598
|
+
encodeFunctionResult(
|
|
599
|
+
functionFragment: FunctionFragment | string,
|
|
600
|
+
values?: ReadonlyArray<any>,
|
|
601
|
+
): string {
|
|
602
|
+
if (typeof functionFragment === "string") {
|
|
436
603
|
functionFragment = this.getFunction(functionFragment);
|
|
437
604
|
}
|
|
438
605
|
|
|
439
|
-
return hexlify(
|
|
606
|
+
return hexlify(
|
|
607
|
+
this._abiCoder.encode(functionFragment.outputs, values || []),
|
|
608
|
+
);
|
|
440
609
|
}
|
|
441
610
|
|
|
442
611
|
// Create the filter for the event with search criteria (e.g. for eth_filterLog)
|
|
443
|
-
encodeFilterTopics(
|
|
444
|
-
|
|
612
|
+
encodeFilterTopics(
|
|
613
|
+
eventFragment: EventFragment | string,
|
|
614
|
+
values: ReadonlyArray<any>,
|
|
615
|
+
): Array<string | Array<string>> {
|
|
616
|
+
if (typeof eventFragment === "string") {
|
|
445
617
|
eventFragment = this.getEvent(eventFragment);
|
|
446
618
|
}
|
|
447
619
|
|
|
448
620
|
if (values.length > eventFragment.inputs.length) {
|
|
449
|
-
logger.throwError(
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
621
|
+
logger.throwError(
|
|
622
|
+
"too many arguments for " + eventFragment.format(),
|
|
623
|
+
Logger.errors.UNEXPECTED_ARGUMENT,
|
|
624
|
+
{
|
|
625
|
+
argument: "values",
|
|
626
|
+
value: values,
|
|
627
|
+
},
|
|
628
|
+
);
|
|
453
629
|
}
|
|
454
630
|
|
|
455
631
|
let topics: Array<string | Array<string>> = [];
|
|
456
|
-
if (!eventFragment.anonymous) {
|
|
632
|
+
if (!eventFragment.anonymous) {
|
|
633
|
+
topics.push(this.getEventTopic(eventFragment));
|
|
634
|
+
}
|
|
457
635
|
|
|
458
636
|
const encodeTopic = (param: ParamType, value: any): string => {
|
|
459
637
|
if (param.type === "string") {
|
|
460
|
-
|
|
638
|
+
return id(value);
|
|
461
639
|
} else if (param.type === "bytes") {
|
|
462
|
-
|
|
640
|
+
return keccak256(hexlify(value));
|
|
463
641
|
}
|
|
464
642
|
|
|
465
|
-
if (param.type === "bool" && typeof
|
|
466
|
-
value =
|
|
643
|
+
if (param.type === "bool" && typeof value === "boolean") {
|
|
644
|
+
value = value ? "0x01" : "0x00";
|
|
467
645
|
}
|
|
468
646
|
|
|
469
647
|
if (param.type.match(/^u?int/)) {
|
|
@@ -471,25 +649,37 @@ export class Interface {
|
|
|
471
649
|
}
|
|
472
650
|
|
|
473
651
|
// Check addresses are valid
|
|
474
|
-
if (param.type === "address") {
|
|
652
|
+
if (param.type === "address") {
|
|
653
|
+
this._abiCoder.encode(["address"], [value]);
|
|
654
|
+
}
|
|
475
655
|
return hexZeroPad(hexlify(value), 32);
|
|
476
656
|
};
|
|
477
657
|
|
|
478
658
|
values.forEach((value, index) => {
|
|
479
|
-
|
|
480
659
|
let param = (<EventFragment>eventFragment).inputs[index];
|
|
481
660
|
|
|
482
661
|
if (!param.indexed) {
|
|
483
662
|
if (value != null) {
|
|
484
|
-
logger.throwArgumentError(
|
|
663
|
+
logger.throwArgumentError(
|
|
664
|
+
"cannot filter non-indexed parameters; must be null",
|
|
665
|
+
"contract." + param.name,
|
|
666
|
+
value,
|
|
667
|
+
);
|
|
485
668
|
}
|
|
486
669
|
return;
|
|
487
670
|
}
|
|
488
671
|
|
|
489
672
|
if (value == null) {
|
|
490
673
|
topics.push(null);
|
|
491
|
-
} else if (
|
|
492
|
-
|
|
674
|
+
} else if (
|
|
675
|
+
param.baseType === "array" ||
|
|
676
|
+
param.baseType === "tuple"
|
|
677
|
+
) {
|
|
678
|
+
logger.throwArgumentError(
|
|
679
|
+
"filtering with tuples or arrays not supported",
|
|
680
|
+
"contract." + param.name,
|
|
681
|
+
value,
|
|
682
|
+
);
|
|
493
683
|
} else if (Array.isArray(value)) {
|
|
494
684
|
topics.push(value.map((value) => encodeTopic(param, value)));
|
|
495
685
|
} else {
|
|
@@ -505,36 +695,46 @@ export class Interface {
|
|
|
505
695
|
return topics;
|
|
506
696
|
}
|
|
507
697
|
|
|
508
|
-
encodeEventLog(
|
|
509
|
-
|
|
698
|
+
encodeEventLog(
|
|
699
|
+
eventFragment: EventFragment | string,
|
|
700
|
+
values: ReadonlyArray<any>,
|
|
701
|
+
): { data: string; topics: Array<string> } {
|
|
702
|
+
if (typeof eventFragment === "string") {
|
|
510
703
|
eventFragment = this.getEvent(eventFragment);
|
|
511
704
|
}
|
|
512
705
|
|
|
513
|
-
const topics: Array<string> = [
|
|
706
|
+
const topics: Array<string> = [];
|
|
514
707
|
|
|
515
|
-
const dataTypes: Array<ParamType> = [
|
|
516
|
-
const dataValues: Array<string> = [
|
|
708
|
+
const dataTypes: Array<ParamType> = [];
|
|
709
|
+
const dataValues: Array<string> = [];
|
|
517
710
|
|
|
518
711
|
if (!eventFragment.anonymous) {
|
|
519
712
|
topics.push(this.getEventTopic(eventFragment));
|
|
520
713
|
}
|
|
521
714
|
|
|
522
715
|
if (values.length !== eventFragment.inputs.length) {
|
|
523
|
-
logger.throwArgumentError(
|
|
716
|
+
logger.throwArgumentError(
|
|
717
|
+
"event arguments/values mismatch",
|
|
718
|
+
"values",
|
|
719
|
+
values,
|
|
720
|
+
);
|
|
524
721
|
}
|
|
525
722
|
|
|
526
723
|
eventFragment.inputs.forEach((param, index) => {
|
|
527
724
|
const value = values[index];
|
|
528
725
|
if (param.indexed) {
|
|
529
726
|
if (param.type === "string") {
|
|
530
|
-
topics.push(id(value))
|
|
727
|
+
topics.push(id(value));
|
|
531
728
|
} else if (param.type === "bytes") {
|
|
532
|
-
topics.push(keccak256(value))
|
|
533
|
-
} else if (
|
|
729
|
+
topics.push(keccak256(value));
|
|
730
|
+
} else if (
|
|
731
|
+
param.baseType === "tuple" ||
|
|
732
|
+
param.baseType === "array"
|
|
733
|
+
) {
|
|
534
734
|
// @TODO
|
|
535
735
|
throw new Error("not implemented");
|
|
536
736
|
} else {
|
|
537
|
-
topics.push(this._abiCoder.encode([
|
|
737
|
+
topics.push(this._abiCoder.encode([param.type], [value]));
|
|
538
738
|
}
|
|
539
739
|
} else {
|
|
540
740
|
dataTypes.push(param);
|
|
@@ -543,21 +743,36 @@ export class Interface {
|
|
|
543
743
|
});
|
|
544
744
|
|
|
545
745
|
return {
|
|
546
|
-
data: this._abiCoder.encode(dataTypes
|
|
547
|
-
topics: topics
|
|
746
|
+
data: this._abiCoder.encode(dataTypes, dataValues),
|
|
747
|
+
topics: topics,
|
|
548
748
|
};
|
|
549
749
|
}
|
|
550
750
|
|
|
551
751
|
// Decode a filter for the event and the search criteria
|
|
552
|
-
decodeEventLog(
|
|
553
|
-
|
|
752
|
+
decodeEventLog(
|
|
753
|
+
eventFragment: EventFragment | string,
|
|
754
|
+
data: BytesLike,
|
|
755
|
+
topics?: ReadonlyArray<string>,
|
|
756
|
+
): Result {
|
|
757
|
+
if (typeof eventFragment === "string") {
|
|
554
758
|
eventFragment = this.getEvent(eventFragment);
|
|
555
759
|
}
|
|
556
760
|
|
|
557
761
|
if (topics != null && !eventFragment.anonymous) {
|
|
558
762
|
let topicHash = this.getEventTopic(eventFragment);
|
|
559
|
-
if (
|
|
560
|
-
|
|
763
|
+
if (
|
|
764
|
+
!isHexString(topics[0], 32) ||
|
|
765
|
+
topics[0].toLowerCase() !== topicHash
|
|
766
|
+
) {
|
|
767
|
+
logger.throwError(
|
|
768
|
+
"fragment/topic mismatch",
|
|
769
|
+
Logger.errors.INVALID_ARGUMENT,
|
|
770
|
+
{
|
|
771
|
+
argument: "topics[0]",
|
|
772
|
+
expected: topicHash,
|
|
773
|
+
value: topics[0],
|
|
774
|
+
},
|
|
775
|
+
);
|
|
561
776
|
}
|
|
562
777
|
topics = topics.slice(1);
|
|
563
778
|
}
|
|
@@ -568,8 +783,18 @@ export class Interface {
|
|
|
568
783
|
|
|
569
784
|
eventFragment.inputs.forEach((param, index) => {
|
|
570
785
|
if (param.indexed) {
|
|
571
|
-
if (
|
|
572
|
-
|
|
786
|
+
if (
|
|
787
|
+
param.type === "string" ||
|
|
788
|
+
param.type === "bytes" ||
|
|
789
|
+
param.baseType === "tuple" ||
|
|
790
|
+
param.baseType === "array"
|
|
791
|
+
) {
|
|
792
|
+
indexed.push(
|
|
793
|
+
ParamType.fromObject({
|
|
794
|
+
type: "bytes32",
|
|
795
|
+
name: param.name,
|
|
796
|
+
}),
|
|
797
|
+
);
|
|
573
798
|
dynamic.push(true);
|
|
574
799
|
} else {
|
|
575
800
|
indexed.push(param);
|
|
@@ -581,19 +806,27 @@ export class Interface {
|
|
|
581
806
|
}
|
|
582
807
|
});
|
|
583
808
|
|
|
584
|
-
let resultIndexed =
|
|
809
|
+
let resultIndexed =
|
|
810
|
+
topics != null
|
|
811
|
+
? this._abiCoder.decode(indexed, concat(topics))
|
|
812
|
+
: null;
|
|
585
813
|
let resultNonIndexed = this._abiCoder.decode(nonIndexed, data, true);
|
|
586
814
|
|
|
587
|
-
let result:
|
|
588
|
-
let nonIndexedIndex = 0,
|
|
815
|
+
let result: Array<any> & { [key: string]: any } = [];
|
|
816
|
+
let nonIndexedIndex = 0,
|
|
817
|
+
indexedIndex = 0;
|
|
589
818
|
eventFragment.inputs.forEach((param, index) => {
|
|
590
819
|
if (param.indexed) {
|
|
591
820
|
if (resultIndexed == null) {
|
|
592
|
-
result[index] = new Indexed({
|
|
593
|
-
|
|
821
|
+
result[index] = new Indexed({
|
|
822
|
+
_isIndexed: true,
|
|
823
|
+
hash: null,
|
|
824
|
+
});
|
|
594
825
|
} else if (dynamic[index]) {
|
|
595
|
-
result[index] = new Indexed({
|
|
596
|
-
|
|
826
|
+
result[index] = new Indexed({
|
|
827
|
+
_isIndexed: true,
|
|
828
|
+
hash: resultIndexed[indexedIndex++],
|
|
829
|
+
});
|
|
597
830
|
} else {
|
|
598
831
|
try {
|
|
599
832
|
result[index] = resultIndexed[indexedIndex++];
|
|
@@ -617,7 +850,12 @@ export class Interface {
|
|
|
617
850
|
if (value instanceof Error) {
|
|
618
851
|
Object.defineProperty(result, param.name, {
|
|
619
852
|
enumerable: true,
|
|
620
|
-
get: () => {
|
|
853
|
+
get: () => {
|
|
854
|
+
throw wrapAccessError(
|
|
855
|
+
`property ${JSON.stringify(param.name)}`,
|
|
856
|
+
value,
|
|
857
|
+
);
|
|
858
|
+
},
|
|
621
859
|
});
|
|
622
860
|
} else {
|
|
623
861
|
result[param.name] = value;
|
|
@@ -631,7 +869,9 @@ export class Interface {
|
|
|
631
869
|
if (value instanceof Error) {
|
|
632
870
|
Object.defineProperty(result, i, {
|
|
633
871
|
enumerable: true,
|
|
634
|
-
get: () => {
|
|
872
|
+
get: () => {
|
|
873
|
+
throw wrapAccessError(`index ${i}`, value);
|
|
874
|
+
},
|
|
635
875
|
});
|
|
636
876
|
}
|
|
637
877
|
}
|
|
@@ -641,13 +881,21 @@ export class Interface {
|
|
|
641
881
|
|
|
642
882
|
// Given a transaction, find the matching function fragment (if any) and
|
|
643
883
|
// determine all its properties and call parameters
|
|
644
|
-
parseTransaction(tx: {
|
|
645
|
-
|
|
884
|
+
parseTransaction(tx: {
|
|
885
|
+
data: string;
|
|
886
|
+
value?: BigNumberish;
|
|
887
|
+
}): TransactionDescription {
|
|
888
|
+
let fragment = this.getFunction(tx.data.substring(0, 10).toLowerCase());
|
|
646
889
|
|
|
647
|
-
if (!fragment) {
|
|
890
|
+
if (!fragment) {
|
|
891
|
+
return null;
|
|
892
|
+
}
|
|
648
893
|
|
|
649
894
|
return new TransactionDescription({
|
|
650
|
-
args: this._abiCoder.decode(
|
|
895
|
+
args: this._abiCoder.decode(
|
|
896
|
+
fragment.inputs,
|
|
897
|
+
"0x" + tx.data.substring(10),
|
|
898
|
+
),
|
|
651
899
|
functionFragment: fragment,
|
|
652
900
|
name: fragment.name,
|
|
653
901
|
signature: fragment.format(),
|
|
@@ -661,33 +909,39 @@ export class Interface {
|
|
|
661
909
|
|
|
662
910
|
// Given an event log, find the matching event fragment (if any) and
|
|
663
911
|
// determine all its properties and values
|
|
664
|
-
parseLog(log: { topics: Array<string
|
|
912
|
+
parseLog(log: { topics: Array<string>; data: string }): LogDescription {
|
|
665
913
|
let fragment = this.getEvent(log.topics[0]);
|
|
666
914
|
|
|
667
|
-
if (!fragment || fragment.anonymous) {
|
|
915
|
+
if (!fragment || fragment.anonymous) {
|
|
916
|
+
return null;
|
|
917
|
+
}
|
|
668
918
|
|
|
669
919
|
// @TODO: If anonymous, and the only method, and the input count matches, should we parse?
|
|
670
920
|
// Probably not, because just because it is the only event in the ABI does
|
|
671
921
|
// not mean we have the full ABI; maybe just a fragment?
|
|
672
922
|
|
|
673
|
-
|
|
674
|
-
return new LogDescription({
|
|
923
|
+
return new LogDescription({
|
|
675
924
|
eventFragment: fragment,
|
|
676
925
|
name: fragment.name,
|
|
677
926
|
signature: fragment.format(),
|
|
678
927
|
topic: this.getEventTopic(fragment),
|
|
679
|
-
args: this.decodeEventLog(fragment, log.data, log.topics)
|
|
928
|
+
args: this.decodeEventLog(fragment, log.data, log.topics),
|
|
680
929
|
});
|
|
681
930
|
}
|
|
682
931
|
|
|
683
932
|
parseError(data: BytesLike): ErrorDescription {
|
|
684
933
|
const hexData = hexlify(data);
|
|
685
|
-
let fragment = this.getError(hexData.substring(0, 10).toLowerCase())
|
|
934
|
+
let fragment = this.getError(hexData.substring(0, 10).toLowerCase());
|
|
686
935
|
|
|
687
|
-
if (!fragment) {
|
|
936
|
+
if (!fragment) {
|
|
937
|
+
return null;
|
|
938
|
+
}
|
|
688
939
|
|
|
689
940
|
return new ErrorDescription({
|
|
690
|
-
args: this._abiCoder.decode(
|
|
941
|
+
args: this._abiCoder.decode(
|
|
942
|
+
fragment.inputs,
|
|
943
|
+
"0x" + hexData.substring(10),
|
|
944
|
+
),
|
|
691
945
|
errorFragment: fragment,
|
|
692
946
|
name: fragment.name,
|
|
693
947
|
signature: fragment.format(),
|
|
@@ -695,7 +949,6 @@ export class Interface {
|
|
|
695
949
|
});
|
|
696
950
|
}
|
|
697
951
|
|
|
698
|
-
|
|
699
952
|
/*
|
|
700
953
|
static from(value: Array<Fragment | string | JsonAbi> | string | Interface) {
|
|
701
954
|
if (Interface.isInterface(value)) {
|
|
@@ -712,4 +965,3 @@ export class Interface {
|
|
|
712
965
|
return !!(value && value._isInterface);
|
|
713
966
|
}
|
|
714
967
|
}
|
|
715
|
-
|