EIP-712
EIP-712 proposes a standard for the hashing and signing of structured data, unlike previous raw data or EIP-191 signatures. The goal is to present the signed data in a readable format, so users know what they're signing.
Think of it like a well-structured contract with clearly stated terms, compared to a long and unreadable string of text. Plug
takes advantage of this standard to create human-readable, off-chain messages that users can understand and agree to by signing offchain.
WARNING
This gets quite technical. If you are struggling to understand and are not a developer, you can skip this section and come back to it later once you're ready. It is not required to understand the experience you will have with Plug
as an end-user.
The Problem
Without EIP-712, signing a message or transaction in the blockchain world could be pretty confusing. Imagine you're signing a digital document, but all you see is a string of random letters and numbers. You have no idea what you're agreeing to; it might as well be a foreign language. What does 0x1c8e9d3c
mean? What about 0x1c8e9d3c1c8e9d3c1c8e9d3c1c8e9d3c
? It's impossible to tell.
This is risky and unclear, akin to blindly signing a contract without reading it, simply because the document is too complicated to understand. In such a scenario, misunderstandings are bound to happen, and you might agree to terms you never intended to.
This is the problem that EIP-712 solves.
Structured Data and Signing
EIP-712 changes this by structuring the data in a human-readable format. It essentially translates that "foreign language" into clear and simple English (or any language you're comfortable with). This way, when you're about to sign something, you know exactly what you're getting into.
With this simple change, all consumers benefit through improved:
Transparency: You see in plain text what you're signing.
Security: A clear understanding of what you're signing.
Ease of Use: Requires no technical background to understand what you're signing.
Trust: You can trust that you're signing what you think you're signing.
How does it work?
To understand how EIP-712 works, let's go through a basic example. Imagine you want to make a simple agreement, something like "I will pay you 10 coins."
The Message Hash
In the pre-EIP-712 world, this agreement might look like a jumbled mess of characters when it's hashed (converted into a fixed-size string of characters). Let's pretend it turns into something like 0xabc123
. With EIP-712, the same agreement is structured and then hashed like:
hashStruct(types, "Agreement", {
agreement: "I will pay you 10 coins",
from: "Your address",
to: "Receiver's address",
});
2
3
4
5
In these few lines of code, we provide all the types used inside the message being signed, the name of the type, and the message we want to hash. This structured data is then hashed, but here's the magic: Because of the structure, the hash now represents the entire content in a unique but still understandable way. Let's say it turns into 0xdef456
.
Obviously, this still results in a hash and that is not very helpful. But, we can use this hash to parse and verify the human-readable message used to create it.
The Signature
Now, let's say you want to sign this agreement.
In the pre-EIP-712 world, you would sign the hash 0xabc123
. But with EIP-712, you sign the structured data hash 0xdef456
and inform the receiver that you're signing the agreement 'I will pay you 10 coins' that is hashed to 0xdef456
.
To verify and execute the action declared in the signature the receiver will get the signature and the structured data hash. They can then verify that the signature is valid by hashing the structured data and comparing it to the signature. If they hash the message and the contents match, the signature is valid. If they don't, the signature is invalid.
With EIP-712 a receiver has the capability to:
- Verify the
Signer
(signature). - Verify the contents of the hash.
Onchain Verification
The magic doesn't stop there: You can actually verify these typed contents directly onchain. While we have only focused on the message being human-readable up to this point, it is vital to fully comprehend that the message and signature are also machine-readable.
This means that smart contracts can be programmed to recognize the message and signature and check that it matches the initial agreement's parameters. This allows for strong, cryptographic proof that the transaction is what it claims to be, right there on the blockchain.
Domain Specification
The last piece of the puzzle is the domain
specification. I had skipped over this to keep things simple, but you're ready to understand it now. The domain
is a set of parameters that are used to specify the context of the message. It is a way to ensure that the message is only valid in a specific context.
For example, if you are signing a message to transfer tokens, you would want to ensure that the message is only valid for the token contract that you are transferring tokens from. In EIP-712, the domain
is declared like:
{
"name": "My Dapp",
"version": "1.0.0",
"chainId": 1,
"verifyingContract": "0xdeadbeef"
}
2
3
4
5
6
Without a domain
specification, you could sign a message to transfer tokens from one contract and then use that same signature to transfer tokens from another contract. Needless to say (we are saying it anyways), this is obviously not ideal!
To ensure that the message is only valid in a specific context, the domain
is hashed and then prepended to the message hash (your nested LivePair type). This ensures that the message is only valid in the context of the domain
that was specified and prevents the message from being used in a different context.
TIP
When using the Plug
library for your dApp this is automatically.