EIP712 Signature Based Workflow: Implementing Off-chain Transactions

Dharmveer Bharti
5 min readMay 3, 2023

--

As a web3 developer, you might have come across terms like gas-less transactions, off-chain transactions. A popular example is the way OpenSea marketplace works.

Hi! this article is a continuation of my previous article which contains an abstract explanation of off-chain transactions. This article contains a small demonstration of off-chain transaction based workflow (or signature based workflow). But before starting any of it, let’s have an overview of a fixed price sale listing on OpenSea:

Diagram depicting fixed price sale workflow at Opensea
Sale listing workflow on Opensea

Notice that the seller doesn’t have to execute an on-chain transaction for listing their NFT for sale. So, listing on OpenSea is an off-chain operation (one-time on-chain transaction may be required for approving the collection to Opensea’s sale contract).

Demonstration — Signature based workflow

Once you’ve understood the above diagram, we can continue to our example which practically implements signature verification and execution.

For the sake of simplicity, we’ll take a smart contract which contains a structure with two fields: value and setter. value is an integer which can be set by any blockchain account, i.e. setter.

Num structure

Find the smart contract code here: https://github.com/d-Bharti001/signature-flow-tutorial/blob/master/contracts/SigFlow.sol

Now, the catch is that the setter should not execute any on-chain transaction; only the contract owner can call a function (setNum) to set the value on behalf of setter.

setNum function
setNum function

For this, the setter needs to sign an Order (also a structure defined in the contract). The contract owner can then take this Order data and generated signature and call the setNum function with these parameters, which would update the structure num.

Order structure defined in the smart contract
Order structure

Clone the project and launch local Truffle blockchain and frontend (steps provided in README): https://github.com/d-Bharti001/signature-flow-tutorial. We’ll be using Metamask wallet for signing Order data.

Blockchain and smart contract

We’ll be interacting with the SigFlow contract through Truffle developer console.

truffle developer console
truffle developer console and interacting with SigFlow contract instance

Frontend

Run npm start inside client folder, then launch the frontend interface in a browser with Metamask extension (or any other injected provider, e.g. Coinbase wallet). Click the button on the top of the page to connect the webpage with your wallet account.

Enter a value in the input field and click Sign message button. Metamask would pop up with a signature request.

Metamask signature request
Metamask signature request

After signing the Order, result is displayed in the frontend only:

Result with signature after signing the message
Signature result

Copy-paste the data obtained and call smart contract’s setNum function with the owner account (i.e. accounts[0] in Truffle):

calling setNum function
calling setNum function

Transaction succeeded. Now you can check the updated value of num:

Updated value of num
Updated num

It’s a success! congratulations. This way, value is updated to 256 by the account 0xBCE14… without the need for that account to spend on any gas fee.

It’s the same way that an OpenSea user doesn’t need to spend on any gas for listing their NFTs for sale.

It’s also the same way lazy minting works. You just have to sign a message containing token id and your address. When someone buys your NFT, a new token is minted in the same transaction with the token id set by you and your address as the first owner of the token. Find more details here: https://nftschool.dev/tutorial/lazy-minting/

Safety features

Validity period

In the Order to be signed, there is a field validPeriod. The function setNum checks if the current block time has exceeded validPeriod (line 77). If that happens, we can say that the Order has expired, and the transaction is rejected.

valid period check in the smart contract
valid period check

One-time execution of signed message

To prevent a signed message from being processed multiple times, we store which messages have already been processed in a mapping in the contract. After validating the signature, we include this check (line 71).

executed order check in the smart contract
Already executed order check

On-chain cancelation of signed message

After a message is signed, simply removing the message and signature from off-chain database won’t work because they might have been exposed earlier (before deletion) and so they can be passed to the contract for execution. So there is an on-chain function to cancel an Order, which can only be called by the account which originally signed the Order.

Cancel order function
Cancel order

Domain information

In addition to the Order data (value, setter, validPeriod), the message to be signed also contains the domain information which includes these fields:

  • name
  • version
  • chain id
  • verifying contract address

This way, it’s made sure that the signature would work only with the intended contract which is deployed to the intended chain. Name and version add to the security.

For more information about EIP712 signatures from a mathematical point of view, visit EIP-712: Typed structured data hashing and signing.

External links

Comment down if you need any help about message signing and verification. If required, I may write another article on how these messages are structured for signing, what is domain separator and order hash, etc.

--

--