前言
ETH的多签是依靠智能合约实现的,也就是说持币的是智能合约,通过在智能合约里设置条件,来使转账之类的权限通过多人签名来集体控制。
从网上找到了一段简单的多签智能合约,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| pragma solidity ^0.4.21;
contract MultiSigWallet{ address private owner; mapping (address => uint8) private managers; modifier isOwner{ require(owner == msg.sender); _; } modifier isManager{ require( msg.sender == owner || managers[msg.sender] == 1); _; } uint constant MIN_SIGNATURES = 3; uint private transactionIdx; struct Transaction { address from; address to; uint amount; uint8 signatureCount; mapping (address => uint8) signatures; } mapping (uint => Transaction) private transactions; uint[] private pendingTransactions; function MultiSigWallet() public{ owner = msg.sender; } event DepositFunds(address from, uint amount); event TransferFunds(address to, uint amount); event TransactionCreated( address from, address to, uint amount, uint transactionId ); function addManager(address manager) public isOwner{ managers[manager] = 1; } function removeManager(address manager) public isOwner{ managers[manager] = 0; } function () public payable{ emit DepositFunds(msg.sender, msg.value); } function withdraw(uint amount) isManager public{ transferTo(msg.sender, amount); } function transferTo(address to, uint amount) isManager public{ require(address(this).balance >= amount); uint transactionId = transactionIdx++; Transaction memory transaction; transaction.from = msg.sender; transaction.to = to; transaction.amount = amount; transaction.signatureCount = 0; transactions[transactionId] = transaction; pendingTransactions.push(transactionId); emit TransactionCreated(msg.sender, to, amount, transactionId); } function getPendingTransactions() public isManager view returns(uint[]){ return pendingTransactions; } function signTransaction(uint transactionId) public isManager{ Transaction storage transaction = transactions[transactionId]; require(0x0 != transaction.from); require(msg.sender != transaction.from); require(transaction.signatures[msg.sender]!=1); transaction.signatures[msg.sender] = 1; transaction.signatureCount++; if(transaction.signatureCount >= MIN_SIGNATURES){ require(address(this).balance >= transaction.amount); transaction.to.transfer(transaction.amount); emit TransferFunds(transaction.to, transaction.amount); deleteTransactions(transactionId); } } function deleteTransactions(uint transacionId) public isManager{ uint8 replace = 0; for(uint i = 0; i< pendingTransactions.length; i++){ if(1==replace){ pendingTransactions[i-1] = pendingTransactions[i]; }else if(transacionId == pendingTransactions[i]){ replace = 1; } } delete pendingTransactions[pendingTransactions.length - 1]; pendingTransactions.length--; delete transactions[transacionId]; } function walletBalance() public isManager view returns(uint){ return address(this).balance; } }
|
部署合约
我本地的测试网络是使用 Ganache 搭建的,如下图
我修改了我的 Ganache 的端口为 8545。
然后打开 Remix,在右边栏选择 Run,在 Environment 中选择 Web3 Provider,如下图
在弹出的层中,输入本地地址和刚才配置的8545端口,就可以连接到 Ganache 的环境了,如下图
连接成功后,会看到在 Environment 下面的 Account 栏中会列出所有 Ganache 中的测试账号。
我们选择第一个账号作为部署合约的主账号,即合约的 owner。
在编辑器中创建一个新的合约文件,把上面的合约代码复制进编辑器,右边栏选择 Compile ,
在 Select new compiler version 中选择 0.4.21 版本的编译器,等待编译完成。
完成后,最下面出现绿色的提示信息,如下图
再切换回 Run 标签,点击 Deploy 就可以部署了。
部署成功后,在 Deployed Contracts 中就能看到新部署的合约了,下拉打开可以看到合约的各个接口,如图
增加合约管理员
在右边栏找到合约的 addManager 接口,下拉打开输入一个以太钱包地址,然后点击 transact 即完成添加。
这里我把 Ganache 的第 2 到 4 的地址加入进管理员中。
给合约内转账
切换回 Ganache,在 Transactions 中找到我们刚才部署的合约地址,0x08ea5409922D4204C0E03d17243E77c3810C0CCe
,
使用任意的其他账号转账任意个 ETH 进合约里。
我用的是 MyEtherWallet进行的转账,只需要把节点设置成本地网络,
然后用私钥模式登陆转账即可,这里就略过不说了。
转完账后,在 Remix 的 Run 标签下,找到合约的 walletBalance 接口,
点击调用即可显示当前合约的余额,注意单位!
多签从合约内转出金额
回到 Remix,在右边栏的 Run 标签下,先确认 Account 选择的是合约的 owner,
这个例子里就是第一个测试地址了。
在合约中找到 transferTo,下拉打开,输入一个本地测试网络中的以太钱包地址,输入待转账的金额,点击 transact。这里注意单位,下图为转 5 ETH,
成功执行后,就会有一个等待多签的 transaction 在 pendingTransactions 这个数组里。
点击 getPendingTransactions 可以得到当前等待多签的 transaction 在数组中的索引值。
下图则代表当前等待多签的 transaction 的索引值是 0 。
这时,在 Run 标签页的顶部,把 Account 依次切换为其他几个管理员,分别执行合约的 signTransaction 接口即可完成多签,其中 transactionId 就是上一步中的索引值,如下图
目前这个合约代码中,是当签名管理员的数量大于等于3个,就正式完成多签并发送交易。
所以当第三个管理执行完 signTransaction 后,查一下合约的余额就发现已经转出去 5ETH了。
总结
目前这个例子中的合约代码还是比较简单的,没有各个用户的权重关系。这个就可以根据自己的需要来写合约了。
Over!