EIP-712: Another leap toward widespread adoption of blockchain and decentralized applications (part 2)
Discover the benefits brought by EIP-712, how you can implement the new EIP-712 standards for your own blockchain applications, and how EIP-712 will increase the number of dApp and digital currency users.
EIP-712 is here to save the day from the possible nightmares of the traditional Ethereum signing method.
For better continuity, please read EIP-712: Another leap toward widespread adoption of blockchain and decentralized applications (part 1).
In this blog, we’ll go over the benefits of EIP-712 and how you can incorporate it into your blockchain-based applications. The same demo of a simple dApp that I created will be referenced throughout.
What is EIP-712?
EIP-712, or Ethereum Improvement Proposal 712, is a standard for hashing and signing typed structured data. It enables digital wallets to display what users are sending and what they’re receiving in a clear and readable format. That means no randomly generated 256-character hash that could convey any number of incorrect or harmful instructions.
So, when we use this standard, I can easily confirm that the values I’m signing are the same ones I agreed to before clicking on Sign Message (EIP 712). See the below screenshots.
With EIP-712, we can confirm the values we are signing: Yes, this is the address I want to send my tokens to. Yes, this is how many tokens I want to send.
EIP-712 is user-friendly and secure
Displaying the message in a format that mirrors what users agreed to in the first place is much more user-friendly than the old way, and it helps prevent users from falling victim to phishing attempts. These are not the only benefits of EIP-712.
For more ways in which EIP-712 improves transactions, let’s look at a portion of the smart contract code for the demo I created:
createMessageParams() {
return JSON.stringify({
types: {
EIP712Domain: [
{name: “name”, type:”string”},
{name: “version”, type: “string”},
{name: “chainId”, type: “uint256”},
{name: “verifyingContract”, type: “address”}
],
ForwardRequest: [
{name: “from”, type: “address”},
{name: “to”, type: “address”},
{name: “value”, type: “uint256”},
{name: “gas”, type: “uint256”},
{name: “nonce”, type: “uint256”}
]
As you can see, the From, To, Value, Gas, and Nonce fields are all there. You’ll also notice the EIP-712 Domain section.
This is also part of the EIP-712 standard.
So, why do we need to include the domain name in the smart contract code? To make sure the signature is not replayed. Signature replay is a kind of attack where a signature is used for multiple transactions. Think back to my signature stamp example. Since the website or server has your signature, they can use it again and again.
That’s why the EIP-712 standard forces blockchain developers to specify the domain name in the smart contract code.
Notice, too, that I’ve included the version as well as the verifying smart contract and its address. These additions further improve the security of blockchain transactions. As it’s nearly impossible that a project will have the same domain, version, and smart contract address, the addition of these values adds extra signature protection.
Lazy minting: another use case of EIP-712
Let’s go over another use case of EIP-712. This time from the point of view of an NFT creator.
Pretend I’m the owner of an NFT. It’s my original artwork of a toucan — with two cans of beans around its neck (I’m trying to compete with the Bored Ape NFTs, okay?).
So I go to an NFT marketplace to sell it. But what if, for some reason, it doesn’t sell? I don’t want to pay gas fees for minting it (publishing it on a blockchain) if it doesn’t even sell.
So what I’ll do is opt for lazy minting. Lazy minting is a feature made available by NFT marketplaces such as OpenZeppelin that allows for gas fees the NFT creator would ordinarily pay to be rolled into the transaction if the NFT is purchased. If you’ve sold on Ebay, then you’re aware that you pay no fees until your item sells. It’s similar to that.
The lazy minting process:
Step 1: I fill out the needed details of the NFT such as the name and attributes.
Step 2: This is where EIP-712 comes in. Instead of directly minting the NFT, I will be prompted to sign the metadata using the EIP-712 standard.
Step 3: The NFT marketplace then stores the signature output and sends the NFT metadata to the database of available NFTs.
- At this point, the Toucan with Two Cans NFT has been listed on the NFT marketplace, and I’ve incurred no gas fees.
Step 4: A buyer comes along (let’s call him Sam), realizes the beauty of my NFT, and smashes the Buy button.
Step 5: Behind the scenes, the marketplace will fetch the metadata and signature from the database.
Step 6: The marketplace will then use the metadata and the signature to get the address of the signer (which would be me). At this stage, if the returned signer is the same as the creator, the NFT will be minted.
- If the signer is somehow not the creator, we can assume that someone has changed the metadata, therefore making the NFT metadata invalid.
Step 7: Sam now owns the Toucan with Two Cans NFT, and I receive funds from the sale, minus the gas fees required to mint the NFT, as well as any other fees associated with the sale.
Lazy minting is just one use case of EIP-712.
How to implement EIP-712 for your blockchain-based application
In this section, I’ll concentrate on the main parts of the demo code that you, as a dApp developer, should be familiar with when implementing EIP-712 for message signing. If you’d like to try this demo yourself, I’ll provide the complete code at the end.
Angular app UI
EIP-712 Domain
First, we need to prepare the parameters for the message singing. As we’re using the EIP-712 standard, you can see that the EIP-712 Domain is there. It consists of four values. The name is the application name, the version is the application version, the chainID is the network ID on which the contract is deployed, and the verifying contract is the address of the contract that can verify whether the signature is valid.
createMessageParams() {
return JSON.stringify({
types: {
EIP712Domain: [
{name: “name”, type:”string”},
{name: “version”, type: “string”},
{name: “chainId”, type: “uint256”},
{name: “verifyingContract”, type: “address”}
],
The EIP-712 Domain is used to distinguish between different applications. It ensures each app has a unique name. If two app names are the same, then it falls back to the version. If the version is also the same, it looks at the chain ID. If the chain ID is the same, it’ll look at the verifying contract. And the address of the verifying contract will always be different, as different contracts have different structures of the message that they want to verify.
Forward Request
The forward request is the request or transaction that the relayer will execute. These five values will create one transfer transaction on the blockchain.
ForwardRequest: [
{name: “from”, type: “address”},
{name: “to”, type: “address”},
{name: “value”, type: “uint256”},
{name: “gas”, type: “uint256”},
{name: “nonce”, type: “uint256”}
]
Primary Type
The primary type is the primary object of the transaction. In our case it’s the forward request. By specifying the primary type as ForwardRequest, that will be what the user sees before signing the message. The user won’t see, for instance, the EIP712Domain values.
primaryType: “ForwardRequest”,
domain: {
name: “ForwardRequest”,
version: “1”,
chainId: 5,
verifyingContract: “0xA11ae4c40BE3883d08cA74Ce051C50BA9c700aa7”
Old Messaging Signing Method
To see what values you’d see in your MetaMask wallet with the old way of message signing, implement the following code:
async signMessageOld() {
if(typeof window.ethereum !== ‘undefined’) {
this.connectWallet().then(async (account) => {
const provider = window.web3.currentProvider;
const web3Provider = new Web3(provider);
const acc = await web3Provider.eth.getAccounts();
const param = this.createMessageParams();
provider.sendAsync({
method: “eth_sign”,
params: [acc[0], Web3.utils.keccak256(param.toString())],
from: acc[0]
}, async (err:any, res:any) => {
if(err) {
console.log(err)
}
console.log(“Signed Data – “, res.result)
});
});
}
}
EIP-712 Message Signing Standard
To see the EIP-712 messaging signing standard in action, implement the following code:
async signMessage() {
if(typeof window.ethereum !== ‘undefined’) {
this.connectWallet().then(async (account) => {
const provider = window.web3.currentProvider;
const web3Provider = new Web3(provider);
const acc = await web3Provider.eth.getAccounts();
const param = this.createMessageParams();
provider.sendAsync({
method: “eth_signTypedData_v4”,
params: [acc[0], param],
from: acc[0]
}, async (err:any, res:any) => {
if(err) {
console.log(err)
}
console.log(“Signed Data – “, res.result)
});
});
}
}
Get the complete code of the message signing demo dApp.
Conclusion
EIP-712 Benefits
EIP-712 is now available and supported by wallet providers, so dApp developers and smart contract developers should do away with the traditional signing method and implement this standard into their blockchain projects.
As mentioned, the EIP-712 standard makes decentralized transactions even more secure by preventing signature replay attacks and users agreeing to inaccurate values. This is in addition to making these transactions much easier to understand, enabling the user to see exactly what they are agreeing to when they sign messages.
With the benefits of EIP-712, I see this standard as being yet another leap toward widespread adoption of dApps and digital assets such as NFTs and cryptocurrency. Standards like EIP-712 are constantly improving blockchain adoption, so organizations should be prepared for a blockchain-filled future. Relevantz can get you there.
What Relevantz Can Do for You
With our vast knowledge and experience in blockchain and creating blockchain applications, such as DeFi applications for financial services organizations, Relevantz can help you explore decentralized app use cases and help build apps that will delight your traditional customers and crypto customers alike.
Want to serve your customers through blockchain-based applications?