经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 区块链 » 查看文章
一个简单的以太坊合约让imtoken支持多签
来源:cnblogs  作者:深蓝  时间:2019/4/22 8:43:48  对本文有异议

熟悉比特币和以太坊的人应该都知道,在比特币中有2种类型的地址,1开头的是P2PKH,就是个人地址,3开头的是P2SH,一般是一个多签地址。所以在原生上比特币就支持多签。多签的一个优势就是可以多方对一笔付款达成共识,才能支付成功。比如3个人合伙开公司,他们的对外付款是比特币,为了防止管理财务的人作恶,于是他们可以创建2/3多签的地址,每个人持有一个私钥,对于每一笔付款,必须任意2个人都签名了才能支付出去。

比特币上的这个多签地址在以太坊上是没有原生支持的!以太坊最大的优点是支持图灵完备的智能合约,所以多签功能需要靠智能合约来实现。

为了简化代码,我们的需求是这样的:创建一个AB两个用户创建2/2的多签合约,该合约支持指定的ERC20 Token的支付。当某需要对外付款时,A用户调用合约,发起对C的转账n个Token,B用户也必须调用合约,发起对C的转账n个Token,只有A和B都调用了合约后,合约才会真的付款。其他用户发起转账无效。

根据以上需求,我改了一款极其简单的多签合约。代码如下:

  1. pragma solidity ^0.4.24;
  2. interface Token {
  3. function balanceOf(address _owner) public view returns (uint256 );
  4. function transfer(address _to, uint256 _value) public ;
  5. }
  6. contract MultiSig {
  7. address private addrA;
  8. address private addrB;
  9. address private addrToken;
  10. struct Permit {
  11. bool addrAYes;
  12. bool addrBYes;
  13. }
  14. mapping (address => mapping (uint => Permit)) private permits;
  15. event Transfer(address indexed from, address indexed to, uint256 value);
  16. constructor(address a, address b, address tokenAddress) public{
  17. addrA = a;
  18. addrB = b;
  19. addrToken = tokenAddress;
  20. }
  21. function getAddrs() public view returns(address, address,address) {
  22. return (addrA, addrB,addrToken);
  23. }
  24. function transferTo(address to, uint amount) public{
  25. Token token = Token(addrToken);
  26. require(token.balanceOf(this) >= amount);
  27. if (msg.sender == addrA) {
  28. permits[to][amount].addrAYes = true;
  29. } else if (msg.sender == addrB) {
  30. permits[to][amount].addrBYes = true;
  31. } else {
  32. require(false);
  33. }
  34. if (permits[to][amount].addrAYes == true && permits[to][amount].addrBYes == true) {
  35. token.transfer(to, amount);
  36. permits[to][amount].addrAYes = false;
  37. permits[to][amount].addrBYes = false;
  38. }
  39. emit Transfer(msg.sender, to, amount);
  40. }
  41. }

以上代码十分简陋,功能十分有限,而且需要在etherscan或者remix上调用,对用户来说十分不友好,于是我想到了可以按ERC20标准接口对这个多签合约进行改造。改造后的合约看起来像是一个Token,但是本质上是一个多签地址。A B用户都可以使用imtoken或者KCash之类的支持ERC20的钱包APP进行多签,而不需要任何复杂的技能。所以我改写后的多签合约如下:

  1. pragma solidity ^0.4.24;
  2. interface IERC20 {
  3. function transfer(address to, uint256 value) external returns (bool);
  4. function approve(address spender, uint256 value) external returns (bool);
  5. function transferFrom(address from, address to, uint256 value) external returns (bool);
  6. function totalSupply() external view returns (uint256);
  7. function balanceOf(address who) external view returns (uint256);
  8. function allowance(address owner, address spender) external view returns (uint256);
  9. event Transfer(address indexed from, address indexed to, uint256 value);
  10. event Approval(address indexed owner, address indexed spender, uint256 value);
  11. }
  12. contract MultiSig is IERC20 {
  13. address private addrA;
  14. address private addrB;
  15. address private addrToken;
  16. struct Permit {
  17. bool addrAYes;
  18. bool addrBYes;
  19. }
  20. mapping (address => mapping (uint => Permit)) private permits;
  21. event Transfer(address indexed from, address indexed to, uint256 value);
  22. event Approval(address indexed owner, address indexed spender, uint256 value);
  23. uint public totalSupply = 10*10**26;
  24. uint8 constant public decimals = 18;
  25. string constant public name = "MutiSigPTN";
  26. string constant public symbol = "MPTN";
  27. function approve(address spender, uint256 value) external returns (bool){
  28. return false;
  29. }
  30. function transferFrom(address from, address to, uint256 value) external returns (bool){
  31. return false;
  32. }
  33. function totalSupply() external view returns (uint256){
  34. IERC20 token = IERC20(addrToken);
  35. return token.totalSupply();
  36. }
  37. function allowance(address owner, address spender) external view returns (uint256){
  38. return 0;
  39. }
  40. constructor(address a, address b, address tokenAddress) public{
  41. addrA = a;
  42. addrB = b;
  43. addrToken = tokenAddress;
  44. }
  45. function getAddrs() public view returns(address, address,address) {
  46. return (addrA, addrB,addrToken);
  47. }
  48. function transfer(address to, uint amount) public returns (bool){
  49. IERC20 token = IERC20(addrToken);
  50. require(token.balanceOf(this) >= amount);
  51. if (msg.sender == addrA) {
  52. permits[to][amount].addrAYes = true;
  53. } else if (msg.sender == addrB) {
  54. permits[to][amount].addrBYes = true;
  55. } else {
  56. require(false);
  57. }
  58. if (permits[to][amount].addrAYes == true && permits[to][amount].addrBYes == true) {
  59. token.transfer(to, amount);
  60. permits[to][amount].addrAYes = false;
  61. permits[to][amount].addrBYes = false;
  62. }
  63. emit Transfer(msg.sender, to, amount);
  64. return true;
  65. }
  66. function balanceOf(address _owner) public view returns (uint) {
  67. IERC20 token = IERC20(addrToken);
  68. if (_owner==addrA || _owner==addrB){
  69. return token.balanceOf(this);
  70. }
  71. return 0;
  72. }
  73. }

这里我需要特别指出的是decimals这个属性必须与所支持的ERC20 Token一致,这样钱包才会算出正确的转账金额。另外imtoken的缓存刷新比较慢,并不是部署了合约后马上就能搜索到的。以上代码都实测没问题。

原文链接:http://www.cnblogs.com/studyzy/p/10739490.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号