Post

W3-1 ERC20、ERC777、ERC721协议介绍

W3-1 ERC20、ERC777、ERC721协议介绍

ERC-20

什么是 ERC20 Token

  • https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
  • EIP:Ethereum Improvement Proposals(以太坊改进提案)
  • OpenZeppelin实现

EIP分类

  • image1

ERC20标准

  • ERC20标准包含哪些内容
    • 定义统⼀的函数名:名称、发⾏量、转账函数、转账事件等
    • 以便交易所、钱包进⾏集成
  • 所有实现了这些函数的合约都是 ERC20 Token
  • ERC20 可以表示任何同质的可以交易的内容:
    • 货币、股票、积分、债券、利息…
    • 可以⽤数量来表示的内容 基本上可以ERC20 表示

接口实现

  • 标准包含的接口
interface IERC20 {
    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address to, uint256 amount) external returns (bool);

    function allowance(address owner, address spender) external view returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}
  • OpenZeppelin实现
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract LBCToken is ERC20 {
    constructor() ERC20("LBC Token", "LBC") {
        _mint(msg.sender, 1000 * 10 ** 18);
    }
}

授权

  • 分两步:
    • ⽤户 A 调⽤ ERC20 的 Approve(合约 B, 数量);
    • ⽤户 A 调⽤ 合约 B 的 deposite(); 完成存款
pragma solidity ^0.8.0;

contract B {
    mapping(address => uint) private deposited;

    function deposite(uint amount) {
        IERC20.transferFrom(msg.sender, address(this), amount);
        deposited[msg.sender] += amount;
    }
}

ERC-777

  • 相关资料
    • https://learnblockchain.cn/2019/09/27/erc777
  • ERC20 问题
    • 转账⽆法携带额外的信息。
    • 没有转账回调:
    • 依靠:授权、授权、授权
    • 误⼊合约被锁死。
  • ERC777:
    • ERC777:send(dest, value, data) • ERC777 通过全局注册表( ERC1820)的注册监听回调
    • 即便是普通地址也可以实现回调
    • 误⼊合约被锁死。
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC777/ERC777.sol";

contract GLDToken is ERC777 {
    constructor(uint256 initialSupply, address[] memory defaultOperators)
    ERC777("Gold", "GLD", defaultOperators)
    {
        _mint(msg.sender, initialSupply, "", "");
    }
}

ERC20-Permit(EIP2612)

  • 线下签名授权
    • (授权)可以在线下签名进⾏,签名信息可以在执⾏接收转账交易时提交到链上,让授权和转账在⼀笔交易⾥完成。
    • 同时转账交易也可以由接收⽅(或其他第三⽅)来提交,也避免了⽤户(ERC20的拥有者)需要有 ETH的依赖。
pragma solidity ^0.8.0;

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

contract ERC2612 is ERC20, ERC20Permit {
    constructor() ERC20("ERC2612", "ERC2612") ERC20Permit("ERC2612") {
        _mint(msg.sender, 1000 * 10 ** 18);
    }
}

同质化Token授权总结

  • image2
  • image3
  • image4

  • ERC20 转账应该总是使⽤ safeTransfer:
    • https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/TransferHelper.sol

ERC-721

  • ERC20 合约中每个 token 与其他的 token ⼀样,称为同质化 Token(可置换的)
  • ERC721 合约中每个 token 都是独⼀⽆⼆的,如:
    • 艺术品画作、收藏品
    • 创作品:声⾳、影⽚、⽂章、⼀份档案
    • 游戏中的(限量)道具
    • 任何有特性的内容:⼀个交易记录

协议标准

pragma solidity ^0.8.0;

interface IERC721 /* is ERC165 */ {
    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
    event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    function balanceOf(address _owner) external view returns (uint256);

    function ownerOf(uint256 _tokenId) external view returns (address);

    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data)
    external payable;

    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    function approve(address _approved, uint256 _tokenId) external payable;

    function setApprovalForAll(address _operator, bool _approved) external;

    function getApproved(uint256 _tokenId) external view returns (address);

    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

元数据标准接口

pragma solidity ^0.8.0;

interface ERC721Metadata {
    function name() external pure returns (string _name);

    function symbol() external pure returns (string _symbol);

    function tokenURI(uint256 _tokenId) external view returns (string);
}
  • ERC721 合约中每个 token 有⼀个 id
  • 每个 Token 有⼀个对应 URI 来描述属性(JSON)

ERC-1155

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract GameItem is ERC721URIStorage {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    constructor() ERC721("GameItem", "ITM") {}
    function awardItem(address player, string memory tokenURI) public returns (uint256) {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();
        _mint(player, newItemId);
        _setTokenURI(newItemId, tokenURI);
        return newItemId;
    }
}
This post is licensed under CC BY 4.0 by the author.