ERC20
ERC-20 的功能示例包括:
- 将代币从一个帐户转到另一个帐户
- 获取帐户的当前代币余额
- 获取网络上可用代币的总供应量
- 批准一个帐户中一定的代币金额由第三方帐户使用
ERC20是一种同质化token
, token
之间是完全等价的. token
就是一个uint256
类型的数字.
ERC20
也是在目前普及最多的代币协议。
方法
function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
function totalSupply() public view returns (uint256)
function balanceOf(address _owner) public view returns (uint256 balance)
function transfer(address _to, uint256 _value) public returns (bool success)
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
function approve(address _spender, uint256 _value) public returns (bool success)
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
事件
event Transfer(address indexed _from, address indexed _to, uint256 _value)
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
ERC20案例
// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;
import "../utils/SafeMath.sol";
import "../interfaces/IERC20.sol";
contract ERC20 is IERC20 {
using SafeMath for uint256;
string public override name;
string public override symbol;
uint8 public override decimals;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
}
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount)
public
virtual
override
returns (bool)
{
_transfer(msg.sender, recipient, amount);
return true;
}
function allowance(address owner, address spender)
public
view
override
returns (uint256)
{
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount)
public
override
returns (bool)
{
_approve(msg.sender, spender, amount);
return true;
}
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(
sender,
msg.sender,
_allowances[sender][msg.sender].sub(
amount,
" ERC20: transfer amount exceeds allowance"
)
);
return true;
}
function _transfer(
address sender,
address recipient,
uint256 amount
) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(
amount,
"ERC20: transfer amount exceeds balance"
);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
function _mint(address account, uint256 amount) internal returns (bool) {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
return true;
}
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_balances[account] = _balances[account].sub(
amount,
"ERC20: burn amount exceeds balance"
);
_totalSupply = _totalSupply.sub(
amount,
"ERC20: burn amount exceeds total supply"
);
emit Transfer(account, address(0), amount);
}
function _approve(
address owner,
address spender,
uint256 amount
) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
}
ERC721
ERC-721是非同质化代币(Non-Fungible Token,缩写为NFT)的标准接口。ERC-721在2017年底首先被CryptoKitties游戏所使用,CryptoKitties的火爆让人们意识到了ERC-721的重要价值。ERC-721是非同质化代币,也就意味着每个Token都是不一样的,都有自己的唯一性和独特价值,当然这也就意味着它们是不可分割的,也同时具有了可追踪性。ERC-721代表了对资产的所有权,为物品或记录的代币化提供了可能,开劈了一个巨大的市场,比如现实世界的房屋(房屋是不可移动,占据特定空间的资产,具有唯一性)和独一无二的艺术品;虚拟世界中的收藏物品,如以太坊上的各种收藏游戏中各种独一无二的收藏品;金融交易中的借贷交易记录。
简明的ERC721智能合约声明:
contract ERC721 {
//元数据,可选,建议有
function name() external view returns (string _name);
function symbol() external view returns (string _symbol);
function tokenURI(uint256 _tokenId) external view returns (string);
//枚举查询选项,可选,建议有
function totalSupply() external view returns (uint256);
function tokenByIndex(uint256 _index) external view returns (uint256);
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
//基本函数-查询
function balanceOf(address _owner) constant returns (uint balance);
function ownerOf(uint256 _tokenId) constant returns (address owner);
//基本函数-操作
function approve(address _to, uint256 _tokenId);(setApprovalForAll/getApproved/isApprovedForAll/)
function transfer(address _to, uint256 _tokenId);(safeTransferFrom/transferFrom)
//基本函数-获取元数据
function tokenMetadata(uint256 _tokenId) constant returns (string infoUrl);
//事件
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);
}
name - 名称
该函数应当返回通证的名称。 例如:
contract MyNFT {
function name() constant returns(string name){
return "My Non-FungibleToken";
}
}
Symbol - 符号
该函数应当返回通证的符号,它有助于提高与ERC20的兼容性。例如:
contract MyNFT {
function symbol() constant returns(string symbol){
return "MNFT";
}
}
totalSupply - 总发行量
该函数应当返回区块链上供应的通证总数量,该数量不一定是固定不变的。 例如:
contract MyNFT {
//想发行多少取决于你 ;)
uint256 private totalSupply = 1000000000;
function totalSupply() constant returns (uint256supply){
return totalSupply;
}
}
balanceOf - 余额
该函数用于查询某一地址里的通证余额。例如:
contract MyNFT {
mapping(address => uint) privatebalances;
function balanceOf(address _owner) constant returns(uint balance){
return balances[_owner];
}
}
下面这些函数定义了合约如何处理通证的所有权及如何转移所有权。其中最重要的两个函数 是获取(takeOwnership)和转账(transfer),用来实现用户之间的通证流转,就像银行的提款 和汇款功能。
ownerOf - 持币人
该函数返回通证持有人的地址。因为每一个ERC721通证都是不可替代的,因此可以在区块链上 唯一的地址找到,我们可以用通证的ID来确定其持有人。
contract MyNFT {
mapping(uint256 => address) privatetokenOwners;
mapping(uint256 => bool) private tokenExists;
function ownerOf(uint256 _tokenId) constant returns (address owner) {
require(tokenExists[_tokenId]);
return tokenOwners[_tokenId];
}
}
approve - 授权
该函数用来授权给另一主体代表持有人进行通证转移操作。例如,假设Alice有一个ERC721通证,她可以 调用approve函数来授权给她的朋友Bob,然后Bob就可以代表Alice行使通证持有人的权利。
contract MyNFT {
mapping(address => mapping (address=> uint256)) allowed;
function approve(address _to, uint256 _tokenId){
require(msg.sender ==ownerOf(_tokenId));
require(msg.sender != _to);
allowed[msg.sender][_to] = _tokenId;
Approval(msg.sender, _to, _tokenId);
}
}
takeOwnership - 获取
权拥有一定数量的通证的情况下,可以通过该功能将这部分 通证从另一个用户的账户中提取出来。
contract MyNFT {
function takeOwnership(uint256_tokenId){
require(tokenExists[_tokenId]);
address oldOwner = ownerOf(_tokenId);
address newOwner = msg.sender;
require(newOwner != oldOwner);
require(allowed[oldOwner][newOwner] == _tokenId);
balances[oldOwner] -= 1;
tokenOwners[_tokenId] = newOwner;
balances[oldOwner] += 1;
Transfer(oldOwner, newOwner,_tokenId);
}
}
transfer - 转账
另一种转移通证的方法时使用transfer函数。转账(transfer)功能可以让用户将通证发给另一个用户, 类似于操作比特币这样的加密数字货币。然而,只有在汇出账户之前授权过汇入账户持有其通证的 情况下,才可以进行转账。
contract MyNFT {
mapping(address => mapping(uint256 => uint256)) private ownerTokens;
function removeFromTokenList(address owner, uint256 _tokenId) private {
for(uint256 i = 0;ownerTokens[owner][i] != _tokenId;i++){
ownerTokens[owner][i] = 0;
}
}
function transfer(address _to, uint256 _tokenId){
address currentOwner = msg.sender;
address newOwner = _to;
require(tokenExists[_tokenId]);
require(currentOwner == ownerOf(_tokenId));
require(currentOwner != newOwner);
require(newOwner != address(0));
removeFromTokenList(_tokenId);
balances[oldOwner] -= 1;
tokenOwners[_tokenId] = newOwner;
balances[newOwner] += 1;
Transfer(oldOwner, newOwner, _tokenId);
}
}
tokenOfOwnerByIndex - 通证检索
这个函数是可选的,但推荐你实现它。
每一个ERC721通证的持有者可以同时持有不止一个通证,因为每个通证都有唯一的ID,但是,要跟踪某个用户持有的 通证可能就会比较困难。为此,合约需要记录每个用户持有的每个通证。通过这种方式,用户可以 通过索引清单检索其拥有的通证。通证检索(tokenOfOwnerByIndex)函数可以通过这种方式追溯某一特定的通证。
contract MyNFT {
mapping(address => mapping(uint256 => uint256)) private ownerTokens;
function tokenOfOwnerByIndex(address _owner, uint256 _index) constant returns (uint tokenId){
return ownerTokens[_owner][_index];
}
}
tokenMetaData - 通证元数据
就像我们之前所说的,使物品具有不可替代性的是它们独一无二的特质。美元和网球卡不可替代, 因为它们的特征不同。但是,在区块链上将这些区分每个通证的特征储存下来成本很高,也不推荐这么做。 为了解决这个问题,我们可以储存每个通证的引用(references),例如IPFS哈希或HTTP(S)链接,这些 引用,被称作元数据。元数据是可选的。
tokenMetaData
函数应当返回通证的元数据,或者通证数据的链接。
contract MyNFT {
mapping(uint256 => string) tokenLinks;
function tokenMetadata(uint256 _tokenId) constant returns (string infoUrl) {
return tokenLinks[_tokenId];
}
}
当调用合约方法的时候,事件将会被触发,并且一旦被触发就会向监听系统传播。外部应用可以监听区块链 中的事件,一旦接收到区块链中的事件被触发,监听系统就可以通过事件中包含的信息执行逻辑程序。 ERC721标准定义了下面两个事件。
Transfer 事件- 转账
当一个通证的所有权从一个用户转移到另一个时,将触发该事件,事件的信息包括汇出账户、汇入账户和通证ID。
contract MyNFT {
event Transfer(address indexed _from,address indexed _to, uint256 _tokenId);
}
Approval 事件- 批准
当一个用户允许另一个用户持有其通证的时候(例如启用“授权”功能的时候),该事件就会被触发,事件的信息中 包含这些通证现在的持有账户、被授权账户以及通证ID。
contract MyNFT {
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
}