从零开始的OpenZeppelin学习 2| ERC20-permit、erc20pausable

1. ERC20-permit

ERC20-permit 是 ERC20 代币标准的一个扩展,正式定义在 EIP-2612 中。它允许代币持有者通过链下签名 的方式授权他人使用自己的代币,而无需发送链上的 approve 交易。这一机制极大地改善了用户体验,特别是在 DeFi 等需要频繁授权的场景中。

传统 ERC20 的问题

在标准 ERC20 中,如果 Alice 想授权 Bob 转移她的代币(例如在 Uniswap 上交易),她必须:

  1. 发送一笔 approve 交易,花费 gas。
  2. 等待交易确认。
  3. 然后 Bob 才能调用 transferFrom 转走代币。

这个过程繁琐且需要用户持有 ETH 支付 gas,对于新用户尤其不友好。

Permit 的工作原理

Permit 引入了 permit 函数,该函数接受以下参数:

  • owner:代币持有者。
  • spender:被授权者。
  • value:授权额度。
  • deadline:签名过期时间。
  • v, r, s:由 owner 使用其私钥对授权信息进行签名的结果。

任何地址(通常是 relayer 或合约)都可以调用 permit,传入签名和授权信息。合约会验证签名是否有效且未过期,然后直接设置 ownerspender 的授权额度。这样,授权步骤就和后续的 transferFrom 可以在同一笔交易中完成,甚至可以将授权完全外包给第三方(元交易)。

主要优点

  • Gas 效率 :用户只需签名,无需支付 gas(由调用 permit 的人支付)。
  • 单笔交易 :可以将 permittransferFrom 合并调用,实现"授权并转账"的一步操作。
  • 元交易支持:应用可以代用户提交交易,实现无 gas 体验。
  • 安全 :通过 noncedeadline 防止签名重放。

实现细节

要实现 ERC20-permit,代币合约需要:

  1. 包含 permit 函数,实现签名验证逻辑。
  2. 维护每个地址的 nonces,防止重放攻击。
  3. 计算 DOMAIN_SEPARATOR(根据 EIP-712 标准),确保签名在不同合约或链之间不会混淆。

核心数据结构(EIP-712 类型哈希)

solidity 复制代码
bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

简化的 permit 实现(基于 OpenZeppelin)

solidity 复制代码
function permit(
    address owner,
    address spender,
    uint256 value,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
) external {
    require(block.timestamp <= deadline, "Permit: expired deadline");
    bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
    bytes32 hash = _hashTypedDataV4(structHash);
    address signer = ECDSA.recover(hash, v, r, s);
    require(signer == owner, "Permit: invalid signature");
    _approve(owner, spender, value);
}

与 OpenZeppelin 集成

OpenZeppelin 提供了 ERC20Permit 合约,只需继承即可快速获得 permit 功能,同时兼容 EIP-2612。

典型使用场景

  • 去中心化交易所:用户只需签名一次即可完成代币兑换。
  • 流动性挖矿:用户签名授权,合约自动提取质押代币。
  • 批量空投:项目方可以使用 permit 让用户授权后一次转走多份代币。
  • 钱包/浏览器扩展:如 MetaMask 已支持 EIP-2612 签名,可直接生成 permit 签名。

注意事项

  • 签名必须包含 deadline,以防止签名被无限期使用。
  • 必须维护 nonce,每个签名只能使用一次。
  • 验证签名时需根据 EIP-712 构造正确的哈希,包括 DOMAIN_SEPARATOR
  • 调用 permit 的地址(可能是攻击者)无法改变授权额度,只能按照签名执行。

总结

ERC20-permit 通过引入链下签名机制,将授权与代币转移解耦,让用户能更灵活地管理资产,同时降低链上交互成本。它已成为现代 DeFi 协议的事实标准,并被广泛采用(如 USDC、DAI、UNI 等代币均已支持)。

2. erc20pausable

ERC20Pausable 是 ERC20 代币标准的一个扩展,通常由 OpenZeppelin 的合约库实现。它允许合约拥有者在紧急情况下暂停 所有代币的转移操作(包括 transfertransferFrommintburn 等),从而为合约提供了一层安全熔断机制。

核心工作原理

  • 状态变量 :合约维护一个布尔值 paused,表示当前是否暂停。
  • 修饰符 :使用 whenNotPaused 修饰符检查 paused 是否为 false,若为 true 则交易回滚。
  • 控制函数 :只有具有特定角色(通常是合约拥有者)的地址可以调用 pause()unpause() 来切换暂停状态。
  • 事件 :暂停或取消暂停时会触发 PausedUnpaused 事件,便于链下监控。
solidity 复制代码
// OpenZeppelin 的简化示例
contract ERC20Pausable is ERC20, Pausable {
    function pause() public onlyOwner {
        _pause();
    }

    function unpause() public onlyOwner {
        _unpause();
    }

    function _beforeTokenTransfer(address from, address to, uint256 amount)
        internal
        whenNotPaused
        override
    {
        super._beforeTokenTransfer(from, to, amount);
    }
}

主要应用场景

  1. 安全应急
    当发现合约漏洞或遭遇黑客攻击时,拥有者可立即暂停所有转账,防止资金进一步流失,为修复争取时间。
  2. 监管合规
    在需要遵守法规的场景下(如证券型代币),可在特定时期暂停交易,配合法律要求。
  3. 升级过渡
    在合约升级期间,暂停代币转移可以避免状态不一致或数据损坏。
  4. 市场极端情况
    例如稳定币脱钩时,项目方可能临时暂停交易以稳定市场(需谨慎使用)。

实现方式

  • 使用 OpenZeppelin 库 :最简单的方式是继承 ERC20Pausable 合约(它本身继承自 ERC20Pausable),并添加自己的逻辑。
  • 自定义实现:可以在 ERC20 的基础上手动添加暂停逻辑,但需注意安全性和权限控制,建议使用经过审计的标准库。

注意事项

  • 中心化风险:暂停权限通常由单一地址或多签控制,若私钥泄露或被恶意使用,可能导致资金被锁或滥用。通常建议使用多签钱包或时间锁来管理权限。
  • 功能影响:暂停不仅阻止用户转账,也可能影响依赖代币流动性的其他合约(如 DEX、借贷协议),需提前告知用户。
  • 透明性:暂停操作应通过事件公开,并在前端或社群中及时公告。
  • 不可逆性 :一旦暂停,用户无法主动解冻,必须依赖拥有者调用 unpause,因此需要设计合理的恢复机制。

总结

ERC20Pausable 为代币合约增加了紧急暂停的能力,是一种简单有效的安全措施。虽然它引入了中心化控制点,但在 DeFi 和复杂应用中已成为常见的安全模块,帮助项目方在极端情况下保护用户资产。

相关推荐
zyb11475824332 小时前
Redis的学习
数据库·redis·学习
小白自救计划2 小时前
【计算机视觉】学习历程
人工智能·学习·计算机视觉
怪侠_岭南一只猿3 小时前
爬虫阶段一实战练习题:爬取豆瓣电影 Top250 复盘
css·经验分享·爬虫·python·学习·正则表达式
ADHD多动联盟3 小时前
ADHD注意力缺陷是什么?主要有儿童ADHD和多动症运动干预吗?
学习·学习方法·玩游戏
weixin_443478513 小时前
flutter组件学习之Stack 组件详解
学习·flutter
梦里1米83 小时前
大模型的使用和Prompt-Tuning学习笔记
笔记·学习·prompt
小雨凉如水4 小时前
flutter 基础组件学习
学习·flutter
云边散步4 小时前
godot2D游戏教程系列二(11)
笔记·学习·游戏·游戏开发
试试勇气4 小时前
Linux学习笔记(十六)--进程信号
linux·笔记·学习