以太坊应用层标准提案之ERC-1155

一 什么是ERC-1155, 为什么需要ERC-1155?

1.1 ERC-20或者ERC-721存在什么问题

1.1.1 部署效率低

无论是ERC-20同质化代币还是ERC-721非同质化代币,只允许部署一个Token合约,如果想部署多个Token合约,那么必须进行多次部署

1.1.2 操作效率低

有些时候,想批量转移多个Token给其他地址,只能不同的Token一个一个转,操作效率低下

1.1.3 交易成本高

有些时候,想转移多个Token给其他地址,只能不同的Token一个一个转,那就需要调用多次交易函数,因此交易成本也高

1.2 什么是ERC-1155

ERC-1155允许在单个合约中创建多个同质化代币或者非同质化代币,允许批量转账多个代币,并且可以保证操作的原子性,提升了操作效率,减少了Gas费用,丰富了使用场景

二 ERC-1155提案标准内容

我们知道,ERC-1155其实创建一个智能合约接口,可以代表和控制任何数量的同质化和非同质化代币类型。 这样一来,ERC-1155 代币就具有与 ERC-20 和 ERC-721 代币相同的功能,甚至可以同时使用这两者的功能

2.1 函数

2.1.1 balanceOf

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

根据用户地址和token id查询余额

2.1.2 balanceOfBatch

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

批量查询多个地址和多个代币ID的余额,并且account数组和id数组是一一对应的

2.1.3 setApprovalForAll

function setApprovalForAll(address operator, bool approved) external;

设置或撤销对某个操作员的授权,允许其代表用户管理所有代币

2.1.4 isApprovedForAll

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

检查某个操作员是否被授权管理用户的所有代币

2.1.5 safeTransferFrom

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

安全转移特定数量的代币到目标地址

2.1.6 safeBatchTransferFrom

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

安全批量转移特定数量的多个代币到目标地址,id数组和value数组也是一一对应的

2.2 事件

2.2.1 TransferSingle

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

2.2.2 TransferBatch

    event TransferBatch(

        address indexed operator,

        address indexed from,

        address indexed to,

        uint256[] ids,

        uint256[] values

    );

2.2.3 ApprovalForAll

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

三 ERC-1155有什么特点

3.1 单一合约部署

因为ERC-1155 允许在同一个合约中创建多种代币类型,减少了要部署的合约数量,增加了部署效率,降低了部署的复杂性和成本。

3.2 支持部署多个币种

之前ERC-20和ERC-721只是支持部署单个代币,现在可以一下子部署多个代币类型

3.3 高效的批量转移

ERC-1155支持批量转移,因此提升了转移的效率

3.4 交易原子性

ERC-1155 支持原子交换,允许在单个交易中同时交换多个代币,确保所有交换要么全部成功,要么全部失败

3.5 降低Gas费用,节省成本

通过允许批量转移和在同一个合约中创建多种代币类型,ERC-1155 相较于为每种代币类型使用单独合约可以帮助降低 Gas 成本

3.6 向后兼容

ERC-1155 设计为向后兼容 ERC-20 和 ERC-721 标准,便于与现有的代币标准和基础设施集成

3.7互操作性

ERC-1155 代币可以在支持该标准的不同平台和应用程序中使用,增加了以太坊生态系统中代币的互操作性

四 手动实现ERC-1155

interface IERC165 {
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
abstract contract ERC165 is IERC165 {

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

    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);

    /*
     * Queries the balance of tokens by account address and token ID
     * @param account The address of the account to query
     * @param id The ID of the token to query
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /*
     * Batch queries the balance of multiple accounts and multiple token IDs. Each address in the accounts array corresponds to a token ID in the ids array.
     * The balanceOfBatch function returns an array where each element represents the balance of the corresponding address and token ID.
     * @param accounts The array of account addresses to query
     * @param ids The array of token IDs to query
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);

    /*
     * Grants or revokes permission for an operator to manage all tokens of the owner
     * @param operator The address of the operator to grant or revoke permission
     * @param approved A boolean value, true to grant permission, false to revoke permission
     */
    function setApprovalForAll(address operator, bool approved) external;

    /*
     * Checks if an operator is authorized to manage all tokens of a specific account
     * @param account The address of the account to check
     * @param operator The address of the operator to check
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /*
     * Safely transfers a specified amount of a token to a target address
     * @param from The address from which the tokens are transferred
     * @param to The address to which the tokens are transferred
     * @param id The ID of the token to transfer
     * @param amount The amount of tokens to transfer
     * @param data Additional data, which may be used to call special operations on the receiving contract
     */
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    /*
     * Safely batch transfers specified amounts of specified token IDs to a target address. The ids array and values array correspond one-to-one.
     * @param from The address from which the tokens are transferred
     * @param to The address to which the tokens are transferred
     * @param ids The array of token IDs to transfer
     * @param values The array of amounts of tokens to transfer, each corresponding to the respective ID in the ids array
     * @param data Additional data, which may be used to call special operations on the receiving contract
     */
    function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) external;
}
interface IERC1155Errors {

    error ERC1155InsufficientBalance(address from, uint256 balance, uint256 needed, uint256 tokenId);

    error ERC1155InvalidSender(address from);

    error ERC1155InvalidReceiver(address to);

    error ERC1155MissingApprovalForAll(address operator, address owner);

    error ERC1155InvalidApprover(address approver);

    error ERC1155InvalidOperator(address operator);

    error ERC1155InvalidArrayLength();
}
interface IERC1155Receiver is IERC165 {


    function onERC1155Received(
        address operator, 
        address from, 
        uint256 id, 
        uint256 amount, 
        bytes calldata data
    ) external view returns (bytes4); 


    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external returns (bytes4);
}
abstract contract ERC1155 is IERC1155, IERC1155Errors {
    string private _uri;
    mapping(uint256 id => mapping(address account => uint256)) _balances;
    mapping(address account => mapping(address operator => bool)) _approvals;

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

    function balanceOf(
        address account,
        uint256 id
    ) public view virtual returns (uint256) {
        return _balances[id][account];
    }

    function balanceOfBatch(
        address[] memory accounts,
        uint256[] memory ids
    ) public view virtual returns (uint256[] memory) {
        if (accounts.length != ids.length) {
            revert ERC1155InvalidArrayLength(ids.length, accounts.length);
        }

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

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

        return batchBalances;
    }

    function balanceOfBatch(
        address[] memory accounts,
        uint256[] memory ids
    ) public view virtual returns (uint256[] memory) {
        if (accounts.length != ids.length) revert ERC1155InvalidArrayLength();

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

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

        return batchBalances;
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        _setApprovalForAll(msg.sender, operator, approved);
    }

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

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 value,
        bytes memory data
    ) public virtual {
        if (from != msg.sender && !isApprovedForAll(from, msg.sender)) {
            revert ERC1155MissingApprovalForAll(msg.sender, from);
        }

        _safeTransferFrom(from, to, id, value, data);
    }

    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory values,
        bytes memory data
    ) public virtual {
        if (from != sender && !isApprovedForAll(from, sender)) {
            revert ERC1155MissingApprovalForAll(sender, from);
        }
        _safeBatchTransferFrom(from, to, ids, values, data);
    }

    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal {
        if (to == address(0)) {
            revert ERC1155InvalidReceiver(address(0));
        }
        if (from == address(0)) {
            revert ERC1155InvalidSender(address(0));
        }

        uint256[] memory ids = new uint256[](1);
        ids[0] = id;
        uint256[] memory amounts = new uint256[](1);
        amounts[0] = amount;

        _checkSafeTransfer(msg.sender, from, to, id, amount, "");
        _update(from, to, ids, amounts);
    }

    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory values,
        bytes memory data
    ) internal {
        if (to == address(0)) {
            revert ERC1155InvalidReceiver(address(0));
        }

        if (from == address(0)) {
            revert ERC1155InvalidSender(address(0));
        }
        _checkSafeBatchTransfer(msg.sender, from, to, ids, amounts, "");
        _update(from, to, ids, amounts);
    }

    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        if (operator == address(0)) revert ERC1155InvalidOperator(address(0));
        _approvals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /*
     * mint
     * @param to
     * @param id
     * @param amount
     */
    function _mint(address to, uint256 id, uint256 amount) internal {
        if (to == address(0)) revert ERC1155InvalidReceiver(address(0));
        uint256[] memory ids = new uint256[](1);
        ids[0] = id;
        uint256[] memory amounts = new uint256[](1);
        amounts[0] = amount;
        _checkSafeTransfer(msg.sender, address(0), to, id, amount, "");
        _update(address(0), to, ids, amounts);
    }

    /*
     * batch mint
     * @param to
     * @param ids
     * @param amounts
     * @param data
     */
    function _batchMint(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal {
        if (to == address(0)) revert ERC1155InvalidReceiver(address(0));
        _checkSafeBatchTransfer(msg.sender, address(0), to, ids, amounts, "");
        _update(address(0), to, ids, amounts);
    }

    /*
     * burn token
     * @param from
     * @param id
     * @param amount
     */
    function _burn(address from, uint256 id, uint256 amount) internal {
        if (from == address(0)) revert ERC1155InvalidSender(address(0));
        uint256[] memory ids = new uint256[](1);
        ids[0] = id;
        uint256[] memory amounts = new uint256[](1);
        amounts[0] = amount;
        _checkSafeTransfer(msg.sender, from, address(0), id, amount, "");
        _update(from, address(0), ids, amounts);
    }

    /*
     * batch burn multiple token
     * @param from
     * @param ids
     * @param amounts
     * @param data
     */
    function _batchBurn(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal {
        if (from == address(0)) revert ERC1155InvalidSender(address(0));
        _checkSafeBatchTransfer(msg.sender, from, address(0), ids, amounts, "");
        _update(from, address(0), ids, amounts);
    }

    /*
     * update or batch update transfer two parties' balances
     * @param from
     * @param to
     * @param ids
     * @param amounts
     */
    function _update(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        if (ids.length != amounts.length) revert ERC1155InvalidArrayLength();

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];
            if (from != address(0)) {
                uint256 fromBalance = _balances[id][from];
                if (fromBalance < amount) {
                    revert ERC1155InsufficientBalance(
                        from,
                        fromBalance,
                        amount,
                        id
                    );
                }
                unchecked {
                    _balances[id][from] = fromBalance - amount;
                }
            }

            if (to != address(0)) {
                _balances[id][to] += amount;
            }
        }

        // send transfer event
        if (ids.length == 1) {
            uint256 id = ids[0];
            uint256 amount = amounts[0];
            emit TransferSingle(msg.sender, from, to, id, amount);
        } else {
            emit TransferBatch(msg.sender, from, to, ids, amounts);
        }
    }

    /*
     * Check if contract is able to  transfer while the receiver is contract
     * @param operator
     * @param from
     * @param to
     * @param id
     * @param amount
     * @param data
     */
    function _checkSafeTransfer(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal {
        if (!(to.code.length > 0)) return;

        try
            IERC1155Receiver(to).onERC1155Received(
                operator,
                from,
                id,
                amount,
                data
            )
        returns (bytes4 selector) {
            if (selector != IERC1155Receiver.onERC1155Received.selector) {
                revert ERC1155InvalidReceiver(to);
            }
        } catch (bytes memory reason) {
            // bytes is a dynamic array type, first 32 bit is array length
            // mload(reason): read the reason first 32 byte length that means the actual error data
            // add(32, reason): reason point to start location in the memory, So here means message offset which should be revert
            assembly {
                revert(add(32, reason), mload(reason))
            }
        }
    }

    /*
     * Check if contract is able to batch transfer while the receiver is contract
     * @param operator
     * @param from
     * @param to
     * @param ids
     * @param amounts
     * @param data
     */
    function _checkSafeBatchTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal {
        if (!(to.code.length > 0)) return;

        try
            IERC1155Receiver(to).onERC1155BatchReceived(
                operator,
                from,
                ids,
                amounts,
                data
            )
        returns (bytes4 selector) {
            if (selector != IERC1155Receiver.onERC1155BatchReceived.selector) {
                revert ERC1155InvalidReceiver(to);
            }
        } catch (bytes memory reason) {
            // bytes is a dynamic array type, first 32 bit is array length
            // mload(reason): read the reason first 32 byte length that means the actual error data
            // add(32, reason): reason point to start location in the memory, So here means message offset which should be revert
            assembly {
                revert(add(32, reason), mload(reason))
            }
        }
    }
}

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫言静好、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值