ERC-7920: Composite EIP-712 Signatures

Pull Request

Reference Implementation

1 Like

Very interesting proposal!

Can the data be composite at multiple layers, like [[x₁, x₂, ..., xₙ], [y₁, y₂, ..., yₙ]]?

And what’s the actual gas cost for a specific number of messages (n)?

1 Like

@hellohanchen thanks for the review!

Currently the messages must be flattened for signature: [x₁, x₂, ..., xₙ].

Gas cost for verification? Thats a good question I can run a test to find out. Runtime should be log(n) number of messages, if the tree is properly constructed.

Thanks for replying. I currently don’t have a great use case for this but this is interesting. Signature and validation work should become more flexible to prepare for the future use cases.

I do have some use cases that the signatures need to be layered like

sig1 = sign(message, key_1)
sig2 = sign(sig1, key_2)

And both signatures need to be verified.

Nice idea…

I noticed that signTypedData has a “backward compatibility”, to return a single result.
however, the sample solidity code only uses a merkle-proof, and doesn’t work with a single-entry proof returned by signTypedData_v5

1 Like

Hi @dror, thanks for reviewing.

The proposed schema for signedTypedData_v5 result is:

{
  signature: `0x${string}`; // Hex encoded 65 byte signature 
  merkleRoot: `0x${string}`; // 32 byte Merkle root as hex string
  proofs: Array<Array<`0x${string}`>>; // Array of Merkle proofs (one for each input message)
}

When N=1, the derivation path is an empty array as the sole message is root and signedTypedData_v5 returns:

{
  signature: `<< sign(keccak256(encode(m))) >>`;
  merkleRoot: `<< keccak256(encode(m)) >>`;
  proofs: [
   []
  ]
}

During verification

_verifyMerkleProof(N=1) = root == leaf

Simplifying check to

isVerified = isValidSignature && root == leaf

Here is a unit test for this case: ERCs/assets/erc-7920/test/solidity.test.ts at 5580d029fc43c7cffb1300788fe5db0bc7f386eb · ethereum/ERCs · GitHub

ok, so just to verify:
I can take a “standard” signTypedData result, and pass it as “root” with an empty merkle proof, and it will succeed. right ?

Hi @dror, here’s what verification would look like with a signedTypedData_v4 result

isVerified = isValidSignature(fullSig) && _verifyMerkleProof(messageHash, [], messageHash)

This would always evaluate as true

_verifyMerkleProof(messageHash, [], messageHash)

Leaving

isVerified = isValidSignature(fullSig)