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-...