【openzeppelin学习系列】ERC20

openzeppelin学习系列文章

提示:本文是我在学习智能合约开发过程中的一些思考和总结。在这个复杂且不断发展的领域中,可能存在一些疏漏或不准确之处。我非常欢迎和鼓励大家提出意见和建议,让我们可以共同讨论、纠正错误,并提高我们对区块链技术和智能合约的理解与掌握。希望通过这种开放的交流,我们都能在这一领域得到成长和进步。



一、简介

ERC20 是一个基于以太坊的智能合约标准库,提供了一种创建新代币的标准方法。ERC20 代币合约可以让任何人在以太坊网络上创建可交易的代币,这些代币可以代表任何东西,如货币、积分等。

二、接口

interface IERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

三、其他合约

1. ERC20Burnable

功能:允许用户销毁(burn)他们的代币,从而减少总供应量。

使用方法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

contract MyToken is ERC20Burnable {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
        _mint(msg.sender, initialSupply);
    }
}

主要方法

  • burn(uint256 amount): 销毁调用者持有的指定数量的代币。
  • burnFrom(address account, uint256 amount): 从指定账户销毁指定数量的代币,前提是调用者拥有足够的授权。

2. ERC20Capped

功能:设定代币的最大供应量上限。

使用方法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

contract MyToken is ERC20Capped {
    constructor(uint256 initialSupply, uint256 cap) ERC20("MyToken", "MTK") ERC20Capped(cap) {
        _mint(msg.sender, initialSupply);
    }

    function mint(address to, uint256 amount) public {
        _mint(to, amount);
    }
}

注意_mint 方法需要在合约中定义,以便遵循 ERC20Capped 的上限限制。

3. ERC20Pausable

功能:允许管理员在紧急情况下暂停和恢复代币的所有传输操作。

使用方法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20Pausable, Ownable {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
        _mint(msg.sender, initialSupply);
    }

    function pause() public onlyOwner {
        _pause();
    }

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

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

主要方法

  • _pause(): 暂停所有代币转移操作。
  • _unpause(): 恢复所有代币转移操作。
  • _beforeTokenTransfer: 在每次代币转移前调用,检查是否已暂停。

4. ERC20Snapshot

功能:允许创建代币持有者的快照,并查询过去的余额。

使用方法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20Snapshot, Ownable {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
        _mint(msg.sender, initialSupply);
    }

    function snapshot() public onlyOwner {
        _snapshot();
    }
}

主要方法

  • _snapshot(): 创建一个新的快照,并返回快照 ID。
  • balanceOfAt(address account, uint256 snapshotId): 返回指定账户在指定快照时的余额。
  • totalSupplyAt(uint256 snapshotId): 返回在指定快照时的总供应量。

5. ERC20Permit

功能:允许持有者通过签名离线授权其他账户转移其代币。

使用方法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

contract MyToken is ERC20Permit {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") ERC20Permit("MyToken") {
        _mint(msg.sender, initialSupply);
    }

主要方法

  • permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s): 根据签名数据批准 spender 转移 owner 的代币。

6. ERC20Votes

功能:用于治理代币,通过快照记录历史投票权。

使用方法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

contract MyToken is ERC20Votes {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") ERC20Permit("MyToken") {
        _mint(msg.sender, initialSupply);
    }

    // 需要覆盖 _afterTokenTransfer, _mint, _burn 来处理投票权更新
    function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
        super._afterTokenTransfer(from, to, amount);
    }

    function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
        super._mint(to, amount);
    }

    function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
        super._burn(account, amount);
    }
}

主要方法

  • delegate(address delegatee): 委托投票权给 delegatee
  • getVotes(address account): 获取 account 当前的投票权。
  • getPastVotes(address account, uint256 blockNumber): 获取 account 在指定区块号时的投票权。

7.SafeERC20

功能SafeERC20 是一个库,为 ERC20 代币操作提供了安全的包装器。这些包装器可以确保操作不会失败,并且在某些情况下会处理代币合约未按预期返回值的问题。

使用方法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract MyContract {
    using SafeERC20 for IERC20;

    IERC20 private _token;

    constructor(IERC20 token) {
        _token = token;
    }

    function safeTransfer(address to, uint256 amount) public {
        _token.safeTransfer(to, amount);
    }

    function safeTransferFrom(address from, address to, uint256 amount) public {
        _token.safeTransferFrom(from, to, amount);
    }

    function safeApprove(address spender, uint256 amount) public {
        _token.safeApprove(spender, amount);
    }

    function safeIncreaseAllowance(address spender, uint256 addedValue) public {
        _token.safeIncreaseAllowance(spender, addedValue);
    }

    function safeDecreaseAllowance(address spender, uint256 subtractedValue) public {
        _token.safeDecreaseAllowance(spender, subtractedValue);
    }

主要方法

  • safeTransfer(IERC20 token, address to, uint256 amount): 安全转移代币。
  • safeTransferFrom(IERC20 token, address from, address to, uint256 amount): 从一个账户安全转移代币到另一个账户。
  • safeApprove(IERC20 token, address spender, uint256 amount): 安全批准代币。
  • safeIncreaseAllowance(IERC20 token, address spender, uint256 addedValue): 安全增加允许的代币数量。
  • safeDecreaseAllowance(IERC20 token, address spender, uint256 subtractedValue): 安全减少允许的代币数量。

8.TokenTimelock

功能TokenTimelock 是一个智能合约,用于锁定 ERC20 代币,直到指定的释放时间。

使用方法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/TokenTimelock.sol";

contract MyTokenTimelock {
    IERC20 private _token;
    TokenTimelock private _timelock;

    constructor(IERC20 token, address beneficiary, uint256 releaseTime) {
        _token = token;
        _timelock = new TokenTimelock(token, beneficiary, releaseTime);
    }

    function getTimelockAddress() public view returns (address) {
        return address(_timelock);
    }
}

主要方法

  • TokenTimelock(IERC20 token, address beneficiary, uint256 releaseTime): 构造函数,用于初始化代币、受益人和释放时间。
  • release(): 释放锁定的代币,必须在释放时间之后调用。

四、注意事项

  1. 版本匹配:确保合约版本与 OpenZeppelin 库版本一致,避免编译错误。
  2. 安全使用:使用 SafeERC20 来确保代币操作的安全性。
  3. 权限控制:使用 OwnableAccessControl 进行权限管理,确保只有授权用户可以执行特定操作。

五、提问:

与 USDT 等 ERC20 代币交互时,为什么建议使用 SafeERC20

  • 处理返回值不规范的合约:一些 ERC20 代币合约(如 USDT)在执行 transfer, transferFrom, approve 等操作时,不会返回布尔值(truefalse),而是直接返回交易结果。这种行为不符合 ERC20 标准,可能会导致在处理这些交易时出现问题。
  • 避免合约调用失败:如果直接调用这些函数,并假设它们会返回布尔值,那么在与不返回布尔值的代币合约交互时,可能会导致合约调用失败或产生意外的错误。
  • 增强安全性:使用 SafeERC20 提供的安全包装函数,可以确保在调用 ERC20 代币函数时,正确处理返回值,并在操作失败时抛出错误。这提高了合约的安全性和可靠性。

欢迎访问我的 GitHub 查看更多相关项目,或通过WeChat(ID: lk34041515)与我联系,共同探讨技术问题。

创作权保护

本文由 [Leon-Kay] 学习总结编写,水平有限,内容仅供参考,作为个人记录使用。若有疏漏,请不吝赐教。版权归作者所有,未经授权,禁止转载、摘编或以其他方式使用本文内容。如需合作或转载本文,请联系作者获得授权。

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值