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分类
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授权总结
- 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.