Skip to content

Commit f52ebf7

Browse files
committed
Fix encoding of array in signTypedData_v4
Currently the behavior of signTypedData_v4 is not according to https://eips.ethereum.org/EIPS/eip-712 when it comes to encoding arrays. The eip states: "The array values are encoded as the keccak256 hash of the concatenated encodeData of their contents". The behavior instead was to encode array values as the keccak256 of the concatenated keccak256 of the values. This worked well for primary types, but not for struct, as encodeData per spec is: "The encoding of a struct instance is enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ) , i.e. the concatenation of the encoded member values in the order that they appear in the type. Each encoded member value is exactly 32-byte long.". Instead, we were using basically `hashStruct` instead of `encodeData`
1 parent 73ace33 commit f52ebf7

2 files changed

Lines changed: 16 additions & 3 deletions

File tree

src/__snapshots__/sign-typed-data.test.ts.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ exports[`TypedDataUtils.encodeData V4 should encode data when called unbound 1`]
320320

321321
exports[`TypedDataUtils.encodeData V4 should encode data when given extraneous types 1`] = `"cddf41b07426e1a761f3da57e35474ae3deaa5b596306531f651c6dc1321e4fd6cdba77591a790691c694fa0be937f835b8a589095e427022aa1035e579ee596"`;
322322

323-
exports[`TypedDataUtils.encodeData V4 should encode data with a custom data type array 1`] = `"077b2e5169bfc57ed1b6acf858d27ae9b8311db1ccf71df99dfcf9efe8eec43856cacfdc07c6f697bc1bc978cf38559d5c729ed1cd1177e047df929e19dc2a2e8548546251a0cc6d0005e1a792e00f85feed5056e580102ed1afa615f87bb130b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8"`;
323+
exports[`TypedDataUtils.encodeData V4 should encode data with a custom data type array 1`] = `"077b2e5169bfc57ed1b6acf858d27ae9b8311db1ccf71df99dfcf9efe8eec43856cacfdc07c6f697bc1bc978cf38559d5c729ed1cd1177e047df929e19dc2a2ebfe1302b079804af4cd9e10539928375539a1724a1cf42d42b2918298c371eafb5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8"`;
324324

325325
exports[`TypedDataUtils.encodeData V4 should encode data with a custom type property set to null 1`] = `"a0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c80000000000000000000000000000000000000000000000000000000000000000b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8"`;
326326

@@ -628,7 +628,7 @@ exports[`TypedDataUtils.hashStruct V4 should hash data when called unbound 1`] =
628628

629629
exports[`TypedDataUtils.hashStruct V4 should hash data when given extraneous types 1`] = `"15d2c54cdaa22a6a3a8dbd89086b2ffcf0853857db9bcf1541765a8f769a63ba"`;
630630

631-
exports[`TypedDataUtils.hashStruct V4 should hash data with a custom data type array 1`] = `"ac26cc7aa2cb9a8a445fae4e48b33f978b558da9b16e26381c53814d3317f541"`;
631+
exports[`TypedDataUtils.hashStruct V4 should hash data with a custom data type array 1`] = `"e4ef223c74f2085fbac1f734aa89e00985dc1dfa72419eedc9e33b52a6d15a2b"`;
632632

633633
exports[`TypedDataUtils.hashStruct V4 should hash data with a custom type property set to null 1`] = `"fdecfae63c304f6fc7795188607e3838f5bf6798e47f147efdb2c71fcec1335e"`;
634634

@@ -1108,7 +1108,7 @@ exports[`signTypedData V4 should sign a typed message with only custom domain se
11081108

11091109
exports[`signTypedData V4 should sign data when given extraneous types 1`] = `"0x9809571f17ee150687932fb7b993f4437b05caf9c8e64818ab4356b33992f3796636c18e88ba9c6dc140f39f95d8ae5770ad2f17070af208690c82d33f3bc8701c"`;
11101110

1111-
exports[`signTypedData V4 should sign data with a custom data type array 1`] = `"0x17c484deba479e3e9f821a6d5defc0179ddf52195c0cd75895c936b3ab4d217c6ba7f1f164a3ac9701e1694a04a13f421fb67ec44cd316846326a98b0d5838a01c"`;
1111+
exports[`signTypedData V4 should sign data with a custom data type array 1`] = `"0x96cf12332272746492a9b9a56ff928e6ad2538d4d3faaaaeb7a691720c94e68e5e14d56fa847deff4e8ccbe11e0aa72066f1330cb22045199c33ce67a9cef5cf1c"`;
11121112

11131113
exports[`signTypedData V4 should sign data with a custom type property set to null 1`] = `"0xc24daccba3391e6f6c3c07c8d62b8c83f7bb4e370613cb2d9ece63a4897d2d044e77e613091a496d8f337854c09ee139e4bca87839d3562ba16a850daa60185a1c"`;
11141114

src/sign-typed-data.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ function encodeField(
173173
version: SignTypedDataVersion.V3 | SignTypedDataVersion.V4,
174174
): [type: string, value: any] {
175175
validateVersion(version, [SignTypedDataVersion.V3, SignTypedDataVersion.V4]);
176+
console.log('ENCODING', types, name, type, value);
176177

177178
if (types[type] !== undefined) {
178179
return [
@@ -206,6 +207,18 @@ function encodeField(
206207
);
207208
}
208209
const parsedType = type.slice(0, type.lastIndexOf('['));
210+
211+
// If it's a struct, we concatenate their encodedData and then take the keccak256
212+
// as per "The array values are encoded as the keccak256 hash of the concatenated encodeData of their contents"
213+
if (types[parsedType] !== undefined) {
214+
const typeValuePairs = value.map((item) =>
215+
encodeData(parsedType, item, types, version),
216+
);
217+
console.log(typeValuePairs);
218+
return ['bytes32', keccak(Buffer.concat(typeValuePairs))];
219+
}
220+
221+
// Otherwise we use encodeField as it's not a struct
209222
const typeValuePairs = value.map((item) =>
210223
encodeField(types, name, parsedType, item, version),
211224
);

0 commit comments

Comments
 (0)