solidity implements ERC1155 multi-token standard

1. NFT - Wikipedia

ERC-1155 The standard was proposed in June 2018 by Witek Radomski, Andrew Cooke, Philippe Castonguay, James Therien, Eric Binet and Ronan Sandford. This standard can provide multiple combinations of fungible tokens, non-fungible tokens (using ERC-1155 to construct ERC-721 assets) and other structural types (such as semi-fungible tokens) for a single application contract. . Compared with ERC-721, each created token ID corresponds to a separate smart contract asset. Under the ERC-1155 multi-token standard, each token ID can represent a series of different types of assets, and is appended with quantitative blocks to Indicates the quantity of each type of asset in the wallet; assets of the same type can be interchanged, and any amount needs to be transferred.
ERC-1155 The NFT structure under the standard is more flexible: it has metadata, supply and other attributes; new features include: multiple types can be transferred at one time Token type to save transaction costs, you can also conduct multiple token transactions (escrow/atomic swap) at the same time without having to approve each contract one by one, you can indicate and mix multiple FT, NFT and SFT in a single application contract.

2、IERC1155 MetadataURI

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


// ERC1155的可选接口,加入了uri()函数查询元数据
interface IERC1155MetadataURI  {
    
    
    // 返回第`id`种类代币的URI
    function uri(uint256 id) external view returns (string memory);
}

3、IERC1155Receiver

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

import "./IERC165.sol";

interface IERC1155Receiver is IERC165 {
    
    
    // 接受ERC1155安全转账`safeTransferFrom`     
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    // 接受ERC1155批量安全转账`safeBatchTransferFrom` 
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

4、IERC1155

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

import "./IERC165.sol";

// https://eips.ethereum.org/EIPS/eip-1155[EIP].
interface IERC1155 is IERC165 {
    
    
    // 单类代币转账事件
    event TransferSingle(
        address indexed operator, 
        address indexed from, 
        address indexed to, 
        uint256 id, 
        uint256 value
    );

    // 批量代币转账事件, ids和values为转账的代币种类和数量数组
    event TransferBatch(
        address indexed operator, 
        address indexed from, 
        address indexed to, 
        uint256[] ids, 
        uint256[] values
    );

    // 批量授权事件, 当`account`将所有代币授权给`operator`时释放
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    // 当`id`种类的代币的URI发生变化时释放,`value`为新的URI
    event URI(string value, uint256 indexed id);

    // 持仓查询,返回`account`拥有的`id`种类的代币的持仓量
    function balanceOf(address account, uint256 id) external view returns (uint256);

    // 批量持仓查询,`accounts`和`ids`数组的长度要想等。
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);

    // 批量授权,将调用者的代币授权给`operator`地址。释放{ApprovalForAll}事件.
    function setApprovalForAll(address operator, bool approved) external;

    // 批量授权查询,如果授权地址`operator`被`account`授权,则返回`true`
    function isApprovedForAll(address account, address operator) external view returns (bool);

    // 安全转账,将`amount`单位`id`种类的代币从`from`转账给`to`.  释放{TransferSingle}事件.
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    // 批量安全转账, 释放{TransferBatch}事件
    function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}

5、ERC1155

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

import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./IERC1155MetadataURI.sol";
import "./Address.sol";
import "./Strings.sol";
import "./IERC165.sol";

// ERC1155多代币标准 https://eips.ethereum.org/EIPS/eip-1155
contract ERC1155 is IERC165, IERC1155, IERC1155MetadataURI {
    
    
    using Address for address; 
    using Strings for uint256; 
    // Token名称
    string public name;
    // Token代号
    string public symbol;
    // 代币种类id 到 账户account 到 余额balances 的映射
    mapping(uint256 => mapping(address => uint256)) private _balances;
    // address 到 授权地址 的批量授权映射
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // 构造函数,初始化`name` 和`symbol`, uri_
    constructor(string memory name_, string memory symbol_) {
    
    
        name = name_;
        symbol = symbol_;
    }

    function supportsInterface(bytes4 interfaceId) 
        public view virtual override returns (bool) {
    
    
        return
            interfaceId == type(IERC1155).interfaceId ||
            interfaceId == type(IERC1155MetadataURI).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

    // 持仓查询 实现IERC1155的balanceOf,返回account地址的id种类代币持仓量。
    function balanceOf(address account, uint256 id) 
        public view virtual override returns (uint256) {
    
    
        require(account != address(0), "ERC1155: address zero is not a valid owner");
        return _balances[id][account];
    }

    // @dev 批量持仓查询   
    function balanceOfBatch(address[] memory accounts, uint256[] memory ids) 
        public view virtual 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;
    }

    //  批量授权,调用者授权operator使用其所有代币
    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 virtual override returns (bool) {
    
    
        return _operatorApprovals[account][operator];
    }

    // 安全转账,将`amount`单位的`id`种类代币从`from`转账到`to`
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) 
        public virtual override {
    
    
        address operator = msg.sender;
        // 调用者是持有者或是被授权
        require(
            from == operator || isApprovedForAll(from, operator),
            "ERC1155: caller is not token owner nor approved"
        );
        require(to != address(0), "ERC1155: transfer to the zero address");
        // from地址有足够持仓
        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);    
    }

    // 批量安全转账,将`amounts`数组单位的`ids`数组种类代币从`from`转账到`to`
    function safeBatchTransferFrom(
        address from, address to, uint256[] memory ids, 
        uint256[] memory amounts, bytes memory data) 
        public virtual override {
    
    
        address operator = msg.sender;
        // 调用者是持有者或是被授权
        require(
            from == operator || isApprovedForAll(from, operator),
            "ERC1155: caller is not token owner nor approved"
        );
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        // 通过for循环更新持仓  
        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 virtual {
    
    
        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 virtual {
    
    
        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 virtual {
    
    
        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 virtual {
    
    
        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 (to.isContract()) {
    
    
            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");
            }
        }
    }

    // ERC1155的批量安全转账检查
    function _doSafeBatchTransferAcceptanceCheck( 
        address operator, address from, address to,
        uint256[] memory ids, uint256[] memory amounts, bytes memory data) private {
    
    
        if (to.isContract()) {
    
    
            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");
            }
        }
    }

    // 返回id种类代币的uri,存储metadata,类似ERC721的tokenURI.
    function uri(uint256 id)  public view virtual override returns (string memory) {
    
    
        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, id.toString())) : "";
    }

    // 计算{uri}的BaseURI,uri就是把baseURI和tokenId拼接在一起,需要开发重写.
    function _baseURI() internal view virtual returns (string memory) {
    
    
        return "";
    }
}

6、NFT1155

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


import "./ERC1155.sol"; 

contract NFT1155 is ERC1155 {
    
    
    uint256 constant MAX_ID = 10000;

    constructor() ERC1155("NFT1155", "NFT1155"){
    
    

    }

    function _baseURI() internal pure override returns (string memory) {
    
    
        return "ipfs://QmcPGA3kecSMfHcv8i6wpgB2RkPA1FSVqu6AZY3rKNmxuj/";
    }

    function mint(address to, uint256 id, uint256 amount) external {
    
    
        require(id < MAX_ID, "id overflow");
        _mint(to, id, amount, "");
    }

    function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts) external {
    
    
        for(uint256 i = 0; i < ids.length;i++){
    
    
            require(ids[i] < MAX_ID, "id overflow");
        }
        _mintBatch(to, ids, amounts, "");
    }
}

Test run:
Insert image description here

7. Open source address

gitee open source address.


おすすめ

転載: blog.csdn.net/weixin_61361738/article/details/134783427