ERC2612 是用于增强 ERC20 代币的功能的扩展标准,具体来说,ERC2612 为 ERC20 代币引入了签名授权(permit)功能,使得用户可以通过签名来授权代币转移,而不需要支付以太坊交易费。
通过链下签名授权不仅可简化用户体验,还减少了链上交易次数,从而降低了交易成本。
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);
}
spender
在 owner
的代币账户上支配 value
个代币。以下是一个实现 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 库中的 ERC20
和 ERC20Permit
合约,以实现 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
方法:
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 的功能。