欢迎光临
我们一直在努力

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

1. ERC20-permit

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

传统 ERC20 的问题

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

  • 发送一笔 approve 交易,花费 gas。
  • 等待交易确认。
  • 然后 Bob 才能调用 transferFrom 转走代币。
  • 这个过程繁琐且需要用户持有 ETH 支付 gas,对于新用户尤其不友好。

    Permit 的工作原理

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

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

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

    主要优点

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

    实现细节

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

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

    bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    简化的 permit 实现(基于 OpenZeppelin)

    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 的合约库实现。它允许合约拥有者在紧急情况下暂停所有代币的转移操作(包括 transfer、transferFrom、mint、burn 等),从而为合约提供了一层安全熔断机制。

    核心工作原理

    • 状态变量:合约维护一个布尔值 paused,表示当前是否暂停。
    • 修饰符:使用 whenNotPaused 修饰符检查 paused 是否为 false,若为 true 则交易回滚。
    • 控制函数:只有具有特定角色(通常是合约拥有者)的地址可以调用 pause() 和 unpause() 来切换暂停状态。
    • 事件:暂停或取消暂停时会触发 Paused 和 Unpaused 事件,便于链下监控。

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

    主要应用场景

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

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

    注意事项

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

    总结

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

    赞(0)
    未经允许不得转载:171主机测评 » 从零开始的OpenZeppelin学习 2| ERC20-permit、erc20pausable
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址