溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

區(qū)塊鏈怎么實(shí)現(xiàn)以太坊通證的多簽合約

發(fā)布時(shí)間:2022-01-19 10:03:38 來源:億速云 閱讀:150 作者:iii 欄目:互聯(lián)網(wǎng)科技

本篇內(nèi)容介紹了“區(qū)塊鏈怎么實(shí)現(xiàn)以太坊通證的多簽合約”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

需求描述

有一天,老板給輝哥提了一個(gè)需求,希望能夠?qū)崿F(xiàn)一個(gè)安全的代幣支出多簽功能,便于基金會治理審核。

匯總而言,產(chǎn)品需求描述如下:

(1)治理委員會由4人組成,一項(xiàng)CLB代幣支出需要所有4人同意才可以轉(zhuǎn)賬打出; (2)為了避嫌中心化作惡,治理委員會的退出只能由委員本人賬號操作有效; (3)當(dāng)治理委員會無法正常運(yùn)作時(shí),老板有權(quán)撤回治理委員會管理的剩余代幣。

多簽錢包市場調(diào)研信息

輝哥首先完成市場上商用硬件錢包的調(diào)研工作。

結(jié)論:

目前(2018.08.24)世面上暫時(shí)沒有體驗(yàn)好的多簽硬錢包,不過等1個(gè)月后比特派硬件錢包更新會支持ETH和ERC20代幣的多重簽名功能。

具體信息:

  • 目前沒有支持ETH和ERC20多簽功能的冷錢包。 市面上有較多錢包支持比特幣多簽功能的。 ImToken錢包不支持ETH及ERC20代幣的多簽管理,但支持離線授權(quán)管理。 國內(nèi)庫神硬件錢包也不支持ETH多簽,價(jià)格大概在2000元左右。 國外的Trezor價(jià)格大概在800元左右。 比特派(bitpay)錢包答復(fù)9月份會發(fā)布一款硬件錢包支持ETH和ERC20多簽功能的冷錢包,價(jià)位1299元左右。 商品鏈接可點(diǎn)擊訪問。

  • 律師推薦的gnosis多簽錢包是英文網(wǎng)頁版操作的。gnosis多簽錢包合約代碼可點(diǎn)擊訪問。

  • 支持ETH多簽合約的PC版錢包還有Mist錢包,PARITY錢包,是英文頁面操作。

  • 目前很多基金會直接使用多簽合約代碼部署后支持在頁面進(jìn)行多簽管理。

多簽智能合約代碼及解析

多簽合約核心代碼如下:

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];
       }

   }

}

各個(gè)函數(shù)的定義說明參考類圖:

區(qū)塊鏈怎么實(shí)現(xiàn)以太坊通證的多簽合約

多簽合約類圖

核心函數(shù)說明:

  • constructor(address _token, address[] _owners, uint256 _required) public validRequirement(_owners.length, _required) 多簽創(chuàng)建函數(shù),參數(shù)分別為通證的地址,多簽賬戶的地址,需要幾個(gè)賬戶多簽;

  • function submitTransaction(address destination, uint256 value) public 提交轉(zhuǎn)賬申請(目標(biāo)賬戶和金額),任何人都可以發(fā)起;如果是委員會委員者發(fā)起則同時(shí)完成審批;

  • function confirmTransaction(uint256 transactionId) public 委員會委員審批通過

  • function revokeConfirmation(uint256 transactionId) public 在轉(zhuǎn)賬成功前,已審核的委員會可撤銷審核授權(quán)

  • function removeOwner(address owner) public ownerExists(owner) 刪除治理委員賬戶,只有自己能操作,已防止他人作惡。

  • function withdrawToken() public onlyCreator 打回通證,只有合約創(chuàng)建者能操作

多簽智能合約場景測試

編譯通過后,按照實(shí)際業(yè)務(wù)場景,輝哥做了一下完整測試。測試流程如下:

區(qū)塊鏈怎么實(shí)現(xiàn)以太坊通證的多簽合約

業(yè)務(wù)流程

具體的操作流程如下,均達(dá)到預(yù)期目標(biāo),本多簽合約具備商用能力。

測試數(shù)據(jù)

代幣

  • 彩貝發(fā)行總量為 1,000,000,000 個(gè)token

  • 激勵(lì) 占比35%,即350,000,000個(gè)token

  • 私募 占比20%,即200,000,000個(gè)token

  • 團(tuán)隊(duì)及基金會 占比25%,即250,000,000個(gè)token

  • 社區(qū)培養(yǎng)及推廣 占比20%,即200,000,000個(gè)token

賬號

  • 治理審批委員會成員:

  • 老板 0xca3...a733c

  • 輝哥 0x147...c160c

  • 歐陽哥哥 0x4b0...4d2db

  • ELLA 0x583...40225

申請人:

  • 阿湯哥 0xdd8...92148

  • 管理員0xca35b7d915458ef540ade6068dfe2f44e8fa733c

  • 輝哥0x14723a09acff6d2a60dcdf7aa4aff308fddc160c

  • 歐陽哥哥0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db

  • ELLA0x583031d1113ad414f02576bd6afabfb302140225

  • 阿湯哥0xdd870fa1b7c4700f2bd7f44238821c26f7392148

1. 創(chuàng)建多簽合約

1) 創(chuàng)建代幣合約Colorbay

切換到管理員賬號0xca3...a733c下,創(chuàng)建代幣合約Colorbay,創(chuàng)建完成后,復(fù)制合約地址備用。試用者也可以創(chuàng)建自己的ERCC20合約,記住地址即可。

0x692a70d2e424a56d2c6c27aa97d1a86395877b3a

2)創(chuàng)建多簽合約

切換到管理員賬號0xca3...a733c下,創(chuàng)建多簽合約。輸入CLB合約地址,將審批委員會名單導(dǎo)入(管理員、輝哥、歐陽哥哥、ELLA),配置需要4個(gè)確認(rèn)個(gè)數(shù)(也稱必簽數(shù),即需要審批4次才能通過)

constructor("0x692a70d2e424a56d2c6c27aa97d1a86395877b3a", ["0xca35b7d915458ef540ade6068dfe2f44e8fa733c","0x14723a09acff6d2a60dcdf7aa4aff308fddc160c","0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db","0x583031d1113ad414f02576bd6afabfb302140225"], "4")

如果必簽數(shù)<委員會成員數(shù),將不能完成任何事務(wù)的審批,必須通過新增委員會成員(見用例8)才能完成

將多簽合約地址復(fù)制下來備用:

0xbbf289d846208c16edc8474705c748aff07732db

3)給多簽合約地址充值CLB

回到CLB合約中,切換到管理員賬號0xca3...a733c下,給多簽合約地址充值2000000CLB

transfer("0xbbf289d846208c16edc8474705c748aff07732db", "2000000,000000000000000000") #約定:運(yùn)行時(shí),去除數(shù)額中的逗號,否則會出錯(cuò)

4)查詢多簽合約地址代幣余額

balanceOf("0xbbf289d846208c16edc8474705c748aff07732db")

5)查詢剩余發(fā)行總量

balanceOf("0xca35b7d915458ef540ade6068dfe2f44e8fa733c")

2、發(fā)起待審批事務(wù)

僅審批委員會成員可操作

1)作為申請人阿湯哥0xdd8...92148,向管理員提出申請,需要10000個(gè)CLB做為活動(dòng)運(yùn)營激勵(lì)。切換到管理員賬號0xca3...a733c下(可以是任意審批委員會賬號)

submitTransaction("0xdd870fa1b7c4700f2bd7f44238821c26f7392148", "10000,000000000000000000")

編號為0的事務(wù)申請?zhí)峤煌瓿珊?,即管理員已經(jīng)審批通過了這個(gè)事務(wù),待其他3個(gè)審批通過。

2)作為申請人阿湯哥0xdd8...92148,直接自己發(fā)起待審批事務(wù)。切換到阿湯哥賬號0xca3...a733c下(任意非審批委員會賬號),將會報(bào)錯(cuò),因?yàn)槠胀ㄓ脩舨荒馨l(fā)起待審批事務(wù)

submitTransaction("0xdd870fa1b7c4700f2bd7f44238821c26f7392148", "10000,0000000000000000000000")

3)查詢當(dāng)前編號為0的事務(wù)有幾人審批確認(rèn)了

getConfirmationCount(0)

4)查詢當(dāng)前編號為0的事務(wù)有哪些委員做審批確認(rèn)了

getConfirmations(0)

3、 進(jìn)入審批流程

1)編號為0的事務(wù)還需要3個(gè)審批確認(rèn),現(xiàn)在輝哥、歐陽哥哥開始審批編號為0的事務(wù),分別切換到輝哥賬號、歐陽哥哥賬號0xca3...a733c下,

confirmTransaction(0)

輝哥再次審批將會報(bào)錯(cuò),因?yàn)橐呀?jīng)審批過了

重復(fù)用例3第3、4步

2)輝哥后悔了,想要撤銷審批,切換到輝哥賬號0xca3...a733c下:

revokeConfirmation(0)

重復(fù)用例3第3、4步

3)審批委員會說服了輝哥,切換到輝哥賬號0xca3...a733c下,輝哥再次做審批通過操作:

重復(fù)用例5第1步

4、完成最后1次審批,同時(shí)執(zhí)行轉(zhuǎn)賬,結(jié)束事務(wù)

1)切換到ELLA賬號0x583...40225下,完成最后1次審批確認(rèn),同時(shí)將執(zhí)行轉(zhuǎn)賬操作(已經(jīng)滿足4次確認(rèn))

2)查詢阿湯哥到賬情況

回到CLB合約,查詢阿湯哥賬號0xdd8...92148余額

balanceOf("0xdd870fa1b7c4700f2bd7f44238821c26f7392148")

3)查詢發(fā)行總量

重復(fù)用例1第5條

4)查詢編號為0的事務(wù)是否已經(jīng)處于完成狀態(tài)

isConfirmed(0)

5、 刪除審批委員會成員

說明:不管是在審批流程中操作,還是在審批結(jié)束后操作,移除成員時(shí),遵循以下規(guī)則:

  • 委員會成員最多數(shù)為100個(gè);

  • 如果必簽的個(gè)數(shù)>委員會成員數(shù),比如必簽個(gè)數(shù)為4,委員會成員數(shù)為4,做刪除1個(gè)成員操作后,那么必簽個(gè)數(shù)會更新成=委員會成員數(shù);

  • 如果必簽的個(gè)數(shù)<委員會成員數(shù),比如必簽個(gè)數(shù)為4,委員會成員數(shù)為5,做刪除1個(gè)成員操作后,那么審批通過不受影響;

  • 刪除操作人必須是自己賬號,就是自有自己能刪除自己,否則就是中心化了。

1)輝哥由于個(gè)人原因,要退出治理審批委員會。他在自己賬號下調(diào)用多簽合約的函數(shù) 0x147...c160c

removeOwner("0x14723a09acff6d2a60dcdf7aa4aff308fddc160c")

6、 取回合約代幣

后來,該治理委員會開始無法有效運(yùn)作,部分人員審批并不及時(shí),嚴(yán)重影響了業(yè)務(wù)進(jìn)展。老板決策把剩余代幣打回到管理賬號,結(jié)束該審計(jì)委員會工作和審批權(quán)限。

“區(qū)塊鏈怎么實(shí)現(xiàn)以太坊通證的多簽合約”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI