分享百科

ERC2612

ERC2612

ERC2612 是用于增强 ERC20 代币的功能的扩展标准,具体来说,ERC2612ERC20 代币引入了签名授权(permit)功能,使得用户可以通过签名来授权代币转移,而不需要支付以太坊交易费。

通过链下签名授权不仅可简化用户体验,还减少了链上交易次数,从而降低了交易成本。

ERC2612 接口

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);
}

方法

  1. permit:通过签名授权 spenderowner 的代币账户上支配 value 个代币。
  2. nonces:返回特定地址的 nonce 值,用于防止重放攻击。
  3. DOMAIN_SEPARATOR:返回 EIP-712) 域分隔符,用于签名消息。

ERC2612 合约示例

以下是一个实现 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());
    }
}

在这个示例中,我们继承了 OpenZeppelin 库中的 ERC20ERC20Permit 合约,以实现 ERC2612 的功能。

使用示例

假设我们已经部署了上述 UpChainToken 合约,下面展示如何通过 ethers.js (v5) 与该合约进行交互,实现 permit 功能:

生成签名

首先,生成离线签名:

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);
}

执行 permit 方法

使用生成的签名在链上调用 permit 方法:

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);

总结

ERC2612 标准通过引入 permit 方法,增强了 ERC20 代币的功能,使得用户可以通过离线签名进行授权操作,从而简化用户体验并减少链上交易次数。上述合约示例展示了如何实现和使用 ERC2612 的功能。

✍️更新/纠错
登链社区