Skip to content

ERC-2612

ERC2612

ERC2612 is an extension standard for enhancing ERC20 token functionality. Specifically, ERC2612 introduces signed authorization (permit) functionality for ERC20 tokens, enabling users to authorize token transfers through signatures without needing to pay Ethereum transaction fees.

Off-chain signature authorization not only simplifies the user experience but also reduces the number of on-chain transactions, thereby lowering transaction costs.

ERC2612 Interface

pragma solidity ^0.8.0;

interface IERC2612 {
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    function nonces(address owner) external view returns (uint256);
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

Methods

  1. permit: Authorizes spender to spend value tokens from owner's token account via signature.
  2. nonces: Returns the nonce value for a specific address, used to prevent replay attacks.
  3. DOMAIN_SEPARATOR: Returns the EIP-712 domain separator used for signing messages.

ERC2612 Contract Example

Below is an example contract implementing ERC2612:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";

contract UpChainToken is ERC20, ERC20Permit {
    constructor() ERC20("UpChainToken", "MTK") ERC20Permit("UpChainToken") {
        _mint(msg.sender, 1000000 * 10 ** decimals());
    }
}

In this example, we inherit the ERC20 and ERC20Permit contracts from the OpenZeppelin library to implement ERC2612 functionality.

Usage Example

Assuming we have deployed the above UpChainToken contract, below demonstrates how to interact with the contract via ethers.js (v5) to implement the permit functionality:

Generating a Signature

First, generate an offline signature:

const { ethers } = require("ethers");
const provider = new ethers.providers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID");
const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider);

const contractAddress = "0xYourContractAddress";
const contractABI = [
    "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external",
    "function nonces(address owner) external view returns (uint256)",
    "function DOMAIN_SEPARATOR() external view returns (bytes32)"
];
const contract = new ethers.Contract(contractAddress, contractABI, wallet);

async function createPermitSignature(owner, spender, value, deadline) {
    const nonce = await contract.nonces(owner);
    const domainSeparator = await contract.DOMAIN_SEPARATOR();

    const permitData = {
        owner: owner,
        spender: spender,
        value: value,
        nonce: nonce,
        deadline: deadline
    };

    const types = {
        Permit: [
            { name: "owner", type: "address" },
            { name: "spender", type: "address" },
            { name: "value", type: "uint256" },
            { name: "nonce", type: "uint256" },
            { name: "deadline", type: "uint256" }
        ]
    };

    const signature = await wallet._signTypedData(
        { name: "UpChainToken", version: "1", chainId: 1, verifyingContract: contractAddress },
        types,
        permitData
    );

    return ethers.utils.splitSignature(signature);
}

Executing the permit Method

Use the generated signature to call the permit method on-chain:

async function permit(owner, spender, value, deadline, v, r, s) {
    const tx = await contract.permit(owner, spender, value, deadline, v, r, s);
    await tx.wait();
    console.log("Permit transaction submitted: ", tx.hash);
}

const owner = wallet.address;
const spender = "0xSpenderAddress";
const value = ethers.utils.parseUnits("100", 18);
const deadline = Math.floor(Date.now() / 1000) + 60 * 60; // 1 hour from now

const { v, r, s } = await createPermitSignature(owner, spender, value, deadline);
await permit(owner, spender, value, deadline, v, r, s);

Summary

The ERC2612 standard, by introducing the permit method, enhances ERC20 token functionality, enabling users to perform authorization operations through offline signatures, thereby simplifying the user experience and reducing the number of on-chain transactions. The above contract example demonstrates how to implement and use ERC2612 functionality.