您好,登錄后才能下訂單哦!
本篇內容介紹了“區塊鏈怎么實現以太坊通證的多簽合約”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
有一天,老板給輝哥提了一個需求,希望能夠實現一個安全的代幣支出多簽功能,便于基金會治理審核。
匯總而言,產品需求描述如下:
(1)治理委員會由4人組成,一項CLB代幣支出需要所有4人同意才可以轉賬打出; (2)為了避嫌中心化作惡,治理委員會的退出只能由委員本人賬號操作有效; (3)當治理委員會無法正常運作時,老板有權撤回治理委員會管理的剩余代幣。
輝哥首先完成市場上商用硬件錢包的調研工作。
結論:
目前(2018.08.24)世面上暫時沒有體驗好的多簽硬錢包,不過等1個月后比特派硬件錢包更新會支持ETH和ERC20代幣的多重簽名功能。
具體信息:
目前沒有支持ETH和ERC20多簽功能的冷錢包。 市面上有較多錢包支持比特幣多簽功能的。 ImToken錢包不支持ETH及ERC20代幣的多簽管理,但支持離線授權管理。 國內庫神硬件錢包也不支持ETH多簽,價格大概在2000元左右。 國外的Trezor價格大概在800元左右。 比特派(bitpay)錢包答復9月份會發布一款硬件錢包支持ETH和ERC20多簽功能的冷錢包,價位1299元左右。 商品鏈接可點擊訪問。
律師推薦的gnosis多簽錢包是英文網頁版操作的。gnosis多簽錢包合約代碼可點擊訪問。
支持ETH多簽合約的PC版錢包還有Mist錢包,PARITY錢包,是英文頁面操作。
目前很多基金會直接使用多簽合約代碼部署后支持在頁面進行多簽管理。
多簽合約核心代碼如下:
contract ColorbayMultiSign { using SafeMath for uint256; uint256 public MAX_OWNER_COUNT = 50; event Confirmation(address indexed sender, uint256 indexed transactionId); event Revocation(address indexed sender, uint256 indexed transactionId); event Submission(uint256 indexed transactionId); event Execution(uint256 indexed transactionId); event ExecutionSuccess(uint256 indexed transactionId); event ExecutionFailure(uint256 indexed transactionId); event OwnerAddition(address indexed owner); event OwnerRemoval(address indexed owner); event RequirementChange(uint256 required); mapping (uint256 => Transaction) public transactions; mapping (uint256 => mapping(address => bool)) public confirmations; mapping (address => bool) public isOwner; address[] public owners; uint256 public required; uint256 public transactionCount; address public creator; ERC20 public token; struct Transaction { address destination; uint256 value; bool executed; } /*omit some modifier function*/ /** * @dev Contract constructor sets initial owners and required number of confirmations. * @param _owners List of initial owners. * @param _required Number of required confirmations. */ constructor(address _token, address[] _owners, uint256 _required) public validRequirement(_owners.length, _required) { token = ERC20(_token); require(_owners.length <= 100); for (uint256 i=0; i<_owners.length; i++) { require(!isOwner[_owners[i]] && _owners[i] != address(0)); isOwner[_owners[i]] = true; } owners = _owners; required = _required; creator = msg.sender; } /** * @dev Allows to remove an owner. Transaction has to be sent by wallet. * @param owner Address of owner. */ function removeOwner(address owner) public ownerExists(owner) { /*only owner can delete itself*/ require(owner == msg.sender); isOwner[owner] = false; for (uint256 i=0; i<owners.length.sub(1); i++) { if (owners[i] == owner) { owners[i] = owners[owners.length.sub(1)]; break; } } owners.length = owners.length.sub(1); if (required > owners.length) { changeRequirement(owners.length); } emit OwnerRemoval(owner); } /** * @dev Withdraw the token remained to the constructor address. */ function withdrawToken() public onlyCreator{ if( 0 < token.balanceOf(address(this))) { token.transfer(creator, token.balanceOf(address(this))); } } /** * @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet. * @param _required Number of required confirmations. */ function changeRequirement(uint256 _required) private validRequirement(owners.length, _required) { required = _required; emit RequirementChange(_required); } /** * @dev Allows an owner to submit and confirm a transaction. * @param destination Transaction target address. * @param value Transaction ether value. * @return Returns transaction ID. */ function submitTransaction(address destination, uint256 value) public returns (uint256 transactionId) { transactionId = addTransaction(destination, value); confirmTransaction(transactionId); } /** * @dev Allows an owner to confirm a transaction. * @param transactionId Transaction ID. */ function confirmTransaction(uint256 transactionId) public ownerExists(msg.sender) transactionExists(transactionId) notConfirmed(transactionId, msg.sender) { confirmations[transactionId][msg.sender] = true; emit Confirmation(msg.sender, transactionId); executeTransaction(transactionId); } /** * @dev Allows an owner to revoke a confirmation for a transaction. * @param transactionId Transaction ID. */ function revokeConfirmation(uint256 transactionId) public ownerExists(msg.sender) confirmed(transactionId, msg.sender) notExecuted(transactionId) { confirmations[transactionId][msg.sender] = false; emit Revocation(msg.sender, transactionId); } /** * @dev Allows anyone to execute a confirmed transaction. * @param transactionId Transaction ID. */ function executeTransaction(uint256 transactionId) public notExecuted(transactionId) { if (isConfirmed(transactionId)) { Transaction storage ta = transactions[transactionId]; ta.executed = true; if(token.transfer(ta.destination, ta.value)) { emit ExecutionSuccess(transactionId); } else { emit ExecutionFailure(transactionId); ta.executed = false; } } } /** * @dev Returns the confirmation status of a transaction. * @param transactionId Transaction ID. * @return Confirmation status. */ function isConfirmed(uint256 transactionId) public view returns (bool) { uint256 count = 0; for (uint256 i=0; i<owners.length; i++) { if (confirmations[transactionId][owners[i]]) { count = count.add(1); } if (count == required) { return true; } } } /** * @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet. * @param destination Transaction target address. * @param value Transaction ether value. * @return Returns transaction ID. */ function addTransaction(address destination, uint256 value) internal notNull(destination) returns (uint256 transactionId) { transactionId = transactionCount; transactions[transactionId] = Transaction({ destination: destination, value: value, executed: false }); transactionCount = transactionCount.add(1); emit Submission(transactionId); } /** * Web3 call functions * @dev Returns number of confirmations of a transaction. * @param transactionId Transaction ID. * @return Number of confirmations. */ function getConfirmationCount(uint256 transactionId) public view returns (uint256 count) { for (uint256 i=0; i<owners.length; i++) { if (confirmations[transactionId][owners[i]]) { count = count.add(1); } } } /** * @dev Returns total number of transactions after filers are applied. * @param pending Include pending transactions. * @param executed Include executed transactions. * @return Total number of transactions after filters are applied. */ function getTransactionCount(bool pending, bool executed) public view returns (uint256 count) { for (uint256 i=0; i<transactionCount; i++) { if (pending && !transactions[i].executed || executed && transactions[i].executed) { count = count.add(1); } } } /** * @dev Returns list of owners. * @return List of owner addresses. */ function getOwners() public view returns (address[]) { return owners; } /** * @dev Returns array with owner addresses, which confirmed transaction. * @param transactionId Transaction ID. * @return Returns array of owner addresses. */ function getConfirmations(uint256 transactionId) public view returns (address[] _confirmations) { address[] memory confirmationsTemp = new address[](owners.length); uint256 count = 0; for (uint256 i=0; i<owners.length; i++) { if (confirmations[transactionId][owners[i]]) { confirmationsTemp[count] = owners[i]; count = count.add(1); } } _confirmations = new address[](count); for (i=0; i<count; i++) { _confirmations[i] = confirmationsTemp[i]; } } /** * @dev Returns list of transaction IDs in defined range. * @param from Index start position of transaction array. * @param to Index end position of transaction array. * @param pending Include pending transactions. * @param executed Include executed transactions. * @return Returns array of transaction IDs. */ function getTransactionIds(uint256 from, uint256 to, bool pending, bool executed) public view returns (uint256[] _transactionIds) { uint256[] memory transactionIdsTemp = new uint256[](transactionCount); uint256 count = 0; for (uint256 i=0; i<transactionCount; i++) { if (pending && !transactions[i].executed || executed && transactions[i].executed) { transactionIdsTemp[count] = i; count = count.add(1); } } _transactionIds = new uint256[](to.sub(from)); for (i=from; i<to; i++) { _transactionIds[i.sub(from)] = transactionIdsTemp[i]; } } }
各個函數的定義說明參考類圖:
多簽合約類圖
核心函數說明:
constructor(address _token, address[] _owners, uint256 _required) public validRequirement(_owners.length, _required) 多簽創建函數,參數分別為通證的地址,多簽賬戶的地址,需要幾個賬戶多簽;
function submitTransaction(address destination, uint256 value) public 提交轉賬申請(目標賬戶和金額),任何人都可以發起;如果是委員會委員者發起則同時完成審批;
function confirmTransaction(uint256 transactionId) public 委員會委員審批通過
function revokeConfirmation(uint256 transactionId) public 在轉賬成功前,已審核的委員會可撤銷審核授權
function removeOwner(address owner) public ownerExists(owner) 刪除治理委員賬戶,只有自己能操作,已防止他人作惡。
function withdrawToken() public onlyCreator 打回通證,只有合約創建者能操作
多簽智能合約場景測試
編譯通過后,按照實際業務場景,輝哥做了一下完整測試。測試流程如下:
業務流程
具體的操作流程如下,均達到預期目標,本多簽合約具備商用能力。
測試數據
代幣
彩貝發行總量為 1,000,000,000 個token
激勵 占比35%,即350,000,000個token
私募 占比20%,即200,000,000個token
團隊及基金會 占比25%,即250,000,000個token
社區培養及推廣 占比20%,即200,000,000個token
賬號
治理審批委員會成員:
老板 0xca3...a733c
輝哥 0x147...c160c
歐陽哥哥 0x4b0...4d2db
ELLA 0x583...40225
申請人:
阿湯哥 0xdd8...92148
管理員0xca35b7d915458ef540ade6068dfe2f44e8fa733c
輝哥0x14723a09acff6d2a60dcdf7aa4aff308fddc160c
歐陽哥哥0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db
ELLA0x583031d1113ad414f02576bd6afabfb302140225
阿湯哥0xdd870fa1b7c4700f2bd7f44238821c26f7392148
1. 創建多簽合約
1) 創建代幣合約Colorbay
切換到管理員賬號0xca3...a733c下,創建代幣合約Colorbay,創建完成后,復制合約地址備用。試用者也可以創建自己的ERCC20合約,記住地址即可。
0x692a70d2e424a56d2c6c27aa97d1a86395877b3a
2)創建多簽合約
切換到管理員賬號0xca3...a733c下,創建多簽合約。輸入CLB合約地址,將審批委員會名單導入(管理員、輝哥、歐陽哥哥、ELLA),配置需要4個確認個數(也稱必簽數,即需要審批4次才能通過)
constructor("0x692a70d2e424a56d2c6c27aa97d1a86395877b3a", ["0xca35b7d915458ef540ade6068dfe2f44e8fa733c","0x14723a09acff6d2a60dcdf7aa4aff308fddc160c","0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db","0x583031d1113ad414f02576bd6afabfb302140225"], "4")
如果必簽數<委員會成員數,將不能完成任何事務的審批,必須通過新增委員會成員(見用例8)才能完成
將多簽合約地址復制下來備用:
0xbbf289d846208c16edc8474705c748aff07732db
3)給多簽合約地址充值CLB
回到CLB合約中,切換到管理員賬號0xca3...a733c下,給多簽合約地址充值2000000CLB
transfer("0xbbf289d846208c16edc8474705c748aff07732db", "2000000,000000000000000000") #約定:運行時,去除數額中的逗號,否則會出錯
4)查詢多簽合約地址代幣余額
balanceOf("0xbbf289d846208c16edc8474705c748aff07732db")
5)查詢剩余發行總量
balanceOf("0xca35b7d915458ef540ade6068dfe2f44e8fa733c")
2、發起待審批事務
僅審批委員會成員可操作
1)作為申請人阿湯哥0xdd8...92148,向管理員提出申請,需要10000個CLB做為活動運營激勵。切換到管理員賬號0xca3...a733c下(可以是任意審批委員會賬號)
submitTransaction("0xdd870fa1b7c4700f2bd7f44238821c26f7392148", "10000,000000000000000000")
編號為0的事務申請提交完成后,即管理員已經審批通過了這個事務,待其他3個審批通過。
2)作為申請人阿湯哥0xdd8...92148,直接自己發起待審批事務。切換到阿湯哥賬號0xca3...a733c下(任意非審批委員會賬號),將會報錯,因為普通用戶不能發起待審批事務
submitTransaction("0xdd870fa1b7c4700f2bd7f44238821c26f7392148", "10000,0000000000000000000000")
3)查詢當前編號為0的事務有幾人審批確認了
getConfirmationCount(0)
4)查詢當前編號為0的事務有哪些委員做審批確認了
getConfirmations(0)
3、 進入審批流程
1)編號為0的事務還需要3個審批確認,現在輝哥、歐陽哥哥開始審批編號為0的事務,分別切換到輝哥賬號、歐陽哥哥賬號0xca3...a733c下,
confirmTransaction(0)
輝哥再次審批將會報錯,因為已經審批過了
重復用例3第3、4步
2)輝哥后悔了,想要撤銷審批,切換到輝哥賬號0xca3...a733c下:
revokeConfirmation(0)
重復用例3第3、4步
3)審批委員會說服了輝哥,切換到輝哥賬號0xca3...a733c下,輝哥再次做審批通過操作:
重復用例5第1步
4、完成最后1次審批,同時執行轉賬,結束事務
1)切換到ELLA賬號0x583...40225下,完成最后1次審批確認,同時將執行轉賬操作(已經滿足4次確認)
2)查詢阿湯哥到賬情況
回到CLB合約,查詢阿湯哥賬號0xdd8...92148余額
balanceOf("0xdd870fa1b7c4700f2bd7f44238821c26f7392148")
3)查詢發行總量
重復用例1第5條
4)查詢編號為0的事務是否已經處于完成狀態
isConfirmed(0)
5、 刪除審批委員會成員
說明:不管是在審批流程中操作,還是在審批結束后操作,移除成員時,遵循以下規則:
委員會成員最多數為100個;
如果必簽的個數>委員會成員數,比如必簽個數為4,委員會成員數為4,做刪除1個成員操作后,那么必簽個數會更新成=委員會成員數;
如果必簽的個數<委員會成員數,比如必簽個數為4,委員會成員數為5,做刪除1個成員操作后,那么審批通過不受影響;
刪除操作人必須是自己賬號,就是自有自己能刪除自己,否則就是中心化了。
1)輝哥由于個人原因,要退出治理審批委員會。他在自己賬號下調用多簽合約的函數 0x147...c160c
removeOwner("0x14723a09acff6d2a60dcdf7aa4aff308fddc160c")
6、 取回合約代幣
后來,該治理委員會開始無法有效運作,部分人員審批并不及時,嚴重影響了業務進展。老板決策把剩余代幣打回到管理賬號,結束該審計委員會工作和審批權限。
“區塊鏈怎么實現以太坊通證的多簽合約”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。