探索如何在武漢鏈(基於ETH)的一個合約中實現同質化與非同質化功能
id:BSN_2021
公眾號:BSN研習社
目標:跟大家一塊去研究下1155標準中提供的案例
章節流程:
-
核心檔案
-
核心方法
-
彙總
在eip-1155中看到如下圖所示一段內容,並提供了一個案例,因此今天跟大家去研究一下內部的實現細節。
一、核心檔案
其主要涉及兩個檔案 ERC1155MixedFungibleMintable.sol和 ERC1155MixedFungible .sol,如下:
1.檔案 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進行對映。
-
示例:
非同質化條件: 100xxx00 1 000xxx000 & TYPE_NF_BIT == TYPE_NF_BIT ;
生成tokenId的規則:id = _type | index +i。即 100xxx00 1 000xxx000 | 001 => 100xxx00 1 000xxx00 1 ;
-
鑄造同質化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。
引用資源地址:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70012206/viewspace-2886924/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- ETH合約功能遷移至BSN武漢鏈對接說明
- FC 2022 | 基於博弈論分析的非同質化代幣證券化與回購市場
- matic馬蹄鏈質押合約代幣系統開發方案功能
- 基於星雲鏈的智慧合約與Dapp(一)——編譯安裝星雲鏈APP編譯
- Flutter 設計 使您的主題同質化Flutter
- 基於XR Interaction ToolKit與PUN實現VR多人協同功能VR
- 探索:優雅地實現非同步方法的並行化非同步並行
- 質數與約數
- Dapp鏈上代幣合約質押模式系統開發功能分析丨框架APP模式框架
- GoCardless提升資料質量與實施資料合約的7個關鍵經驗Go
- 智慧合約NFT鏈上質押模式系統開發案例模式
- BSC鏈智慧合約質押分紅系統開發方案
- BTC位元鏈丨ETH以太鏈丨TRX波場鏈丨BSC波場鏈丨OP鏈發行代幣合約質押模式挖礦系統開發模式
- 中移鏈合約常用開發介紹(三)工程化開發智慧合約
- 能合約DAPP鏈上質押挖礦分紅系統開發實現技術案例APP
- 醫動力Android基於CC元件化框架的探索與實踐Android元件化框架
- BNB鏈合約雙幣質押流動性挖礦系統功能開發(Solidity原理)Solid
- 基於RMAN實現壞塊介質恢復(blockrecover)BloC
- polygon馬蹄鏈質押DApp開發合約系統搭建GoAPP
- 基於鑽孔資料的三維地質模型視覺化模型視覺化
- 質量功能展開:一個被埋沒的寶藏質量工具!
- 智慧合約LP池質押挖礦系統開發功能案例
- DAPP合約質押理財系統開發功能分析APP
- [質量管理] 《製造業質量管理數字化實施指南》
- DApp智慧合約質押挖礦開發功能詳情繫統開發技術與實踐APP
- 如何基於Django中的WebSockets和非同步檢視來實現實時通訊功能DjangoWeb非同步
- Ptahdao智慧合約的流動性質押挖礦系統功能開發
- 【JAVA】助力數字化營銷:基於協同過濾演算法實現個性化商品推薦Java演算法
- 同質化的3A遊戲背後,隱藏著怎樣的行業現象?遊戲行業
- DAPP鏈上合約質押挖礦分紅開發原理丨DAPP鏈上合約質押挖礦分紅系統開發邏輯及方案APP
- 在後臺框架同質化的今天,我是如何思考並做出差異化的框架
- OP鏈DAPP合約代幣質押挖礦系統開發APP
- NFT鏈上合約質押理財系統開發技術分析
- 第三章 通過java SDK 實現個性化智慧合約的部署與測試Java
- 美圖個性化推薦的實踐與探索
- 中移鏈系統合約管控功能介紹
- 基於區塊鏈技術的FDF智慧合約迴圈互助遊戲開發實現區塊鏈遊戲開發
- 模擬5億年的進化資訊,首個同時推理蛋白質序列、結構和功能的生物學大模型大模型