Fixed normalization and abstracted EIP-712 Array parsing (#4541).
This commit is contained in:
parent
92cb3e4c06
commit
8f99601df1
@ -68,6 +68,66 @@ describe("Tests Typed Data (EIP-712) aliases", function() {
|
|||||||
},
|
},
|
||||||
encoded: "0xa272ada5f88085e4cb18acdb87bd057a8cbfec249fee53de0149409080947cf500000000000000000000000000000000000000000000000000000000000000231c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
|
encoded: "0xa272ada5f88085e4cb18acdb87bd057a8cbfec249fee53de0149409080947cf500000000000000000000000000000000000000000000000000000000000000231c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "array-uint",
|
||||||
|
types: {
|
||||||
|
foo: [
|
||||||
|
{ name: "a", type: "uint256[]" },
|
||||||
|
{ name: "b", type: "string" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
typesAlias: {
|
||||||
|
foo: [
|
||||||
|
{ name: "a", type: "uint[]" },
|
||||||
|
{ name: "b", type: "string" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
a: [ 35, 36, 37 ],
|
||||||
|
b: "hello"
|
||||||
|
},
|
||||||
|
encoded: "0x1a961843d0002bdd66ec21afd6e4a5b0aac34a4b6112890378c6e3a38b752e0b0c22b846886e98aeffc1f1166d4b35868da4d4da853dcb3b2856cfc233fd10c81c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "array-int",
|
||||||
|
types: {
|
||||||
|
foo: [
|
||||||
|
{ name: "a", type: "int256[]" },
|
||||||
|
{ name: "b", type: "string" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
typesAlias: {
|
||||||
|
foo: [
|
||||||
|
{ name: "a", type: "int[]" },
|
||||||
|
{ name: "b", type: "string" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
a: [ 35, 36, 37 ],
|
||||||
|
b: "hello"
|
||||||
|
},
|
||||||
|
encoded: "0x0b89085a01a3b67d2231c6a136f9c8eea75d7d479a83a127356f8540ee15af010c22b846886e98aeffc1f1166d4b35868da4d4da853dcb3b2856cfc233fd10c81c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested-array-uint",
|
||||||
|
types: {
|
||||||
|
foo: [
|
||||||
|
{ name: "a", type: "uint256[][]" },
|
||||||
|
{ name: "b", type: "string" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
typesAlias: {
|
||||||
|
foo: [
|
||||||
|
{ name: "a", type: "uint[][]" },
|
||||||
|
{ name: "b", type: "string" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
a: [ [ 35, 36 ], [ 37 ] ],
|
||||||
|
b: "hello"
|
||||||
|
},
|
||||||
|
encoded: "0x5efa7c4b66979cf78fcc7c3e71cbfa04ec2c7529002642082bf20a91552c1147fa5ffe3a0504d850bc7c9eeda1cf960b596b73f4dc0272a6fa89dace08e320291c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const test of tests) {
|
for (const test of tests) {
|
||||||
|
@ -181,6 +181,35 @@ function encodeType(name: string, fields: Array<TypedDataField>): string {
|
|||||||
return `${ name }(${ fields.map(({ name, type }) => (type + " " + name)).join(",") })`;
|
return `${ name }(${ fields.map(({ name, type }) => (type + " " + name)).join(",") })`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ArrayResult = {
|
||||||
|
base: string; // The base type
|
||||||
|
index?: string; // the full Index (if any)
|
||||||
|
array?: { // The Array... (if index)
|
||||||
|
base: string; // ...base type (same as above)
|
||||||
|
prefix: string; // ...sans the final Index
|
||||||
|
count: number; // ...the final Index (-1 for dynamic)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// foo[][3] => { base: "foo", index: "[][3]", array: {
|
||||||
|
// base: "foo", prefix: "foo[]", count: 3 } }
|
||||||
|
function splitArray(type: string): ArrayResult {
|
||||||
|
const match = type.match(/^([^\x5b]*)((\x5b\d*\x5d)*)(\x5b(\d*)\x5d)$/);
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
base: match[1],
|
||||||
|
index: (match[2] + match[4]),
|
||||||
|
array: {
|
||||||
|
base: match[1],
|
||||||
|
prefix: (match[1] + match[2]),
|
||||||
|
count: (match[5] ? parseInt(match[5]): -1),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { base: type };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A **TypedDataEncode** prepares and encodes [[link-eip-712]] payloads
|
* A **TypedDataEncode** prepares and encodes [[link-eip-712]] payloads
|
||||||
* for signed typed data.
|
* for signed typed data.
|
||||||
@ -235,11 +264,14 @@ export class TypedDataEncoder {
|
|||||||
|
|
||||||
const types: Record<string, Array<TypedDataField>> = { };
|
const types: Record<string, Array<TypedDataField>> = { };
|
||||||
Object.keys(_types).forEach((type) => {
|
Object.keys(_types).forEach((type) => {
|
||||||
// Normalize int/uint unless they are a complex type themselves
|
|
||||||
types[type] = _types[type].map(({ name, type }) => {
|
types[type] = _types[type].map(({ name, type }) => {
|
||||||
if (type === "int" && !_types["int"]) { type = "int256"; }
|
|
||||||
if (type === "uint" && !_types["uint"]) { type = "uint256"; }
|
// Normalize the base type (unless name conflict)
|
||||||
return { name, type };
|
let { base, index } = splitArray(type);
|
||||||
|
if (base === "int" && !_types["int"]) { base = "int256"; }
|
||||||
|
if (base === "uint" && !_types["uint"]) { base = "uint256"; }
|
||||||
|
|
||||||
|
return { name, type: (base + (index || "")) };
|
||||||
});
|
});
|
||||||
|
|
||||||
links.set(type, new Set());
|
links.set(type, new Set());
|
||||||
@ -258,7 +290,7 @@ export class TypedDataEncoder {
|
|||||||
uniqueNames.add(field.name);
|
uniqueNames.add(field.name);
|
||||||
|
|
||||||
// Get the base type (drop any array specifiers)
|
// Get the base type (drop any array specifiers)
|
||||||
const baseType = (<any>(field.type.match(/^([^\x5b]*)(\x5b|$)/)))[1] || null;
|
const baseType = splitArray(field.type).base;
|
||||||
assertArgument(baseType !== name, `circular type reference to ${ JSON.stringify(baseType) }`, "types", _types);
|
assertArgument(baseType !== name, `circular type reference to ${ JSON.stringify(baseType) }`, "types", _types);
|
||||||
|
|
||||||
// Is this a base encoding type?
|
// Is this a base encoding type?
|
||||||
@ -331,12 +363,12 @@ export class TypedDataEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Array
|
// Array
|
||||||
const match = type.match(/^(.*)(\x5b(\d*)\x5d)$/);
|
const array = splitArray(type).array;
|
||||||
if (match) {
|
if (array) {
|
||||||
const subtype = match[1];
|
const subtype = array.prefix;
|
||||||
const subEncoder = this.getEncoder(subtype);
|
const subEncoder = this.getEncoder(subtype);
|
||||||
return (value: Array<any>) => {
|
return (value: Array<any>) => {
|
||||||
assertArgument(!match[3] || parseInt(match[3]) === value.length, `array length mismatch; expected length ${ parseInt(match[3]) }`, "value", value);
|
assertArgument(array.count === -1 || array.count === value.length, `array length mismatch; expected length ${ array.count }`, "value", value);
|
||||||
|
|
||||||
let result = value.map(subEncoder);
|
let result = value.map(subEncoder);
|
||||||
if (this.#fullTypes.has(subtype)) {
|
if (this.#fullTypes.has(subtype)) {
|
||||||
@ -413,10 +445,10 @@ export class TypedDataEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Array
|
// Array
|
||||||
const match = type.match(/^(.*)(\x5b(\d*)\x5d)$/);
|
const array = splitArray(type).array;
|
||||||
if (match) {
|
if (array) {
|
||||||
assertArgument(!match[3] || parseInt(match[3]) === value.length, `array length mismatch; expected length ${ parseInt(match[3]) }`, "value", value);
|
assertArgument(array.count === -1 || array.count === value.length, `array length mismatch; expected length ${ array.count }`, "value", value);
|
||||||
return value.map((v: any) => this._visit(match[1], v, callback));
|
return value.map((v: any) => this._visit(array.prefix, v, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct
|
// Struct
|
||||||
|
Loading…
Reference in New Issue
Block a user