This ERC provides a standard for signing multiple typed-data messages with a single signature by encoding them into a Merkle tree. This allows components to independently verify messages, without requiring full knowledge of the others. It provides a significant UX improvement by reducing the number of signature prompts to one, while preserving the security and flexibility of the EIP-712 standard.
This ERC also gives applications the flexibility to verify messages in isolation, or in aggregate. This opens up new verification modalities: for e.g, an application can require that message (x) is only valid when signed in combination message (y).
Motivation
As the ecosystem moves towards ETH-less transactions, users are often required to sign multiple off-chain messages in quick succession. Typically, a first signature is needed for a precise spend allowance (via Permit2, ERC-2612, etc.), followed by subsequent messages to direct the use of funds. This creates a frictional user experience as each signature requires a separate wallet interaction and creates confusion about what, in aggregate, is being approved.
Merging multiple messages into a single message: prevents independent verifiability. Each message cannot be verified without knowledge of the entire batch
Separate signature requests: creates friction in the user experience
This ERC has the following objectives:
Single Signature
A single signature should cover multiple messages
Isolated Verification
Messages should be independently verifiable without knowledge of others
Human-readable
Readability benefits of EIP-712 should be preserved. Giving wallets and users insight into what is being signed.
Specification
Overview
The composite signature scheme uses a Merkle tree to hash multiple typed-data data messages together under a single root. The user signs only the Merkle root. The process is described below.
Generating a Composite Signature
For a set of messages [m₁, m₂, ..., mₙ], encode each using EIP-712’s encode and compute its hash:
hashₙ = keccak256(encode(mₙ))
Use these message hashes as leaf nodes in a Merkle tree and compute a merkleRoot
Sign the merkle root.
signature = sign(merkleRoot)
Verification Process
To verify that an individual message mₓ was included in a composite signature:
The message is verified if and only if (1) and (2) succeed.
isVerified = isValidSignature && isValidProof
Specification of eth_signTypedData_v5 JSON RPC method.
This ERC adds a new method eth_signTypedData_v5 to Ethereum JSON-RPC. This method allows signing multiple typed data messages with a single signature using the specification described above. The signing account must be prior unlocked.
This method returns: the signature, merkle root, and an array of proofs (each corresponding to an input message).
Parameters
Address - Signing account
TypedData | TypedDataArray - A single TypedData object or Array of TypedData objects from EIP-712.
Returns
{signature:`0x${string}`;// Hex encoded 65 byte signature (same format as eth_sign)merkleRoot:`0x${string}`;// 32 byte Merkle root as hex stringproofs:Array<Array<`0x${string}`>>;// Array of Merkle proofs (one for each input message)}
The choice of using a Merkle tree to bundle messages provides the following additional benefits:
Efficient verification on-chain
_verifyMerkleProof has a runtime of O(log2(N)) where N is the number of messages that were signed.
Flexible Verification Modes
Applications can require combination of messages be signed together to enhance security.
N=1 backwards compatibility
Merkle signature for single message bundles are equal to eth_signTypedData_v4. Requiring no onchain changes.
Backwards Compatibility
When the number of message is one, eth_signTypedData_v5 produces the same signature as eth_signTypedData_v4 since merkleRoot == keccak256(encode(message)). This allows eth_signTypedData_v5 to be a drop-in replacement for eth_signTypedData_v4 with no changes to on-chain verification.
Reference Implementation
eth_signTypedData_v5
Reference implementation of eth_signTypedData_v5 can be found the assets directory.
Verifier
Solidity implementation of a onchain verifier can be found the assets directory.
This ERC focuses on generating composite messages and verifying their signatures. It does not contain mechanisms to prevent replays. Developers must ensure their applications can handle receiving the same message twice.
Partial Message Verification
During verification, care must be taken to ensure that both of these checks pass:
EIP-712 signature on the Merkle root is valid
Merkle proof is valid against the root
User Understanding
Wallets must communicate to users that they are signing multiple messages at once. Wallets must display of all message types before signing.
To ensure batch signature requests are digestible, it is recommended to limit the maximum number of messages to 10.
Merkle Tree Construction
Merkle tree should be constructed in a consistent manner.
The hashing function must be keccak256
To ensure predictable/consistent proof sizes, implementations must pad leaves with zero hashes to reach next power of two to ensure balance. Let n be the number of messages. Before constructing the tree, compute the smallest k such that 2^(k-1) < n ≤ 2^k. Insert zero hashes into the list of messages until list of messages is equal to 2^k.
To ensure an implicit verification path, pairs must be sorted lexicographically before constructing parent hash.