HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • Web3 完整技术体系

    • Web3 完整技术体系
    • 区块链基础与密码学
    • 第一章:比特币原理与实现
    • 第二章:以太坊架构与核心概念
    • Solidity智能合约开发基础
    • 04-Solidity进阶与安全
    • 05-ERC标准详解
    • 06-DeFi核心协议-去中心化交易所
    • 07-DeFi核心协议-借贷协议
    • 08-DeFi核心协议-稳定币
    • NFT与元宇宙
    • Layer2扩容方案
    • 跨链技术
    • Web3前端开发
    • 链下数据与预言机
    • 智能合约测试与部署
    • MEV与交易优化
    • DAO治理
    • 项目实战:完整DeFi借贷协议

05-ERC标准详解

第一部分:ERC20代币标准

1.1 ERC20标准概述

ERC20是以太坊上最广泛使用的代币标准,定义了同质化代币(Fungible Token)的接口规范。

1.1.1 标准接口

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

/**
 * @dev ERC20标准接口
 */
interface IERC20 {
    // 返回代币总供应量
    function totalSupply() external view returns (uint256);

    // 返回账户余额
    function balanceOf(address account) external view returns (uint256);

    // 转账
    function transfer(address recipient, 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 sender, address recipient, 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);
}

1.2 完整的ERC20实现

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

/**
 * @title ERC20
 * @dev 完整的ERC20代币实现
 */
contract ERC20 is IERC20 {
    // 状态变量
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    uint256 private _totalSupply;

    string public name;
    string public symbol;
    uint8 public decimals;

    /**
     * @dev 构造函数
     */
    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
        decimals = 18;
    }

    /**
     * @dev 返回代币总供应量
     */
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev 返回账户余额
     */
    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev 转账
     */
    function transfer(address recipient, uint256 amount) public override returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    /**
     * @dev 返回授权额度
     */
    function allowance(address owner, address spender) public view override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev 授权
     */
    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    /**
     * @dev 授权转账
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][msg.sender];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, msg.sender, currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev 增加授权额度
     * 解决approve的竞态条件问题
     */
    function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
        _approve(msg.sender, spender, _allowances[msg.sender][spender] + addedValue);
        return true;
    }

    /**
     * @dev 减少授权额度
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
        uint256 currentAllowance = _allowances[msg.sender][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(msg.sender, spender, currentAllowance - subtractedValue);
        }
        return true;
    }

    /**
     * @dev 内部转账函数
     */
    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");

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);
    }

    /**
     * @dev 铸造代币
     */
    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: mint to the zero address");

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);
    }

    /**
     * @dev 销毁代币
     */
    function _burn(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: burn from the 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);
    }

    /**
     * @dev 内部授权函数
     */
    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);
    }
}

1.3 扩展功能的ERC20

1.3.1 可铸造的代币

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

contract ERC20Mintable is ERC20 {
    address public owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        owner = msg.sender;
    }

    /**
     * @dev 铸造代币
     */
    function mint(address account, uint256 amount) public onlyOwner {
        _mint(account, amount);
    }

    /**
     * @dev 转移所有权
     */
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), "New owner is zero address");
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}

1.3.2 可销毁的代币

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

contract ERC20Burnable is ERC20 {
    constructor(string memory name, string memory symbol) ERC20(name, symbol) {}

    /**
     * @dev 销毁自己的代币
     */
    function burn(uint256 amount) public {
        _burn(msg.sender, amount);
    }

    /**
     * @dev 销毁授权的代币
     */
    function burnFrom(address account, uint256 amount) public {
        uint256 currentAllowance = allowance(account, msg.sender);
        require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
        unchecked {
            _approve(account, msg.sender, currentAllowance - amount);
        }
        _burn(account, amount);
    }
}

1.3.3 可暂停的代币

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

contract ERC20Pausable is ERC20 {
    address public owner;
    bool public paused;

    event Paused(address account);
    event Unpaused(address account);

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    modifier whenNotPaused() {
        require(!paused, "Pausable: paused");
        _;
    }

    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        owner = msg.sender;
    }

    /**
     * @dev 暂停
     */
    function pause() public onlyOwner {
        paused = true;
        emit Paused(msg.sender);
    }

    /**
     * @dev 恢复
     */
    function unpause() public onlyOwner {
        paused = false;
        emit Unpaused(msg.sender);
    }

    /**
     * @dev 重写transfer,添加暂停检查
     */
    function transfer(address recipient, uint256 amount)
        public
        override
        whenNotPaused
        returns (bool)
    {
        return super.transfer(recipient, amount);
    }

    /**
     * @dev 重写transferFrom,添加暂停检查
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public override whenNotPaused returns (bool) {
        return super.transferFrom(sender, recipient, amount);
    }
}

1.3.4 带快照功能的代币

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

contract ERC20Snapshot is ERC20 {
    address public owner;

    // 快照ID
    uint256 private _currentSnapshotId;

    // 快照数据结构
    struct Snapshots {
        uint256[] ids;
        uint256[] values;
    }

    // 账户余额快照
    mapping(address => Snapshots) private _accountBalanceSnapshots;
    // 总供应量快照
    Snapshots private _totalSupplySnapshots;

    event Snapshot(uint256 id);

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        owner = msg.sender;
    }

    /**
     * @dev 创建快照
     */
    function snapshot() public onlyOwner returns (uint256) {
        _currentSnapshotId++;
        emit Snapshot(_currentSnapshotId);
        return _currentSnapshotId;
    }

    /**
     * @dev 获取快照时的余额
     */
    function balanceOfAt(address account, uint256 snapshotId)
        public
        view
        returns (uint256)
    {
        (bool snapshotted, uint256 value) = _valueAt(
            snapshotId,
            _accountBalanceSnapshots[account]
        );
        return snapshotted ? value : balanceOf(account);
    }

    /**
     * @dev 获取快照时的总供应量
     */
    function totalSupplyAt(uint256 snapshotId) public view returns (uint256) {
        (bool snapshotted, uint256 value) = _valueAt(snapshotId, _totalSupplySnapshots);
        return snapshotted ? value : totalSupply();
    }

    /**
     * @dev 重写transfer,更新快照
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal override {
        _updateAccountSnapshot(sender);
        _updateAccountSnapshot(recipient);
        super._transfer(sender, recipient, amount);
    }

    /**
     * @dev 重写mint,更新快照
     */
    function _mint(address account, uint256 amount) internal override {
        _updateAccountSnapshot(account);
        _updateTotalSupplySnapshot();
        super._mint(account, amount);
    }

    /**
     * @dev 重写burn,更新快照
     */
    function _burn(address account, uint256 amount) internal override {
        _updateAccountSnapshot(account);
        _updateTotalSupplySnapshot();
        super._burn(account, amount);
    }

    function _valueAt(uint256 snapshotId, Snapshots storage snapshots)
        private
        view
        returns (bool, uint256)
    {
        require(snapshotId > 0, "Snapshot id is 0");
        require(snapshotId <= _currentSnapshotId, "Nonexistent snapshot id");

        uint256 index = _findIndex(snapshots.ids, snapshotId);

        if (index == snapshots.ids.length) {
            return (false, 0);
        } else {
            return (true, snapshots.values[index]);
        }
    }

    function _updateAccountSnapshot(address account) private {
        _updateSnapshot(_accountBalanceSnapshots[account], balanceOf(account));
    }

    function _updateTotalSupplySnapshot() private {
        _updateSnapshot(_totalSupplySnapshots, totalSupply());
    }

    function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) private {
        uint256 currentId = _currentSnapshotId;
        if (_lastSnapshotId(snapshots.ids) < currentId) {
            snapshots.ids.push(currentId);
            snapshots.values.push(currentValue);
        }
    }

    function _lastSnapshotId(uint256[] storage ids) private view returns (uint256) {
        if (ids.length == 0) {
            return 0;
        } else {
            return ids[ids.length - 1];
        }
    }

    function _findIndex(uint256[] storage ids, uint256 snapshotId)
        private
        view
        returns (uint256)
    {
        uint256 low = 0;
        uint256 high = ids.length;

        while (low < high) {
            uint256 mid = (low + high) / 2;
            if (ids[mid] > snapshotId) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        return high;
    }
}

1.4 实际应用案例

1.4.1 治理代币

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

/**
 * @title GovernanceToken
 * @dev 用于DAO治理的代币
 */
contract GovernanceToken is ERC20Snapshot {
    // 提案结构
    struct Proposal {
        string description;
        uint256 forVotes;
        uint256 againstVotes;
        uint256 snapshotId;
        uint256 deadline;
        bool executed;
        mapping(address => bool) hasVoted;
    }

    Proposal[] public proposals;

    event ProposalCreated(uint256 indexed proposalId, string description, uint256 deadline);
    event Voted(uint256 indexed proposalId, address indexed voter, bool support, uint256 weight);
    event ProposalExecuted(uint256 indexed proposalId);

    constructor() ERC20Snapshot("Governance Token", "GOV") {
        _mint(msg.sender, 1000000 * 10**18);
    }

    /**
     * @dev 创建提案
     */
    function createProposal(string memory description, uint256 votingPeriod)
        public
        returns (uint256)
    {
        require(balanceOf(msg.sender) >= 1000 * 10**18, "Insufficient tokens to propose");

        uint256 snapshotId = snapshot();
        uint256 proposalId = proposals.length;

        Proposal storage newProposal = proposals.push();
        newProposal.description = description;
        newProposal.snapshotId = snapshotId;
        newProposal.deadline = block.timestamp + votingPeriod;

        emit ProposalCreated(proposalId, description, newProposal.deadline);
        return proposalId;
    }

    /**
     * @dev 投票
     */
    function vote(uint256 proposalId, bool support) public {
        Proposal storage proposal = proposals[proposalId];
        require(block.timestamp < proposal.deadline, "Voting ended");
        require(!proposal.hasVoted[msg.sender], "Already voted");

        uint256 weight = balanceOfAt(msg.sender, proposal.snapshotId);
        require(weight > 0, "No voting power");

        proposal.hasVoted[msg.sender] = true;

        if (support) {
            proposal.forVotes += weight;
        } else {
            proposal.againstVotes += weight;
        }

        emit Voted(proposalId, msg.sender, support, weight);
    }

    /**
     * @dev 执行提案
     */
    function executeProposal(uint256 proposalId) public {
        Proposal storage proposal = proposals[proposalId];
        require(block.timestamp >= proposal.deadline, "Voting not ended");
        require(!proposal.executed, "Already executed");
        require(proposal.forVotes > proposal.againstVotes, "Proposal rejected");

        proposal.executed = true;
        emit ProposalExecuted(proposalId);

        // 执行提案逻辑...
    }

    /**
     * @dev 获取提案信息
     */
    function getProposal(uint256 proposalId)
        public
        view
        returns (
            string memory description,
            uint256 forVotes,
            uint256 againstVotes,
            uint256 deadline,
            bool executed
        )
    {
        Proposal storage proposal = proposals[proposalId];
        return (
            proposal.description,
            proposal.forVotes,
            proposal.againstVotes,
            proposal.deadline,
            proposal.executed
        );
    }
}

第二部分:ERC721 NFT标准

2.1 ERC721标准概述

ERC721是非同质化代币(Non-Fungible Token, NFT)标准,每个代币都是唯一的。

2.1.1 标准接口

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

/**
 * @dev ERC721标准接口
 */
interface IERC721 {
    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);

    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 transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
    function getApproved(uint256 tokenId) external view returns (address operator);
    function setApprovalForAll(address operator, bool approved) external;
    function isApprovedForAll(address owner, address operator) external view returns (bool);
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}

/**
 * @dev ERC721接收器接口
 */
interface IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

/**
 * @dev ERC721元数据接口
 */
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);
}

2.2 完整的ERC721实现

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

/**
 * @title ERC721
 * @dev 完整的ERC721实现
 */
contract ERC721 is IERC721, IERC721Metadata {
    // Token名称
    string private _name;
    // Token符号
    string private _symbol;

    // tokenId到owner的映射
    mapping(uint256 => address) private _owners;
    // owner到token数量的映射
    mapping(address => uint256) private _balances;
    // tokenId到授权地址的映射
    mapping(uint256 => address) private _tokenApprovals;
    // owner到operator授权的映射
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev 返回token名称
     */
    function name() public view override returns (string memory) {
        return _name;
    }

    /**
     * @dev 返回token符号
     */
    function symbol() public view override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev 返回token URI
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0
            ? string(abi.encodePacked(baseURI, _toString(tokenId)))
            : "";
    }

    /**
     * @dev 基础URI
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev 返回owner的token数量
     */
    function balanceOf(address owner) public view override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    /**
     * @dev 返回tokenId的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;
    }

    /**
     * @dev 授权
     */
    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);
    }

    /**
     * @dev 获取tokenId的授权地址
     */
    function getApproved(uint256 tokenId) public view override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");
        return _tokenApprovals[tokenId];
    }

    /**
     * @dev 设置operator授权
     */
    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);
    }

    /**
     * @dev 检查operator是否被授权
     */
    function isApprovedForAll(address owner, address operator)
        public
        view
        override
        returns (bool)
    {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev 转移token
     */
    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);
    }

    /**
     * @dev 安全转移token
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev 安全转移token(带数据)
     */
    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);
    }

    /**
     * @dev 内部安全转移函数
     */
    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"
        );
    }

    /**
     * @dev 检查token是否存在
     */
    function _exists(uint256 tokenId) internal view returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev 检查spender是否被授权
     */
    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));
    }

    /**
     * @dev 铸造token
     */
    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);
    }

    /**
     * @dev 安全铸造token
     */
    function _safeMint(address to, uint256 tokenId) internal {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev 安全铸造token(带数据)
     */
    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"
        );
    }

    /**
     * @dev 销毁token
     */
    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);
    }

    /**
     * @dev 内部转移函数
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal {
        require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        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);
    }

    /**
     * @dev 内部授权函数
     */
    function _approve(address to, uint256 tokenId) internal {
        _tokenApprovals[tokenId] = to;
        emit Approval(ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev 检查ERC721接收
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (_isContract(to)) {
            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))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev 检查是否为合约
     */
    function _isContract(address account) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev 将uint256转为string
     */
    function _toString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }
}

2.3 ERC721扩展功能

2.3.1 可枚举的NFT

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

interface IERC721Enumerable {
    function totalSupply() external view returns (uint256);
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
    function tokenByIndex(uint256 index) external view returns (uint256);
}

contract ERC721Enumerable is ERC721, IERC721Enumerable {
    // 所有token的数组
    uint256[] private _allTokens;
    // tokenId到索引的映射
    mapping(uint256 => uint256) private _allTokensIndex;
    // owner的token数组
    mapping(address => uint256[]) private _ownedTokens;
    // tokenId到owner索引的映射
    mapping(uint256 => uint256) private _ownedTokensIndex;

    constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {}

    /**
     * @dev 返回总供应量
     */
    function totalSupply() public view override returns (uint256) {
        return _allTokens.length;
    }

    /**
     * @dev 根据索引返回tokenId
     */
    function tokenByIndex(uint256 index) public view override returns (uint256) {
        require(index < totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

    /**
     * @dev 根据owner和索引返回tokenId
     */
    function tokenOfOwnerByIndex(address owner, uint256 index)
        public
        view
        override
        returns (uint256)
    {
        require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    /**
     * @dev 重写_mint
     */
    function _mint(address to, uint256 tokenId) internal override {
        super._mint(to, tokenId);
        _addTokenToAllTokensEnumeration(tokenId);
        _addTokenToOwnerEnumeration(to, tokenId);
    }

    /**
     * @dev 重写_burn
     */
    function _burn(uint256 tokenId) internal override {
        address owner = ownerOf(tokenId);
        super._burn(tokenId);
        _removeTokenFromAllTokensEnumeration(tokenId);
        _removeTokenFromOwnerEnumeration(owner, tokenId);
    }

    /**
     * @dev 重写_transfer
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override {
        super._transfer(from, to, tokenId);
        _removeTokenFromOwnerEnumeration(from, tokenId);
        _addTokenToOwnerEnumeration(to, tokenId);
    }

    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        _ownedTokensIndex[tokenId] = _ownedTokens[to].length;
        _ownedTokens[to].push(tokenId);
    }

    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        uint256 lastTokenIndex = _ownedTokens[from].length - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
            _ownedTokens[from][tokenIndex] = lastTokenId;
            _ownedTokensIndex[lastTokenId] = tokenIndex;
        }

        delete _ownedTokensIndex[tokenId];
        _ownedTokens[from].pop();
    }

    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId;
        _allTokensIndex[lastTokenId] = tokenIndex;

        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }
}

2.3.2 NFT市场合约

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

/**
 * @title NFTMarketplace
 * @dev NFT交易市场
 */
contract NFTMarketplace {
    struct Listing {
        address seller;
        uint256 price;
        bool active;
    }

    // NFT合约地址 => tokenId => 挂单信息
    mapping(address => mapping(uint256 => Listing)) public listings;

    // 平台手续费率(基点,1% = 100)
    uint256 public feeRate = 250; // 2.5%
    address public feeRecipient;

    event Listed(
        address indexed nftContract,
        uint256 indexed tokenId,
        address indexed seller,
        uint256 price
    );

    event Sold(
        address indexed nftContract,
        uint256 indexed tokenId,
        address indexed buyer,
        address seller,
        uint256 price
    );

    event Cancelled(
        address indexed nftContract,
        uint256 indexed tokenId,
        address indexed seller
    );

    constructor(address _feeRecipient) {
        feeRecipient = _feeRecipient;
    }

    /**
     * @dev 挂单出售NFT
     */
    function listNFT(
        address nftContract,
        uint256 tokenId,
        uint256 price
    ) external {
        require(price > 0, "Price must be greater than 0");

        IERC721 nft = IERC721(nftContract);
        require(nft.ownerOf(tokenId) == msg.sender, "Not token owner");
        require(
            nft.isApprovedForAll(msg.sender, address(this)) ||
                nft.getApproved(tokenId) == address(this),
            "Marketplace not approved"
        );

        listings[nftContract][tokenId] = Listing({
            seller: msg.sender,
            price: price,
            active: true
        });

        emit Listed(nftContract, tokenId, msg.sender, price);
    }

    /**
     * @dev 购买NFT
     */
    function buyNFT(address nftContract, uint256 tokenId) external payable {
        Listing memory listing = listings[nftContract][tokenId];
        require(listing.active, "Listing not active");
        require(msg.value >= listing.price, "Insufficient payment");

        // 计算费用
        uint256 fee = (listing.price * feeRate) / 10000;
        uint256 sellerProceeds = listing.price - fee;

        // 删除挂单
        delete listings[nftContract][tokenId];

        // 转移NFT
        IERC721(nftContract).safeTransferFrom(listing.seller, msg.sender, tokenId);

        // 转账
        payable(listing.seller).transfer(sellerProceeds);
        payable(feeRecipient).transfer(fee);

        // 退还多余ETH
        if (msg.value > listing.price) {
            payable(msg.sender).transfer(msg.value - listing.price);
        }

        emit Sold(nftContract, tokenId, msg.sender, listing.seller, listing.price);
    }

    /**
     * @dev 取消挂单
     */
    function cancelListing(address nftContract, uint256 tokenId) external {
        Listing memory listing = listings[nftContract][tokenId];
        require(listing.active, "Listing not active");
        require(listing.seller == msg.sender, "Not seller");

        delete listings[nftContract][tokenId];

        emit Cancelled(nftContract, tokenId, msg.sender);
    }

    /**
     * @dev 更新价格
     */
    function updatePrice(
        address nftContract,
        uint256 tokenId,
        uint256 newPrice
    ) external {
        Listing storage listing = listings[nftContract][tokenId];
        require(listing.active, "Listing not active");
        require(listing.seller == msg.sender, "Not seller");
        require(newPrice > 0, "Price must be greater than 0");

        listing.price = newPrice;

        emit Listed(nftContract, tokenId, msg.sender, newPrice);
    }
}

第三部分:ERC1155多代币标准

3.1 ERC1155标准概述

ERC1155是多代币标准,可以在单个合约中管理多种类型的代币(同质化和非同质化)。

3.1.1 标准接口

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

interface IERC1155 {
    event TransferSingle(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 id,
        uint256 value
    );

    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    event URI(string value, uint256 indexed id);

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

    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    function setApprovalForAll(address operator, bool approved) external;

    function isApprovedForAll(address account, address operator) external view returns (bool);

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

interface IERC1155Receiver {
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

interface IERC1155MetadataURI {
    function uri(uint256 id) external view returns (string memory);
}

3.2 完整的ERC1155实现

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

contract ERC1155 is IERC1155, IERC1155MetadataURI {
    // id => account => balance
    mapping(uint256 => mapping(address => uint256)) private _balances;
    // account => operator => approved
    mapping(address => mapping(address => bool)) private _operatorApprovals;
    // URI
    string private _uri;

    constructor(string memory uri_) {
        _uri = uri_;
    }

    function uri(uint256) public view virtual override returns (string memory) {
        return _uri;
    }

    function balanceOf(address account, uint256 id)
        public
        view
        override
        returns (uint256)
    {
        require(account != address(0), "ERC1155: balance query for the zero address");
        return _balances[id][account];
    }

    function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
        public
        view
        override
        returns (uint256[] memory)
    {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }

    function setApprovalForAll(address operator, bool approved) public virtual override {
        require(msg.sender != operator, "ERC1155: setting approval status for self");

        _operatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function isApprovedForAll(address account, address operator)
        public
        view
        override
        returns (bool)
    {
        return _operatorApprovals[account][operator];
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        require(
            from == msg.sender || isApprovedForAll(from, msg.sender),
            "ERC1155: caller is not owner nor approved"
        );
        _safeTransferFrom(from, to, id, amount, data);
    }

    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        require(
            from == msg.sender || isApprovedForAll(from, msg.sender),
            "ERC1155: transfer caller is not owner nor approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }

    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal {
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = msg.sender;

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }

    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal {
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = msg.sender;

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    }

    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal {
        require(to != address(0), "ERC1155: mint to the zero address");

        address operator = msg.sender;

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    }

    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = msg.sender;

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal {
        require(from != address(0), "ERC1155: burn from the zero address");

        address operator = msg.sender;

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);
    }

    function _burnBatch(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal {
        require(from != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = msg.sender;

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);
    }

    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        if (_isContract(to)) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (
                bytes4 response
            ) {
                if (response != IERC1155Receiver.onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        if (_isContract(to)) {
            try
                IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data)
            returns (bytes4 response) {
                if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _isContract(address account) private view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }
}

3.3 游戏道具合约示例

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

/**
 * @title GameItems
 * @dev 游戏道具合约,使用ERC1155管理多种道具
 */
contract GameItems is ERC1155 {
    address public owner;
    uint256 public constant GOLD_COIN = 0;
    uint256 public constant SILVER_COIN = 1;
    uint256 public constant SWORD = 2;
    uint256 public constant SHIELD = 3;
    uint256 public constant POTION = 4;

    // 道具供应上限
    mapping(uint256 => uint256) public itemSupplyCap;
    // 当前供应量
    mapping(uint256 => uint256) public itemSupply;

    event ItemCrafted(address indexed player, uint256 indexed itemId, uint256 amount);
    event ItemUsed(address indexed player, uint256 indexed itemId, uint256 amount);

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    constructor() ERC1155("https://game.example/api/item/{id}.json") {
        owner = msg.sender;

        // 设置供应上限
        itemSupplyCap[GOLD_COIN] = type(uint256).max; // 无上限
        itemSupplyCap[SILVER_COIN] = type(uint256).max;
        itemSupplyCap[SWORD] = 10000;
        itemSupplyCap[SHIELD] = 10000;
        itemSupplyCap[POTION] = 50000;
    }

    /**
     * @dev 铸造游戏道具
     */
    function mintItem(
        address to,
        uint256 id,
        uint256 amount
    ) public onlyOwner {
        require(
            itemSupply[id] + amount <= itemSupplyCap[id],
            "Exceeds supply cap"
        );

        itemSupply[id] += amount;
        _mint(to, id, amount, "");
    }

    /**
     * @dev 批量铸造道具
     */
    function mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts
    ) public onlyOwner {
        require(ids.length == amounts.length, "Length mismatch");

        for (uint256 i = 0; i < ids.length; i++) {
            require(
                itemSupply[ids[i]] + amounts[i] <= itemSupplyCap[ids[i]],
                "Exceeds supply cap"
            );
            itemSupply[ids[i]] += amounts[i];
        }

        _mintBatch(to, ids, amounts, "");
    }

    /**
     * @dev 合成道具
     * 例如:2个银币 = 1个金币
     */
    function craftGoldCoin() external {
        require(balanceOf(msg.sender, SILVER_COIN) >= 2, "Insufficient silver coins");

        _burn(msg.sender, SILVER_COIN, 2);
        _mint(msg.sender, GOLD_COIN, 1, "");

        emit ItemCrafted(msg.sender, GOLD_COIN, 1);
    }

    /**
     * @dev 使用药水(销毁)
     */
    function usePotion(uint256 amount) external {
        require(balanceOf(msg.sender, POTION) >= amount, "Insufficient potions");

        _burn(msg.sender, POTION, amount);

        emit ItemUsed(msg.sender, POTION, amount);
    }

    /**
     * @dev 批量转移道具
     */
    function giftItems(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts
    ) external {
        safeBatchTransferFrom(msg.sender, to, ids, amounts, "");
    }
}

第四部分:ERC2612(EIP-2612)许可代币

4.1 ERC2612标准概述

ERC2612扩展了ERC20,添加了permit函数,允许用户通过签名授权,无需单独发送授权交易。

4.1.1 标准接口

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

interface IERC2612 {
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    function nonces(address owner) external view returns (uint256);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

4.2 完整的ERC2612实现

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

contract ERC20Permit is ERC20, IERC2612 {
    mapping(address => uint256) private _nonces;

    bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
    uint256 private immutable _CACHED_CHAIN_ID;
    address private immutable _CACHED_THIS;

    bytes32 private immutable _HASHED_NAME;
    bytes32 private immutable _HASHED_VERSION;
    bytes32 private immutable _TYPE_HASH;

    bytes32 public constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes("1"));
        bytes32 typeHash = keccak256(
            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
        );

        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
        _CACHED_CHAIN_ID = block.chainid;
        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
        _CACHED_THIS = address(this);
        _TYPE_HASH = typeHash;
    }

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");

        bytes32 structHash = keccak256(
            abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)
        );

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ecrecover(hash, v, r, s);
        require(signer == owner, "ERC20Permit: invalid signature");

        _approve(owner, spender, value);
    }

    function nonces(address owner) public view virtual override returns (uint256) {
        return _nonces[owner];
    }

    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
        return _domainSeparatorV4();
    }

    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
            return _CACHED_DOMAIN_SEPARATOR;
        } else {
            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
        }
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
    }

    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
    }

    function _useNonce(address owner) internal virtual returns (uint256 current) {
        current = _nonces[owner];
        _nonces[owner] = current + 1;
    }
}

4.3 使用Permit的实际示例

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

/**
 * @title TokenSwapWithPermit
 * @dev 使用permit的代币交换合约
 */
contract TokenSwapWithPermit {
    IERC20Permit public tokenA;
    IERC20Permit public tokenB;

    event Swapped(address indexed user, uint256 amountA, uint256 amountB);

    constructor(address _tokenA, address _tokenB) {
        tokenA = IERC20Permit(_tokenA);
        tokenB = IERC20Permit(_tokenB);
    }

    /**
     * @dev 传统方式:需要先调用approve
     */
    function swapTraditional(uint256 amountA) external {
        // 用户必须先调用tokenA.approve(address(this), amountA)
        tokenA.transferFrom(msg.sender, address(this), amountA);

        uint256 amountB = amountA; // 1:1兑换
        tokenB.transfer(msg.sender, amountB);

        emit Swapped(msg.sender, amountA, amountB);
    }

    /**
     * @dev 使用permit:一次交易完成授权和兑换
     */
    function swapWithPermit(
        uint256 amountA,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        // 使用签名授权
        tokenA.permit(msg.sender, address(this), amountA, deadline, v, r, s);

        // 执行转账
        tokenA.transferFrom(msg.sender, address(this), amountA);

        uint256 amountB = amountA;
        tokenB.transfer(msg.sender, amountB);

        emit Swapped(msg.sender, amountA, amountB);
    }
}

第五部分:标准对比和最佳实践

5.1 ERC标准对比

特性ERC20ERC721ERC1155
代币类型同质化非同质化多代币(同质+非同质)
唯一性所有代币相同每个代币唯一支持两种类型
批量转账不支持不支持支持
Gas效率中等较高最优
使用场景货币、积分艺术品、收藏品游戏道具、多类资产
复杂度简单中等较复杂

5.2 选择建议

  1. ERC20:适用于同质化代币

    • 加密货币
    • 治理代币
    • 积分系统
    • 稳定币
  2. ERC721:适用于唯一资产

    • 数字艺术品
    • 域名
    • 虚拟土地
    • 身份证明
  3. ERC1155:适用于多种资产

    • 游戏道具
    • 票务系统
    • 混合资产平台
    • 需要批量操作的场景

5.3 安全最佳实践

  1. ERC20安全

    • 使用increaseAllowance和decreaseAllowance避免竞态条件
    • 检查地址不为零地址
    • 防止整数溢出(0.8.x自动检查)
    • 实现暂停机制
  2. ERC721安全

    • 使用safeTransferFrom而非transferFrom
    • 验证token存在性
    • 检查授权和所有权
    • 防止重入攻击
  3. ERC1155安全

    • 批量操作时检查数组长度
    • 实现接收检查
    • 验证余额充足
    • 防止整数溢出

5.4 Gas优化技巧

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

contract GasOptimizedERC20 {
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    uint256 private _totalSupply;

    // 使用immutable减少存储读取
    string public immutable name;
    string public immutable symbol;
    uint8 public immutable decimals;

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
        decimals = 18;
    }

    function transfer(address to, uint256 amount) external returns (bool) {
        // 缓存变量减少存储读取
        address sender = msg.sender;
        uint256 senderBalance = _balances[sender];

        require(senderBalance >= amount, "Insufficient balance");

        // 使用unchecked减少gas
        unchecked {
            _balances[sender] = senderBalance - amount;
            _balances[to] += amount;
        }

        return true;
    }

    // 批量转账节省gas
    function batchTransfer(address[] calldata recipients, uint256[] calldata amounts)
        external
        returns (bool)
    {
        require(recipients.length == amounts.length, "Length mismatch");

        address sender = msg.sender;
        uint256 totalAmount = 0;

        // 先计算总额
        for (uint256 i = 0; i < amounts.length; ) {
            totalAmount += amounts[i];
            unchecked { i++; }
        }

        require(_balances[sender] >= totalAmount, "Insufficient balance");

        // 执行转账
        unchecked {
            _balances[sender] -= totalAmount;
        }

        for (uint256 i = 0; i < recipients.length; ) {
            _balances[recipients[i]] += amounts[i];
            unchecked { i++; }
        }

        return true;
    }
}
Prev
04-Solidity进阶与安全
Next
06-DeFi核心协议-去中心化交易所