1 | "use strict";
|
2 |
|
3 | import { BigNumber } from "@ethersproject/bignumber";
|
4 | import { arrayify, concat, hexlify, zeroPad } from "@ethersproject/bytes";
|
5 | import { keccak256 as hashKeccak256 } from "@ethersproject/keccak256";
|
6 | import { sha256 as hashSha256 } from "@ethersproject/sha2";
|
7 | import { toUtf8Bytes } from "@ethersproject/strings";
|
8 |
|
9 | const regexBytes = new RegExp("^bytes([0-9]+)$");
|
10 | const regexNumber = new RegExp("^(u?int)([0-9]*)$");
|
11 | const regexArray = new RegExp("^(.*)\\[([0-9]*)\\]$");
|
12 |
|
13 | const Zeros = "0000000000000000000000000000000000000000000000000000000000000000";
|
14 |
|
15 | import { Logger } from "@ethersproject/logger";
|
16 | import { version } from "./_version";
|
17 | const logger = new Logger(version);
|
18 |
|
19 |
|
20 | function _pack(type: string, value: any, isArray?: boolean): Uint8Array {
|
21 | switch(type) {
|
22 | case "address":
|
23 | if (isArray) { return zeroPad(value, 32); }
|
24 | return arrayify(value);
|
25 | case "string":
|
26 | return toUtf8Bytes(value);
|
27 | case "bytes":
|
28 | return arrayify(value);
|
29 | case "bool":
|
30 | value = (value ? "0x01": "0x00");
|
31 | if (isArray) { return zeroPad(value, 32); }
|
32 | return arrayify(value);
|
33 | }
|
34 |
|
35 | let match = type.match(regexNumber);
|
36 | if (match) {
|
37 |
|
38 | let size = parseInt(match[2] || "256")
|
39 |
|
40 | if ((match[2] && String(size) !== match[2]) || (size % 8 !== 0) || size === 0 || size > 256) {
|
41 | logger.throwArgumentError("invalid number type", "type", type)
|
42 | }
|
43 |
|
44 | if (isArray) { size = 256; }
|
45 |
|
46 | value = BigNumber.from(value).toTwos(size);
|
47 |
|
48 | return zeroPad(value, size / 8);
|
49 | }
|
50 |
|
51 | match = type.match(regexBytes);
|
52 | if (match) {
|
53 | const size = parseInt(match[1]);
|
54 |
|
55 | if (String(size) !== match[1] || size === 0 || size > 32) {
|
56 | logger.throwArgumentError("invalid bytes type", "type", type)
|
57 | }
|
58 | if (arrayify(value).byteLength !== size) {
|
59 | logger.throwArgumentError(`invalid value for ${ type }`, "value", value)
|
60 | }
|
61 | if (isArray) { return arrayify((value + Zeros).substring(0, 66)); }
|
62 | return value;
|
63 | }
|
64 |
|
65 | match = type.match(regexArray);
|
66 | if (match && Array.isArray(value)) {
|
67 | const baseType = match[1];
|
68 | const count = parseInt(match[2] || String(value.length));
|
69 | if (count != value.length) {
|
70 | logger.throwArgumentError(`invalid array length for ${ type }`, "value", value)
|
71 | }
|
72 | const result: Array<Uint8Array> = [];
|
73 | value.forEach(function(value) {
|
74 | result.push(_pack(baseType, value, true));
|
75 | });
|
76 | return concat(result);
|
77 | }
|
78 |
|
79 | return logger.throwArgumentError("invalid type", "type", type)
|
80 | }
|
81 |
|
82 |
|
83 |
|
84 | export function pack(types: ReadonlyArray<string>, values: ReadonlyArray<any>) {
|
85 | if (types.length != values.length) {
|
86 | logger.throwArgumentError("wrong number of values; expected ${ types.length }", "values", values)
|
87 | }
|
88 | const tight: Array<Uint8Array> = [];
|
89 | types.forEach(function(type, index) {
|
90 | tight.push(_pack(type, values[index]));
|
91 | });
|
92 | return hexlify(concat(tight));
|
93 | }
|
94 |
|
95 | export function keccak256(types: ReadonlyArray<string>, values: ReadonlyArray<any>) {
|
96 | return hashKeccak256(pack(types, values));
|
97 | }
|
98 |
|
99 | export function sha256(types: ReadonlyArray<string>, values: ReadonlyArray<any>) {
|
100 | return hashSha256(pack(types, values));
|
101 | }
|