@qevm/abi 5.7.1
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/LICENSE.md +21 -0
- package/README.md +52 -0
- package/lib/_version.d.ts +2 -0
- package/lib/_version.d.ts.map +1 -0
- package/lib/_version.js +5 -0
- package/lib/_version.js.map +1 -0
- package/lib/abi-coder.d.ts +17 -0
- package/lib/abi-coder.d.ts.map +1 -0
- package/lib/abi-coder.js +99 -0
- package/lib/abi-coder.js.map +1 -0
- package/lib/coders/abstract-coder.d.ts +53 -0
- package/lib/coders/abstract-coder.d.ts.map +1 -0
- package/lib/coders/abstract-coder.js +153 -0
- package/lib/coders/abstract-coder.js.map +1 -0
- package/lib/coders/address.d.ts +8 -0
- package/lib/coders/address.d.ts.map +1 -0
- package/lib/coders/address.js +28 -0
- package/lib/coders/address.js.map +1 -0
- package/lib/coders/anonymous.d.ts +9 -0
- package/lib/coders/anonymous.d.ts.map +1 -0
- package/lib/coders/anonymous.js +22 -0
- package/lib/coders/anonymous.js.map +1 -0
- package/lib/coders/array.d.ts +14 -0
- package/lib/coders/array.d.ts.map +1 -0
- package/lib/coders/array.js +214 -0
- package/lib/coders/array.js.map +1 -0
- package/lib/coders/boolean.d.ts +8 -0
- package/lib/coders/boolean.d.ts.map +1 -0
- package/lib/coders/boolean.js +20 -0
- package/lib/coders/boolean.js.map +1 -0
- package/lib/coders/bytes.d.ts +12 -0
- package/lib/coders/bytes.d.ts.map +1 -0
- package/lib/coders/bytes.js +33 -0
- package/lib/coders/bytes.js.map +1 -0
- package/lib/coders/fixed-bytes.d.ts +10 -0
- package/lib/coders/fixed-bytes.d.ts.map +1 -0
- package/lib/coders/fixed-bytes.js +28 -0
- package/lib/coders/fixed-bytes.js.map +1 -0
- package/lib/coders/null.d.ts +8 -0
- package/lib/coders/null.d.ts.map +1 -0
- package/lib/coders/null.js +24 -0
- package/lib/coders/null.js.map +1 -0
- package/lib/coders/number.d.ts +11 -0
- package/lib/coders/number.d.ts.map +1 -0
- package/lib/coders/number.js +45 -0
- package/lib/coders/number.js.map +1 -0
- package/lib/coders/string.d.ts +9 -0
- package/lib/coders/string.d.ts.map +1 -0
- package/lib/coders/string.js +21 -0
- package/lib/coders/string.js.map +1 -0
- package/lib/coders/tuple.d.ts +11 -0
- package/lib/coders/tuple.d.ts.map +1 -0
- package/lib/coders/tuple.js +60 -0
- package/lib/coders/tuple.js.map +1 -0
- package/lib/fragments.d.ts +85 -0
- package/lib/fragments.d.ts.map +1 -0
- package/lib/fragments.js +863 -0
- package/lib/fragments.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +21 -0
- package/lib/index.js.map +1 -0
- package/lib/interface.d.ts +89 -0
- package/lib/interface.d.ts.map +1 -0
- package/lib/interface.js +617 -0
- package/lib/interface.js.map +1 -0
- package/package.json +33 -0
- package/src.ts/_version.ts +1 -0
- package/src.ts/abi-coder.ts +123 -0
- package/src.ts/coders/abstract-coder.ts +207 -0
- package/src.ts/coders/address.ts +31 -0
- package/src.ts/coders/anonymous.ts +25 -0
- package/src.ts/coders/array.ts +236 -0
- package/src.ts/coders/boolean.ts +23 -0
- package/src.ts/coders/bytes.ts +38 -0
- package/src.ts/coders/fixed-bytes.ts +30 -0
- package/src.ts/coders/null.ts +24 -0
- package/src.ts/coders/number.ts +57 -0
- package/src.ts/coders/string.ts +25 -0
- package/src.ts/coders/tuple.ts +61 -0
- package/src.ts/fragments.ts +1070 -0
- package/src.ts/index.ts +34 -0
- package/src.ts/interface.ts +715 -0
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "Eugene Kuleshov",
|
|
3
|
+
"dependencies": {
|
|
4
|
+
"@qevm/address": "^5.7.0",
|
|
5
|
+
"@qevm/bignumber": "^5.7.0",
|
|
6
|
+
"@qevm/bytes": "5.7.1",
|
|
7
|
+
"@qevm/constants": "^5.7.0",
|
|
8
|
+
"@qevm/hash": "^5.7.1",
|
|
9
|
+
"@qevm/keccak256": "^5.7.0",
|
|
10
|
+
"@ethersproject/logger": "^5.7.0",
|
|
11
|
+
"@ethersproject/properties": "^5.7.0",
|
|
12
|
+
"@qevm/strings": "^5.7.0"
|
|
13
|
+
},
|
|
14
|
+
"description": "Utilities and Classes for parsing, formatting and managing Ethereum ABIs.",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"Ethereum",
|
|
17
|
+
"qethers"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"main": "./lib/index.js",
|
|
21
|
+
"module": "./lib.esm/index.js",
|
|
22
|
+
"name": "@qevm/abi",
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
28
|
+
},
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"tarballHash": "0x76f6fd1617e7f7aacd876957111e5368a7e53b8aebf572b30f0f1a0cd2b4a603",
|
|
31
|
+
"types": "./lib/index.d.ts",
|
|
32
|
+
"version": "5.7.1"
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const version = "abi/5.7.0";
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// See: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
|
|
4
|
+
|
|
5
|
+
import { arrayify, BytesLike } from "@qevm/bytes";
|
|
6
|
+
import { defineReadOnly } from "@ethersproject/properties";
|
|
7
|
+
|
|
8
|
+
import { Logger } from "@ethersproject/logger";
|
|
9
|
+
import { version } from "./_version";
|
|
10
|
+
const logger = new Logger(version);
|
|
11
|
+
|
|
12
|
+
import { Coder, Reader, Result, Writer } from "./coders/abstract-coder";
|
|
13
|
+
import { AddressCoder } from "./coders/address";
|
|
14
|
+
import { ArrayCoder } from "./coders/array";
|
|
15
|
+
import { BooleanCoder } from "./coders/boolean";
|
|
16
|
+
import { BytesCoder } from "./coders/bytes";
|
|
17
|
+
import { FixedBytesCoder } from "./coders/fixed-bytes";
|
|
18
|
+
import { NullCoder } from "./coders/null";
|
|
19
|
+
import { NumberCoder } from "./coders/number";
|
|
20
|
+
import { StringCoder } from "./coders/string";
|
|
21
|
+
import { TupleCoder } from "./coders/tuple";
|
|
22
|
+
|
|
23
|
+
import { ParamType } from "./fragments";
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
const paramTypeBytes = new RegExp(/^bytes([0-9]*)$/);
|
|
27
|
+
const paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export type CoerceFunc = (type: string, value: any) => any;
|
|
31
|
+
|
|
32
|
+
export class AbiCoder {
|
|
33
|
+
readonly coerceFunc: CoerceFunc;
|
|
34
|
+
|
|
35
|
+
constructor(coerceFunc?: CoerceFunc) {
|
|
36
|
+
defineReadOnly(this, "coerceFunc", coerceFunc || null);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_getCoder(param: ParamType): Coder {
|
|
40
|
+
|
|
41
|
+
switch (param.baseType) {
|
|
42
|
+
case "address":
|
|
43
|
+
return new AddressCoder(param.name);
|
|
44
|
+
case "bool":
|
|
45
|
+
return new BooleanCoder(param.name);
|
|
46
|
+
case "string":
|
|
47
|
+
return new StringCoder(param.name);
|
|
48
|
+
case "bytes":
|
|
49
|
+
return new BytesCoder(param.name);
|
|
50
|
+
case "array":
|
|
51
|
+
return new ArrayCoder(this._getCoder(param.arrayChildren), param.arrayLength, param.name);
|
|
52
|
+
case "tuple":
|
|
53
|
+
return new TupleCoder((param.components || []).map((component) => {
|
|
54
|
+
return this._getCoder(component);
|
|
55
|
+
}), param.name);
|
|
56
|
+
case "":
|
|
57
|
+
return new NullCoder(param.name);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// u?int[0-9]*
|
|
61
|
+
let match = param.type.match(paramTypeNumber);
|
|
62
|
+
if (match) {
|
|
63
|
+
let size = parseInt(match[2] || "256");
|
|
64
|
+
if (size === 0 || size > 256 || (size % 8) !== 0) {
|
|
65
|
+
logger.throwArgumentError("invalid " + match[1] + " bit length", "param", param);
|
|
66
|
+
}
|
|
67
|
+
return new NumberCoder(size / 8, (match[1] === "int"), param.name);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// bytes[0-9]+
|
|
71
|
+
match = param.type.match(paramTypeBytes);
|
|
72
|
+
if (match) {
|
|
73
|
+
let size = parseInt(match[1]);
|
|
74
|
+
if (size === 0 || size > 32) {
|
|
75
|
+
logger.throwArgumentError("invalid bytes length", "param", param);
|
|
76
|
+
}
|
|
77
|
+
return new FixedBytesCoder(size, param.name);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return logger.throwArgumentError("invalid type", "type", param.type);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_getWordSize(): number { return 32; }
|
|
84
|
+
|
|
85
|
+
_getReader(data: Uint8Array, allowLoose?: boolean): Reader {
|
|
86
|
+
return new Reader(data, this._getWordSize(), this.coerceFunc, allowLoose);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
_getWriter(): Writer {
|
|
90
|
+
return new Writer(this._getWordSize());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
getDefaultValue(types: ReadonlyArray<string | ParamType>): Result {
|
|
94
|
+
const coders: Array<Coder> = types.map((type) => this._getCoder(ParamType.from(type)));
|
|
95
|
+
const coder = new TupleCoder(coders, "_");
|
|
96
|
+
return coder.defaultValue();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
encode(types: ReadonlyArray<string | ParamType>, values: ReadonlyArray<any>): string {
|
|
100
|
+
if (types.length !== values.length) {
|
|
101
|
+
logger.throwError("types/values length mismatch", Logger.errors.INVALID_ARGUMENT, {
|
|
102
|
+
count: { types: types.length, values: values.length },
|
|
103
|
+
value: { types: types, values: values }
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const coders = types.map((type) => this._getCoder(ParamType.from(type)));
|
|
108
|
+
const coder = (new TupleCoder(coders, "_"));
|
|
109
|
+
|
|
110
|
+
const writer = this._getWriter();
|
|
111
|
+
coder.encode(writer, values);
|
|
112
|
+
return writer.data;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
decode(types: ReadonlyArray<string | ParamType>, data: BytesLike, loose?: boolean): Result {
|
|
116
|
+
const coders: Array<Coder> = types.map((type) => this._getCoder(ParamType.from(type)));
|
|
117
|
+
const coder = new TupleCoder(coders, "_");
|
|
118
|
+
return coder.decode(this._getReader(arrayify(data), loose));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const defaultAbiCoder: AbiCoder = new AbiCoder();
|
|
123
|
+
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { arrayify, BytesLike, concat, hexConcat, hexlify } from "@qevm/bytes";
|
|
4
|
+
import { BigNumber, BigNumberish } from "@qevm/bignumber";
|
|
5
|
+
import { defineReadOnly } from "@ethersproject/properties";
|
|
6
|
+
|
|
7
|
+
import { Logger } from "@ethersproject/logger";
|
|
8
|
+
import { version } from "../_version";
|
|
9
|
+
const logger = new Logger(version);
|
|
10
|
+
|
|
11
|
+
export interface Result extends ReadonlyArray<any> {
|
|
12
|
+
readonly [key: string]: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function checkResultErrors(result: Result): Array<{ path: Array<string | number>, error: Error }> {
|
|
16
|
+
// Find the first error (if any)
|
|
17
|
+
const errors: Array<{ path: Array<string | number>, error: Error }> = [ ];
|
|
18
|
+
|
|
19
|
+
const checkErrors = function(path: Array<string | number>, object: any): void {
|
|
20
|
+
if (!Array.isArray(object)) { return; }
|
|
21
|
+
for (let key in object) {
|
|
22
|
+
const childPath = path.slice();
|
|
23
|
+
childPath.push(key);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
checkErrors(childPath, object[key]);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
errors.push({ path: childPath, error: error });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
checkErrors([ ], result);
|
|
33
|
+
|
|
34
|
+
return errors;
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type CoerceFunc = (type: string, value: any) => any;
|
|
39
|
+
|
|
40
|
+
export abstract class Coder {
|
|
41
|
+
|
|
42
|
+
// The coder name:
|
|
43
|
+
// - address, uint256, tuple, array, etc.
|
|
44
|
+
readonly name: string;
|
|
45
|
+
|
|
46
|
+
// The fully expanded type, including composite types:
|
|
47
|
+
// - address, uint256, tuple(address,bytes), uint256[3][4][], etc.
|
|
48
|
+
readonly type: string;
|
|
49
|
+
|
|
50
|
+
// The localName bound in the signature, in this example it is "baz":
|
|
51
|
+
// - tuple(address foo, uint bar) baz
|
|
52
|
+
readonly localName: string;
|
|
53
|
+
|
|
54
|
+
// Whether this type is dynamic:
|
|
55
|
+
// - Dynamic: bytes, string, address[], tuple(boolean[]), etc.
|
|
56
|
+
// - Not Dynamic: address, uint256, boolean[3], tuple(address, uint8)
|
|
57
|
+
readonly dynamic: boolean;
|
|
58
|
+
|
|
59
|
+
constructor(name: string, type: string, localName: string, dynamic: boolean) {
|
|
60
|
+
// @TODO: defineReadOnly these
|
|
61
|
+
this.name = name;
|
|
62
|
+
this.type = type;
|
|
63
|
+
this.localName = localName;
|
|
64
|
+
this.dynamic = dynamic;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_throwError(message: string, value: any): void {
|
|
68
|
+
logger.throwArgumentError(message, this.localName, value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
abstract encode(writer: Writer, value: any): number;
|
|
72
|
+
abstract decode(reader: Reader): any;
|
|
73
|
+
|
|
74
|
+
abstract defaultValue(): any;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export class Writer {
|
|
78
|
+
readonly wordSize: number;
|
|
79
|
+
|
|
80
|
+
_data: Array<Uint8Array>;
|
|
81
|
+
_dataLength: number;
|
|
82
|
+
_padding: Uint8Array;
|
|
83
|
+
|
|
84
|
+
constructor(wordSize?: number) {
|
|
85
|
+
defineReadOnly(this, "wordSize", wordSize || 32);
|
|
86
|
+
this._data = [ ];
|
|
87
|
+
this._dataLength = 0;
|
|
88
|
+
this._padding = new Uint8Array(wordSize);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get data(): string {
|
|
92
|
+
return hexConcat(this._data);
|
|
93
|
+
}
|
|
94
|
+
get length(): number { return this._dataLength; }
|
|
95
|
+
|
|
96
|
+
_writeData(data: Uint8Array): number {
|
|
97
|
+
this._data.push(data);
|
|
98
|
+
this._dataLength += data.length;
|
|
99
|
+
return data.length;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
appendWriter(writer: Writer): number {
|
|
103
|
+
return this._writeData(concat(writer._data));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Arrayish items; padded on the right to wordSize
|
|
107
|
+
writeBytes(value: BytesLike): number {
|
|
108
|
+
let bytes = arrayify(value);
|
|
109
|
+
const paddingOffset = bytes.length % this.wordSize;
|
|
110
|
+
if (paddingOffset) {
|
|
111
|
+
bytes = concat([ bytes, this._padding.slice(paddingOffset) ])
|
|
112
|
+
}
|
|
113
|
+
return this._writeData(bytes);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
_getValue(value: BigNumberish): Uint8Array {
|
|
117
|
+
let bytes = arrayify(BigNumber.from(value));
|
|
118
|
+
if (bytes.length > this.wordSize) {
|
|
119
|
+
logger.throwError("value out-of-bounds", Logger.errors.BUFFER_OVERRUN, {
|
|
120
|
+
length: this.wordSize,
|
|
121
|
+
offset: bytes.length
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (bytes.length % this.wordSize) {
|
|
125
|
+
bytes = concat([ this._padding.slice(bytes.length % this.wordSize), bytes ]);
|
|
126
|
+
}
|
|
127
|
+
return bytes;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// BigNumberish items; padded on the left to wordSize
|
|
131
|
+
writeValue(value: BigNumberish): number {
|
|
132
|
+
return this._writeData(this._getValue(value));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
writeUpdatableValue(): (value: BigNumberish) => void {
|
|
136
|
+
const offset = this._data.length;
|
|
137
|
+
this._data.push(this._padding);
|
|
138
|
+
this._dataLength += this.wordSize;
|
|
139
|
+
return (value: BigNumberish) => {
|
|
140
|
+
this._data[offset] = this._getValue(value);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export class Reader {
|
|
146
|
+
readonly wordSize: number;
|
|
147
|
+
readonly allowLoose: boolean;
|
|
148
|
+
|
|
149
|
+
readonly _data: Uint8Array;
|
|
150
|
+
readonly _coerceFunc: CoerceFunc;
|
|
151
|
+
|
|
152
|
+
_offset: number;
|
|
153
|
+
|
|
154
|
+
constructor(data: BytesLike, wordSize?: number, coerceFunc?: CoerceFunc, allowLoose?: boolean) {
|
|
155
|
+
defineReadOnly(this, "_data", arrayify(data));
|
|
156
|
+
defineReadOnly(this, "wordSize", wordSize || 32);
|
|
157
|
+
defineReadOnly(this, "_coerceFunc", coerceFunc);
|
|
158
|
+
defineReadOnly(this, "allowLoose", allowLoose);
|
|
159
|
+
|
|
160
|
+
this._offset = 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get data(): string { return hexlify(this._data); }
|
|
164
|
+
get consumed(): number { return this._offset; }
|
|
165
|
+
|
|
166
|
+
// The default Coerce function
|
|
167
|
+
static coerce(name: string, value: any): any {
|
|
168
|
+
let match = name.match("^u?int([0-9]+)$");
|
|
169
|
+
if (match && parseInt(match[1]) <= 48) { value = value.toNumber(); }
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
coerce(name: string, value: any): any {
|
|
174
|
+
if (this._coerceFunc) { return this._coerceFunc(name, value); }
|
|
175
|
+
return Reader.coerce(name, value);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
_peekBytes(offset: number, length: number, loose?: boolean): Uint8Array {
|
|
179
|
+
let alignedLength = Math.ceil(length / this.wordSize) * this.wordSize;
|
|
180
|
+
if (this._offset + alignedLength > this._data.length) {
|
|
181
|
+
if (this.allowLoose && loose && this._offset + length <= this._data.length) {
|
|
182
|
+
alignedLength = length;
|
|
183
|
+
} else {
|
|
184
|
+
logger.throwError("data out-of-bounds", Logger.errors.BUFFER_OVERRUN, {
|
|
185
|
+
length: this._data.length,
|
|
186
|
+
offset: this._offset + alignedLength
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return this._data.slice(this._offset, this._offset + alignedLength)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
subReader(offset: number): Reader {
|
|
194
|
+
return new Reader(this._data.slice(this._offset + offset), this.wordSize, this._coerceFunc, this.allowLoose);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
readBytes(length: number, loose?: boolean): Uint8Array {
|
|
198
|
+
let bytes = this._peekBytes(0, length, !!loose);
|
|
199
|
+
this._offset += bytes.length;
|
|
200
|
+
// @TODO: Make sure the length..end bytes are all 0?
|
|
201
|
+
return bytes.slice(0, length);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
readValue(): BigNumber {
|
|
205
|
+
return BigNumber.from(this.readBytes(this.wordSize));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { getAddress } from "@qevm/address";
|
|
4
|
+
import { hexZeroPad } from "@qevm/bytes";
|
|
5
|
+
|
|
6
|
+
import { Coder, Reader, Writer } from "./abstract-coder";
|
|
7
|
+
|
|
8
|
+
export class AddressCoder extends Coder {
|
|
9
|
+
|
|
10
|
+
constructor(localName: string) {
|
|
11
|
+
super("address", "address", localName, false);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
defaultValue(): string {
|
|
15
|
+
return "0x0000000000000000000000000000000000000000";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
encode(writer: Writer, value: string): number {
|
|
19
|
+
try {
|
|
20
|
+
value = getAddress(value)
|
|
21
|
+
} catch (error) {
|
|
22
|
+
this._throwError(error.message, value);
|
|
23
|
+
}
|
|
24
|
+
return writer.writeValue(value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
decode(reader: Reader): any {
|
|
28
|
+
return getAddress(hexZeroPad(reader.readValue().toHexString(), 20));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Coder, Reader, Writer } from "./abstract-coder";
|
|
4
|
+
|
|
5
|
+
// Clones the functionality of an existing Coder, but without a localName
|
|
6
|
+
export class AnonymousCoder extends Coder {
|
|
7
|
+
private coder: Coder;
|
|
8
|
+
|
|
9
|
+
constructor(coder: Coder) {
|
|
10
|
+
super(coder.name, coder.type, undefined, coder.dynamic);
|
|
11
|
+
this.coder = coder;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
defaultValue(): any {
|
|
15
|
+
return this.coder.defaultValue();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
encode(writer: Writer, value: any): number {
|
|
19
|
+
return this.coder.encode(writer, value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
decode(reader: Reader): any {
|
|
23
|
+
return this.coder.decode(reader);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Logger } from "@ethersproject/logger";
|
|
4
|
+
import { version } from "../_version";
|
|
5
|
+
const logger = new Logger(version);
|
|
6
|
+
|
|
7
|
+
import { Coder, Reader, Result, Writer } from "./abstract-coder";
|
|
8
|
+
import { AnonymousCoder } from "./anonymous";
|
|
9
|
+
|
|
10
|
+
export function pack(writer: Writer, coders: ReadonlyArray<Coder>, values: Array<any> | { [ name: string ]: any }): number {
|
|
11
|
+
let arrayValues: Array<any> = null;
|
|
12
|
+
|
|
13
|
+
if (Array.isArray(values)) {
|
|
14
|
+
arrayValues = values;
|
|
15
|
+
|
|
16
|
+
} else if (values && typeof(values) === "object") {
|
|
17
|
+
let unique: { [ name: string ]: boolean } = { };
|
|
18
|
+
|
|
19
|
+
arrayValues = coders.map((coder) => {
|
|
20
|
+
const name = coder.localName;
|
|
21
|
+
if (!name) {
|
|
22
|
+
logger.throwError("cannot encode object for signature with missing names", Logger.errors.INVALID_ARGUMENT, {
|
|
23
|
+
argument: "values",
|
|
24
|
+
coder: coder,
|
|
25
|
+
value: values
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (unique[name]) {
|
|
30
|
+
logger.throwError("cannot encode object for signature with duplicate names", Logger.errors.INVALID_ARGUMENT, {
|
|
31
|
+
argument: "values",
|
|
32
|
+
coder: coder,
|
|
33
|
+
value: values
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
unique[name] = true;
|
|
38
|
+
|
|
39
|
+
return values[name];
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
} else {
|
|
43
|
+
logger.throwArgumentError("invalid tuple value", "tuple", values);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (coders.length !== arrayValues.length) {
|
|
47
|
+
logger.throwArgumentError("types/value length mismatch", "tuple", values);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let staticWriter = new Writer(writer.wordSize);
|
|
51
|
+
let dynamicWriter = new Writer(writer.wordSize);
|
|
52
|
+
|
|
53
|
+
let updateFuncs: Array<(baseOffset: number) => void> = [];
|
|
54
|
+
coders.forEach((coder, index) => {
|
|
55
|
+
let value = arrayValues[index];
|
|
56
|
+
|
|
57
|
+
if (coder.dynamic) {
|
|
58
|
+
// Get current dynamic offset (for the future pointer)
|
|
59
|
+
let dynamicOffset = dynamicWriter.length;
|
|
60
|
+
|
|
61
|
+
// Encode the dynamic value into the dynamicWriter
|
|
62
|
+
coder.encode(dynamicWriter, value);
|
|
63
|
+
|
|
64
|
+
// Prepare to populate the correct offset once we are done
|
|
65
|
+
let updateFunc = staticWriter.writeUpdatableValue();
|
|
66
|
+
updateFuncs.push((baseOffset: number) => {
|
|
67
|
+
updateFunc(baseOffset + dynamicOffset);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
} else {
|
|
71
|
+
coder.encode(staticWriter, value);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Backfill all the dynamic offsets, now that we know the static length
|
|
76
|
+
updateFuncs.forEach((func) => { func(staticWriter.length); });
|
|
77
|
+
|
|
78
|
+
let length = writer.appendWriter(staticWriter);
|
|
79
|
+
length += writer.appendWriter(dynamicWriter);
|
|
80
|
+
return length;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function unpack(reader: Reader, coders: Array<Coder>): Result {
|
|
84
|
+
let values: any = [];
|
|
85
|
+
|
|
86
|
+
// A reader anchored to this base
|
|
87
|
+
let baseReader = reader.subReader(0);
|
|
88
|
+
|
|
89
|
+
coders.forEach((coder) => {
|
|
90
|
+
let value: any = null;
|
|
91
|
+
|
|
92
|
+
if (coder.dynamic) {
|
|
93
|
+
let offset = reader.readValue();
|
|
94
|
+
let offsetReader = baseReader.subReader(offset.toNumber());
|
|
95
|
+
try {
|
|
96
|
+
value = coder.decode(offsetReader);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
// Cannot recover from this
|
|
99
|
+
if (error.code === Logger.errors.BUFFER_OVERRUN) { throw error; }
|
|
100
|
+
value = error;
|
|
101
|
+
value.baseType = coder.name;
|
|
102
|
+
value.name = coder.localName;
|
|
103
|
+
value.type = coder.type;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
} else {
|
|
107
|
+
try {
|
|
108
|
+
value = coder.decode(reader);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
// Cannot recover from this
|
|
111
|
+
if (error.code === Logger.errors.BUFFER_OVERRUN) { throw error; }
|
|
112
|
+
value = error;
|
|
113
|
+
value.baseType = coder.name;
|
|
114
|
+
value.name = coder.localName;
|
|
115
|
+
value.type = coder.type;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (value != undefined) {
|
|
120
|
+
values.push(value);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// We only output named properties for uniquely named coders
|
|
125
|
+
const uniqueNames = coders.reduce((accum, coder) => {
|
|
126
|
+
const name = coder.localName;
|
|
127
|
+
if (name) {
|
|
128
|
+
if (!accum[name]) { accum[name] = 0; }
|
|
129
|
+
accum[name]++;
|
|
130
|
+
}
|
|
131
|
+
return accum;
|
|
132
|
+
}, <{ [ name: string ]: number }>{ });
|
|
133
|
+
|
|
134
|
+
// Add any named parameters (i.e. tuples)
|
|
135
|
+
coders.forEach((coder: Coder, index: number) => {
|
|
136
|
+
let name = coder.localName;
|
|
137
|
+
if (!name || uniqueNames[name] !== 1) { return; }
|
|
138
|
+
|
|
139
|
+
if (name === "length") { name = "_length"; }
|
|
140
|
+
|
|
141
|
+
if (values[name] != null) { return; }
|
|
142
|
+
|
|
143
|
+
const value = values[index];
|
|
144
|
+
|
|
145
|
+
if (value instanceof Error) {
|
|
146
|
+
Object.defineProperty(values, name, {
|
|
147
|
+
enumerable: true,
|
|
148
|
+
get: () => { throw value; }
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
values[name] = value;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
for (let i = 0; i < values.length; i++) {
|
|
156
|
+
const value = values[i];
|
|
157
|
+
if (value instanceof Error) {
|
|
158
|
+
Object.defineProperty(values, i, {
|
|
159
|
+
enumerable: true,
|
|
160
|
+
get: () => { throw value; }
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return Object.freeze(values);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
export class ArrayCoder extends Coder {
|
|
170
|
+
readonly coder: Coder;
|
|
171
|
+
readonly length: number;
|
|
172
|
+
|
|
173
|
+
constructor(coder: Coder, length: number, localName: string) {
|
|
174
|
+
const type = (coder.type + "[" + (length >= 0 ? length: "") + "]");
|
|
175
|
+
const dynamic = (length === -1 || coder.dynamic);
|
|
176
|
+
super("array", type, localName, dynamic);
|
|
177
|
+
|
|
178
|
+
this.coder = coder;
|
|
179
|
+
this.length = length;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
defaultValue(): Array<any> {
|
|
183
|
+
// Verifies the child coder is valid (even if the array is dynamic or 0-length)
|
|
184
|
+
const defaultChild = this.coder.defaultValue();
|
|
185
|
+
|
|
186
|
+
const result: Array<any> = [];
|
|
187
|
+
for (let i = 0; i < this.length; i++) {
|
|
188
|
+
result.push(defaultChild);
|
|
189
|
+
}
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
encode(writer: Writer, value: Array<any>): number {
|
|
194
|
+
if (!Array.isArray(value)) {
|
|
195
|
+
this._throwError("expected array value", value);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let count = this.length;
|
|
199
|
+
|
|
200
|
+
if (count === -1) {
|
|
201
|
+
count = value.length;
|
|
202
|
+
writer.writeValue(value.length);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
logger.checkArgumentCount(value.length, count, "coder array" + (this.localName? (" "+ this.localName): ""));
|
|
206
|
+
|
|
207
|
+
let coders = [];
|
|
208
|
+
for (let i = 0; i < value.length; i++) { coders.push(this.coder); }
|
|
209
|
+
|
|
210
|
+
return pack(writer, coders, value);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
decode(reader: Reader): any {
|
|
214
|
+
let count = this.length;
|
|
215
|
+
if (count === -1) {
|
|
216
|
+
count = reader.readValue().toNumber();
|
|
217
|
+
|
|
218
|
+
// Check that there is *roughly* enough data to ensure
|
|
219
|
+
// stray random data is not being read as a length. Each
|
|
220
|
+
// slot requires at least 32 bytes for their value (or 32
|
|
221
|
+
// bytes as a link to the data). This could use a much
|
|
222
|
+
// tighter bound, but we are erroring on the side of safety.
|
|
223
|
+
if (count * 32 > reader._data.length) {
|
|
224
|
+
logger.throwError("insufficient data length", Logger.errors.BUFFER_OVERRUN, {
|
|
225
|
+
length: reader._data.length,
|
|
226
|
+
count: count
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
let coders = [];
|
|
231
|
+
for (let i = 0; i < count; i++) { coders.push(new AnonymousCoder(this.coder)); }
|
|
232
|
+
|
|
233
|
+
return reader.coerce(this.name, unpack(reader, coders));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Coder, Reader, Writer } from "./abstract-coder";
|
|
4
|
+
|
|
5
|
+
export class BooleanCoder extends Coder {
|
|
6
|
+
|
|
7
|
+
constructor(localName: string) {
|
|
8
|
+
super("bool", "bool", localName, false);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
defaultValue(): boolean {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
encode(writer: Writer, value: boolean): number {
|
|
16
|
+
return writer.writeValue(value ? 1: 0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
decode(reader: Reader): any {
|
|
20
|
+
return reader.coerce(this.type, !reader.readValue().isZero());
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|