乐趣区

关于区块链:探索如何在武汉链基于ETH的一个合约中实现同质化与非同质化功能

id:BSN_2021
公众号:BSN 研习社

指标:跟大家一块去钻研下 1155 规范中提供的案例
章节流程:外围文件

外围办法

汇总

在 eip-1155 中看到如下图所示一段内容,并提供了一个案例,因而明天跟大家去钻研一下外部的实现细节。

一、外围文件
其次要波及两个文件 ERC1155MixedFungibleMintable.sol 和 ERC1155MixedFungible .sol,如下:

文件 ERC1155MixedFungible .sol 内容如下:


pragma solidity ^0.5.0;

import "./ERC1155.sol";

/**
    @dev Extension to ERC1155 for Mixed Fungible and Non-Fungible Items support
    The main benefit is sharing of common type information, just like you do when
    creating a fungible id.
*/
contract ERC1155MixedFungible is ERC1155 {


    // Use a split bit implementation. Store the type in the upper 128 bits..
    // 十进制:115792089237316195423570985008687907852929702298719625575994209400481361428480
    // 十六进制:ffffffffffffffffffffffffffffffff00000000000000000000000000000000
    // 二进制:// 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
    // 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    uint256 public constant TYPE_MASK = uint256(uint128(~0)) << 128;


    // ..and the non-fungible index in the lower 128
    // 十进制:340282366920938463463374607431768211455
    // 十六进制:ffffffffffffffffffffffffffffffff
    // 二进制: 
    // 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    // 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
    uint256 public constant NF_INDEX_MASK = uint128(~0);


    // The top bit is a flag to tell if this is a NFI.
    // 十进制: 57896044618658097711785492504343953926634992332820282019728792003956564819968
    // 十六进制: 8000000000000000000000000000000000000000000000000000000000000000
    // 二进制:
    // 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    // 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    uint256 public constant TYPE_NF_BIT = 1 << 255;


    mapping (uint256 => address) nfOwners;

    // Only to make code clearer. Should not be functions
    function isNonFungible(uint256 _id) public pure returns(bool) {return _id & TYPE_NF_BIT == TYPE_NF_BIT;}
    function isFungible(uint256 _id) public pure returns(bool) {return _id & TYPE_NF_BIT == 0;}
    function getNonFungibleIndex(uint256 _id) public pure returns(uint256) {return _id & NF_INDEX_MASK;}
    function getNonFungibleBaseType(uint256 _id) public pure returns(uint256) {return _id & TYPE_MASK;}
    function isNonFungibleBaseType(uint256 _id) public pure returns(bool) {
        // A base type has the NF bit but does not have an index.
        return (_id & TYPE_NF_BIT == TYPE_NF_BIT) && (_id & NF_INDEX_MASK == 0);
    }
    function isNonFungibleItem(uint256 _id) public pure returns(bool) {
        // A base type has the NF bit but does has an index.
        return (_id & TYPE_NF_BIT == TYPE_NF_BIT) && (_id & NF_INDEX_MASK != 0);
    }
    function ownerOf(uint256 _id) public view returns (address) {return nfOwners[_id];
    }

    // override
    function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external {require(_to != address(0x0), "cannot send to zero address");
        require(_from == msg.sender || operatorApproval[_from][msg.sender] == true, "Need operator approval for 3rd party transfers.");

        if (isNonFungible(_id)) {require(nfOwners[_id] == _from);
            nfOwners[_id] = _to;
            // You could keep balance of NF type in base type id like so:
            // uint256 baseType = getNonFungibleBaseType(_id);
            // balances[baseType][_from] = balances[baseType][_from].sub(_value);
            // balances[baseType][_to]   = balances[baseType][_to].add(_value);
        } else {balances[_id][_from] = balances[_id][_from].sub(_value);
            balances[_id][_to]   = balances[_id][_to].add(_value);
        }

        emit TransferSingle(msg.sender, _from, _to, _id, _value);

        if (_to.isContract()) {_doSafeTransferAcceptanceCheck(msg.sender, _from, _to, _id, _value, _data);
        }
    }

    // override
    function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external {require(_to != address(0x0), "cannot send to zero address");
        require(_ids.length == _values.length, "Array length must match");

        // Only supporting a global operator approval allows us to do only 1 check and not to touch storage to handle allowances.
        require(_from == msg.sender || operatorApproval[_from][msg.sender] == true, "Need operator approval for 3rd party transfers.");

        for (uint256 i = 0; i < _ids.length; ++i) {
            // Cache value to local variable to reduce read costs.
            uint256 id = _ids[i];
            uint256 value = _values[i];

            if (isNonFungible(id)) {require(nfOwners[id] == _from);
                nfOwners[id] = _to;
            } else {balances[id][_from] = balances[id][_from].sub(value);
                balances[id][_to]   = value.add(balances[id][_to]);
            }
        }

        emit TransferBatch(msg.sender, _from, _to, _ids, _values);

        if (_to.isContract()) {_doSafeBatchTransferAcceptanceCheck(msg.sender, _from, _to, _ids, _values, _data);
        }
    }

    function balanceOf(address _owner, uint256 _id) external view returns (uint256) {if (isNonFungibleItem(_id))
            return nfOwners[_id] == _owner ? 1 : 0;
        return balances[_id][_owner];
    }

    function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory) {require(_owners.length == _ids.length);

        uint256[] memory balances_ = new uint256[](_owners.length);

        for (uint256 i = 0; i < _owners.length; ++i) {uint256 id = _ids[i];
            if (isNonFungibleItem(id)) {balances_[i] = nfOwners[id] == _owners[i] ? 1 : 0;
            } else {balances_[i] = balances[id][_owners[i]];
            }
        }

        return balances_;
    }
}

2. 文件 ERC1155MixedFungibleMintable.sol 内容如下:

pragma solidity ^0.5.0;

import "./ERC1155MixedFungible.sol";

/**
    @dev Mintable form of ERC1155
    Shows how easy it is to mint new items
*/
contract ERC1155MixedFungibleMintable is ERC1155MixedFungible {

    uint256 nonce;
    mapping (uint256 => address) public creators;
    mapping (uint256 => uint256) public maxIndex;

    modifier creatorOnly(uint256 _id) {require(creators[_id] == msg.sender);
        _;
    }

    // This function only creates the type.
    function create(
        string calldata _uri,
        bool   _isNF)
    external returns(uint256 _type) {

        // Store the type in the upper 128 bits
        _type = (++nonce << 128);

        // Set a flag if this is an NFI.
        if (_isNF)
          _type = _type | TYPE_NF_BIT;

        // This will allow restricted access to creators.
        creators[_type] = msg.sender;

        // emit a Transfer event with Create semantic to help with discovery.
        emit TransferSingle(msg.sender, address(0x0), address(0x0), _type, 0);

        if (bytes(_uri).length > 0)
            emit URI(_uri, _type);
    }

    function mintNonFungible(uint256 _type, address[] calldata _to) external creatorOnly(_type) {

        // No need to check this is a nf type rather than an id since
        // creatorOnly() will only let a type pass through.
        require(isNonFungible(_type));

        // Index are 1-based.
        uint256 index = maxIndex[_type] + 1;
        maxIndex[_type] = _to.length.add(maxIndex[_type]);

        for (uint256 i = 0; i < _to.length; ++i) {address dst = _to[i];
            uint256 id  = _type | index + i;

            nfOwners[id] = dst;

            // You could use base-type id to store NF type balances if you wish.
            // balances[_type][dst] = quantity.add(balances[_type][dst]);

            emit TransferSingle(msg.sender, address(0x0), dst, id, 1);

            if (dst.isContract()) {_doSafeTransferAcceptanceCheck(msg.sender, msg.sender, dst, id, 1, '');
            }
        }
    }

    function mintFungible(uint256 _id, address[] calldata _to, uint256[] calldata _quantities) external creatorOnly(_id) {require(isFungible(_id));

        for (uint256 i = 0; i < _to.length; ++i) {address to = _to[i];
            uint256 quantity = _quantities[i];

            // Grant the items to the caller
            balances[_id][to] = quantity.add(balances[_id][to]);

            // Emit the Transfer/Mint event.
            // the 0x0 source address implies a mint
            // It will also provide the circulating supply info.
            emit TransferSingle(msg.sender, address(0x0), to, _id, quantity);

            if (to.isContract()) {_doSafeTransferAcceptanceCheck(msg.sender, msg.sender, to, _id, quantity, '');
            }
        }
    }
}

二、外围办法
先看一下几个内置的状态变量,如下:
TYPE_NF_BIT= 100xxx000 000xxx000
TYPE_MASK= 111xxx111 000xxx000
NF_INDEX_MASK=000xxx000 111xxx111

次要的几个办法,详解如下:

创立类型 create(string calldata _uri,bool _isNF):

要害逻辑:首先,通过 nonce 自增而后左移 128 位生成_type 类别惟一标识(tokenid)。接下来,判断是否是非同质化,是则与 TYPE_NF_BIT 按位或(注:其后果最高位标识为 1 结尾),否则间接返回_type。

示例:前 128 位,后 128 位。
nft_type:100xxx001 000xxx000;
ft_type: 000xxx001 000xxx000;

铸造非同质化 mintNonFungible(uint256 _type, address[] calldata _to)

要害逻辑:首先,要求_type 合乎非同质化条件,即判断_type 按位与 TYPE_NF_BIT 是否等于 TYPE_NF_BIT。接下来,获取最新的索引值 index,而后依据规定(将_type 与 index+ i 按位或)生成惟一标识,并与对应的 address 进行映射。

示例:
非同质化条件:100xxx001 000xxx000 & TYPE_NF_BIT == TYPE_NF_BIT;
生成 tokenId 的规定:id = _type | index +i。即 100xxx001 000xxx000 | 001 => 100xxx001 000xxx001;

铸造同质化 mintFungible(uint256 _id, address[] calldata _to, uint256[] calldata _quantities)

要害逻辑:首先,要求合乎同质化条件,即判断_type 按位与 TYPE_NF_BIT 是否等于 0;接下来,计算最新的数量,并与对应的 address 进行映射。

示例:
同质化条件:000xxx001 000xxx000 & TYPE_NF_BIT == 0;

转移 safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data)

要害逻辑:依据_id 判断是否是非同质化标识,即判断_type 按位与 TYPE_NF_BIT 是否等于 TYPE_NF_BIT,而后返回对应的后果。

获取余额 balanceOf(address _owner, uint256 _id) external view returns (uint256)

要害逻辑:依据_id 判断是否是非同质化标识,即判断_id 按位与 TYPE_NF_BIT 是否等于 TYPE_NF_BIT,并且_id 按位与 NF_INDEX_MASK 等于零。而后返回对应的后果。注:非同质化的数量为 0 或 1;

三、汇总
要害的几个办法看完后,能够发现技术上没有太大的难度,次要是使用了标识位拆分(Split ID bits)以及位运算进行奇妙的设计。行将 tokenid uint256 分为两局部(前 128 位和后 128 位)。当非同质化时前 128 位标识类型,前面 128 为代表索引或 id,即 <uint128: base token id><uint128: index of non-fungible>。当同质化时前 128 位标识 id,后 128 位为零,即 <uint128: base token id><uint128: zero>;

艰深点的来说,当调用 create 办法时,非同质化生成的为类别(或系列)标识,而后再生成该类别下的惟一标识,即 nf-tokenID。同质化生成的即为惟一标识, 即 f -tokenID。

援用资源地址:
https://eips.ethereum.org/EIP…
https://github.com/enjin/erc-…

退出移动版