Skip to content

Commit 9778b62

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 9f281f0 commit 9778b62

File tree

2 files changed

+17
-8
lines changed

2 files changed

+17
-8
lines changed

index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ const TypedDataUtils = {
117117

118118
if (type.lastIndexOf(']') === type.length - 1) {
119119
const parsedType = type.slice(0, type.lastIndexOf('['));
120+
121+
// If it's a struct, we concatenate their encodedData and then take the keccak256
122+
// as per "The array values are encoded as the keccak256 hash of the concatenated encodeData of their contents"
123+
if (types[parsedType] !== undefined) {
124+
const typeValuePairs = value.map((item) => this.encodeData(parsedType, item, types));
125+
return ['bytes32', ethUtil.sha3(Buffer.concat(typeValuePairs))];
126+
}
127+
128+
// Otherwise we use encodeField as it's not a struct
120129
const typeValuePairs = value.map((item) => encodeField(name, parsedType, item));
121130
return ['bytes32', ethUtil.sha3(ethAbi.rawEncode(
122131
typeValuePairs.map(([t]) => t),

test/index.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -657,15 +657,15 @@ test('signedTypeData_v4', (t) => {
657657
`0x${[
658658
'4bd8a9a2b93427bb184aca81e24beb30ffa3c747e2a33d4225ec08bf12e2e753',
659659
'9b4846dd48b866f0ac54d61b9b21a9e746f921cefa4ee94c4c0a1c49c774f67f',
660-
'ca322beec85be24e374d18d582a6f2997f75c54e7993ab5bc07404ce176ca7cd',
660+
'efa62530c7ae3a290f8a13a5fc20450bdb3a6af19d9d9d2542b5a94e631a9168',
661661
'b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8',
662662
].join('')}`);
663663
t.equal(ethUtil.bufferToHex(utils.hashStruct(typedData.primaryType, typedData.message, typedData.types)),
664-
'0xeb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8');
664+
'0x99b97a26b830a26d5ca27ced87ba4d73c6276a2b8315656882a771d6f98b01f3');
665665
t.equal(ethUtil.bufferToHex(utils.hashStruct('EIP712Domain', typedData.domain, typedData.types)),
666666
'0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f');
667667
t.equal(ethUtil.bufferToHex(utils.sign(typedData)),
668-
'0xa85c2e2b118698e88db68a8105b794a8cc7cec074e89ef991cb4f5f533819cc2');
668+
'0x66c6a7d830704b2b1c4a4245129d468a84449b50a6237f6fad4a38a0ace770a1');
669669

670670
const privateKey = ethUtil.sha3('cow');
671671

@@ -674,7 +674,7 @@ test('signedTypeData_v4', (t) => {
674674

675675
const sig = sigUtil.signTypedData_v4(privateKey, { data: typedData });
676676

677-
t.equal(sig, '0x65cbd956f2fae28a601bebc9b906cea0191744bd4c4247bcd27cd08f8eb6b71c78efdf7a31dc9abee78f492292721f362d296cf86b4538e07b51303b67f749061b');
677+
t.equal(sig, '0xf632e305033e23de75545fcdd0a481d83d9d41954e12c07004327cddf4e3c762757652b04d11dd022e0018e6160723f322d0b4bd9b41c87db93755405f5548391b');
678678
});
679679

680680

@@ -766,15 +766,15 @@ test('signedTypeData_v4', (t) => {
766766
`0x${[
767767
'4bd8a9a2b93427bb184aca81e24beb30ffa3c747e2a33d4225ec08bf12e2e753',
768768
'9b4846dd48b866f0ac54d61b9b21a9e746f921cefa4ee94c4c0a1c49c774f67f',
769-
'ca322beec85be24e374d18d582a6f2997f75c54e7993ab5bc07404ce176ca7cd',
769+
'efa62530c7ae3a290f8a13a5fc20450bdb3a6af19d9d9d2542b5a94e631a9168',
770770
'b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8',
771771
].join('')}`);
772772
t.equal(ethUtil.bufferToHex(utils.hashStruct(typedData.primaryType, typedData.message, typedData.types)),
773-
'0xeb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8');
773+
'0x99b97a26b830a26d5ca27ced87ba4d73c6276a2b8315656882a771d6f98b01f3');
774774
t.equal(ethUtil.bufferToHex(utils.hashStruct('EIP712Domain', typedData.domain, typedData.types)),
775775
'0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f');
776776
t.equal(ethUtil.bufferToHex(utils.sign(typedData)),
777-
'0xa85c2e2b118698e88db68a8105b794a8cc7cec074e89ef991cb4f5f533819cc2');
777+
'0x66c6a7d830704b2b1c4a4245129d468a84449b50a6237f6fad4a38a0ace770a1');
778778

779779
const privateKey = ethUtil.sha3('cow');
780780

@@ -783,7 +783,7 @@ test('signedTypeData_v4', (t) => {
783783

784784
const sig = sigUtil.signTypedData_v4(privateKey, { data: typedData });
785785

786-
t.equal(sig, '0x65cbd956f2fae28a601bebc9b906cea0191744bd4c4247bcd27cd08f8eb6b71c78efdf7a31dc9abee78f492292721f362d296cf86b4538e07b51303b67f749061b');
786+
t.equal(sig, '0xf632e305033e23de75545fcdd0a481d83d9d41954e12c07004327cddf4e3c762757652b04d11dd022e0018e6160723f322d0b4bd9b41c87db93755405f5548391b');
787787
});
788788

789789
test('signedTypeData_v4 with recursive types', (t) => {

0 commit comments

Comments
 (0)