乐闻世界logo
搜索文章和话题

Solidity 中 ERC20 和 ERC721 代币标准的核心实现原理是什么?

3月6日 21:51

ERC20 和 ERC721 是以太坊上最广泛使用的代币标准,分别代表同质化代币和非同质化代币(NFT)。理解它们的核心实现原理对于开发 DeFi 和 NFT 项目至关重要。

1. ERC20 标准详解

ERC20 定义了同质化代币的标准接口,每个代币都是可互换的。

ERC20 接口定义

solidity
interface IERC20 { // 查询总供应量 function totalSupply() external view returns (uint256); // 查询账户余额 function balanceOf(address account) external view returns (uint256); // 查询授权额度 function allowance(address owner, address spender) external view returns (uint256); // 转账 function transfer(address to, uint256 amount) external returns (bool); // 授权 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); }

ERC20 完整实现

solidity
contract ERC20 is IERC20 { // 状态变量 mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; // 构造函数 constructor(string memory name_, string memory symbol_, uint8 decimals_) { _name = name_; _symbol = symbol_; _decimals = decimals_; } // 查询函数 function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function decimals() public view returns (uint8) { return _decimals; } function totalSupply() public view override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } function allowance(address owner, address spender) public view override returns (uint256) { return _allowances[owner][spender]; } // 转账函数 function transfer(address to, uint256 amount) public override returns (bool) { address owner = msg.sender; _transfer(owner, to, amount); return true; } // 授权函数 function approve(address spender, uint256 amount) public override returns (bool) { address owner = msg.sender; _approve(owner, spender, amount); return true; } // 授权转账 function transferFrom(address from, address to, uint256 amount) public override returns (bool) { address spender = msg.sender; _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } // 内部转账逻辑 function _transfer(address from, address to, uint256 amount) internal { require(from != address(0), "ERC20: transfer from zero address"); require(to != address(0), "ERC20: transfer to zero address"); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: insufficient balance"); unchecked { _balances[from] = fromBalance - amount; _balances[to] += amount; } emit Transfer(from, to, amount); } // 内部授权逻辑 function _approve(address owner, address spender, uint256 amount) internal { require(owner != address(0), "ERC20: approve from zero address"); require(spender != address(0), "ERC20: approve to zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } // 消耗授权额度 function _spendAllowance(address owner, address spender, uint256 amount) internal { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } // 铸造代币(内部函数) function _mint(address account, uint256 amount) internal { require(account != address(0), "ERC20: mint to zero address"); _totalSupply += amount; unchecked { _balances[account] += amount; } emit Transfer(address(0), account, amount); } // 销毁代币(内部函数) function _burn(address account, uint256 amount) internal { require(account != address(0), "ERC20: burn from zero address"); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; _totalSupply -= amount; } emit Transfer(account, address(0), amount); } }

ERC20 扩展功能

solidity
// 可暂停的 ERC20 import "@openzeppelin/contracts/security/Pausable.sol"; contract PausableERC20 is ERC20, Pausable { constructor() ERC20("PausableToken", "PTK", 18) {} function _beforeTokenTransfer(address from, address to, uint256 amount) internal override whenNotPaused { super._beforeTokenTransfer(from, to, amount); } function pause() public onlyOwner { _pause(); } function unpause() public onlyOwner { _unpause(); } } // 可销毁的 ERC20 contract BurnableERC20 is ERC20 { constructor() ERC20("BurnableToken", "BTK", 18) {} function burn(uint256 amount) public { _burn(msg.sender, amount); } function burnFrom(address account, uint256 amount) public { _spendAllowance(account, msg.sender, amount); _burn(account, amount); } }

2. ERC721 标准详解

ERC721 定义了非同质化代币(NFT)的标准,每个代币都是唯一的。

ERC721 接口定义

solidity
interface IERC721 { // 查询余额 function balanceOf(address owner) external view returns (uint256 balance); // 查询代币所有者 function ownerOf(uint256 tokenId) external view returns (address owner); // 安全转账 function safeTransferFrom(address from, address to, uint256 tokenId) external; function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; // 转账 function transferFrom(address from, address to, uint256 tokenId) external; // 授权 function approve(address to, uint256 tokenId) external; function setApprovalForAll(address operator, bool approved) external; // 查询授权 function getApproved(uint256 tokenId) external view returns (address operator); function isApprovedForAll(address owner, address operator) external view returns (bool); // 事件 event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); } // 元数据接口 interface IERC721Metadata { function name() external view returns (string memory); function symbol() external view returns (string memory); function tokenURI(uint256 tokenId) external view returns (string memory); }

ERC721 完整实现

solidity
contract ERC721 is IERC721, IERC721Metadata { // Token name string private _name; // Token symbol string private _symbol; // Token ID 到所有者地址的映射 mapping(uint256 => address) private _owners; // 所有者地址到代币数量的映射 mapping(address => uint256) private _balances; // Token ID 到授权地址的映射 mapping(uint256 => address) private _tokenApprovals; // 所有者到操作员授权的映射 mapping(address => mapping(address => bool)) private _operatorApprovals; // Token ID 到 token URI 的映射 mapping(uint256 => string) private _tokenURIs; constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } function name() public view override returns (string memory) { return _name; } function symbol() public view override returns (string memory) { return _symbol; } function tokenURI(uint256 tokenId) public view override returns (string memory) { require(_exists(tokenId), "ERC721: URI query for nonexistent token"); return _tokenURIs[tokenId]; } function balanceOf(address owner) public view override returns (uint256) { require(owner != address(0), "ERC721: balance query for zero address"); return _balances[owner]; } function ownerOf(uint256 tokenId) public view override returns (address) { address owner = _owners[tokenId]; require(owner != address(0), "ERC721: owner query for nonexistent token"); return owner; } function approve(address to, uint256 tokenId) public override { address owner = ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require( msg.sender == owner || isApprovedForAll(owner, msg.sender), "ERC721: approve caller is not owner nor approved for all" ); _approve(to, tokenId); } function getApproved(uint256 tokenId) public view override returns (address) { require(_exists(tokenId), "ERC721: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } function setApprovalForAll(address operator, bool approved) public override { require(operator != msg.sender, "ERC721: approve to caller"); _operatorApprovals[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function isApprovedForAll(address owner, address operator) public view override returns (bool) { return _operatorApprovals[owner][operator]; } function transferFrom(address from, address to, uint256 tokenId) public override { require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); _transfer(from, to, tokenId); } function safeTransferFrom(address from, address to, uint256 tokenId) public override { safeTransferFrom(from, to, tokenId, ""); } function safeTransferFrom( address from, address to, uint256 tokenId, bytes memory _data ) public override { require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); _safeTransfer(from, to, tokenId, _data); } // 内部函数 function _exists(uint256 tokenId) internal view returns (bool) { return _owners[tokenId] != address(0); } function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { require(_exists(tokenId), "ERC721: operator query for nonexistent token"); address owner = ownerOf(tokenId); return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); } function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } function _transfer(address from, address to, uint256 tokenId) internal { require(ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); require(to != address(0), "ERC721: transfer to the zero address"); // 清除授权 _approve(address(0), tokenId); _balances[from] -= 1; _balances[to] += 1; _owners[tokenId] = to; emit Transfer(from, to, tokenId); } function _approve(address to, uint256 tokenId) internal { _tokenApprovals[tokenId] = to; emit Approval(ownerOf(tokenId), to, tokenId); } // 铸造函数 function _mint(address to, uint256 tokenId) internal { require(to != address(0), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _balances[to] += 1; _owners[tokenId] = to; emit Transfer(address(0), to, tokenId); } // 安全铸造 function _safeMint(address to, uint256 tokenId) internal { _safeMint(to, tokenId, ""); } function _safeMint(address to, uint256 tokenId, bytes memory _data) internal { _mint(to, tokenId); require( _checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer" ); } // 销毁函数 function _burn(uint256 tokenId) internal { address owner = ownerOf(tokenId); _approve(address(0), tokenId); _balances[owner] -= 1; delete _owners[tokenId]; emit Transfer(owner, address(0), tokenId); } // 检查接收者是否实现了 ERC721Receiver 接口 function _checkOnERC721Received( address from, address to, uint256 tokenId, bytes memory _data ) private returns (bool) { if (to.code.length > 0) { try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data) returns (bytes4 retval) { return retval == IERC721Receiver.onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert("ERC721: transfer to non ERC721Receiver implementer"); } else { assembly { revert(add(32, reason), mload(reason)) } } } } return true; } } // ERC721Receiver 接口 interface IERC721Receiver { function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }

3. ERC20 vs ERC721 对比

特性ERC20ERC721
代币类型同质化非同质化
互换性可互换唯一不可互换
余额查询balanceOf(address)balanceOf(address) + ownerOf(tokenId)
转账transfer(to, amount)transferFrom(from, to, tokenId)
授权approve(spender, amount)approve(to, tokenId) + setApprovalForAll(operator, approved)
使用场景货币、积分、治理代币数字艺术品、游戏道具、身份凭证

4. 实际应用示例

完整的 ERC20 Token

solidity
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MyToken is ERC20, Ownable { constructor(uint256 initialSupply) ERC20("MyToken", "MTK") { _mint(msg.sender, initialSupply * 10 ** decimals()); } function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } }

完整的 NFT 合约

solidity
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MyNFT is ERC721URIStorage, Ownable { uint256 private _tokenIds; uint256 public mintPrice = 0.01 ether; uint256 public maxSupply = 10000; string public baseURI; constructor(string memory _baseURI) ERC721("MyNFT", "MNFT") { baseURI = _baseURI; } function mint(string memory tokenURI) public payable returns (uint256) { require(msg.value >= mintPrice, "Insufficient payment"); require(_tokenIds < maxSupply, "Max supply reached"); _tokenIds++; uint256 newTokenId = _tokenIds; _mint(msg.sender, newTokenId); _setTokenURI(newTokenId, tokenURI); return newTokenId; } function withdraw() public onlyOwner { payable(owner()).transfer(address(this).balance); } function _baseURI() internal view override returns (string memory) { return baseURI; } }

5. 安全注意事项

  1. 整数溢出:使用 Solidity 0.8+ 或 SafeMath 库
  2. 重入攻击:遵循 Checks-Effects-Interactions 模式
  3. 授权管理:正确处理 approve 和 allowance
  4. 零地址检查:防止代币发送到零地址
  5. 事件发射:所有状态变更都应触发事件

6. 扩展标准

  • ERC777:更高级的代币标准,支持钩子函数
  • ERC1155:多代币标准,支持同质化和非同质化代币
  • ERC2981:NFT 版税标准
  • ERC4907:NFT 租赁标准
标签:Solidity