zoukankan      html  css  js  c++  java
  • 一个简单的以太坊合约让imtoken支持多签

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

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

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

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

    pragma solidity ^0.4.24;
     
    interface Token {
      function balanceOf(address _owner)  public view returns (uint256 );
      function transfer(address _to, uint256 _value) public ;
    }
        
    contract MultiSig {
        address private addrA;
        address private addrB;
        address private addrToken;
     
        struct Permit {
            bool addrAYes;
            bool addrBYes;
        }
        
        mapping (address => mapping (uint => Permit)) private permits;
        
        event Transfer(address indexed from, address indexed to, uint256 value);
        
        constructor(address a, address b, address tokenAddress) public{
            addrA = a;
            addrB = b;
            addrToken = tokenAddress;
        }
        function getAddrs() public view returns(address, address,address) {
          return (addrA, addrB,addrToken);
        }
        function transferTo(address to,  uint amount) public{
            Token token = Token(addrToken);
            require(token.balanceOf(this) >= amount);
     
            if (msg.sender == addrA) {
                permits[to][amount].addrAYes = true;
            } else if (msg.sender == addrB) {
                permits[to][amount].addrBYes = true;
            } else {
                require(false);
            }
     
            if (permits[to][amount].addrAYes == true && permits[to][amount].addrBYes == true) {
                token.transfer(to, amount);
                permits[to][amount].addrAYes = false;
                permits[to][amount].addrBYes = false;
            }
            emit Transfer(msg.sender, to, amount);
        }
    }

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

    pragma solidity ^0.4.24;
    
    interface IERC20 {
        function transfer(address to, uint256 value) external returns (bool);
    
        function approve(address spender, uint256 value) external returns (bool);
    
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    
        function totalSupply() external view returns (uint256);
    
        function balanceOf(address who) external view returns (uint256);
    
        function allowance(address owner, address spender) external view returns (uint256);
    
        event Transfer(address indexed from, address indexed to, uint256 value);
    
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    
        
    contract MultiSig is IERC20 {
        address private addrA;
        address private addrB;
        address private addrToken;
    
        struct Permit {
            bool addrAYes;
            bool addrBYes;
        }
        
        mapping (address => mapping (uint => Permit)) private permits;
        
         event Transfer(address indexed from, address indexed to, uint256 value);
    
        event Approval(address indexed owner, address indexed spender, uint256 value);
        
        uint public totalSupply = 10*10**26;
        uint8 constant public decimals = 18;
        string constant public name = "MutiSigPTN";
        string constant public symbol = "MPTN";
    
     function approve(address spender, uint256 value) external returns (bool){
         return false;
     }
    
        function transferFrom(address from, address to, uint256 value) external returns (bool){
            return false;
        }
    
        function totalSupply() external view returns (uint256){
              IERC20 token = IERC20(addrToken);
              return token.totalSupply();
        }
    
    
        function allowance(address owner, address spender) external view returns (uint256){
            return 0;
        }
        
        constructor(address a, address b, address tokenAddress) public{
            addrA = a;
            addrB = b;
            addrToken = tokenAddress;
        }
        function getAddrs() public view returns(address, address,address) {
          return (addrA, addrB,addrToken);
        }
        function  transfer(address to,  uint amount)  public returns (bool){
            IERC20 token = IERC20(addrToken);
            require(token.balanceOf(this) >= amount);
    
            if (msg.sender == addrA) {
                permits[to][amount].addrAYes = true;
            } else if (msg.sender == addrB) {
                permits[to][amount].addrBYes = true;
            } else {
                require(false);
            }
    
            if (permits[to][amount].addrAYes == true && permits[to][amount].addrBYes == true) {
                token.transfer(to, amount);
                permits[to][amount].addrAYes = false;
                permits[to][amount].addrBYes = false;
            }
            emit Transfer(msg.sender, to, amount);
            return true;
        }
        function balanceOf(address _owner) public view returns (uint) {
            IERC20 token = IERC20(addrToken);
            if (_owner==addrA || _owner==addrB){
                return token.balanceOf(this);
            }
            return 0;
        }
    }

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

  • 相关阅读:
    第一百一十一节,JavaScript,BOM浏览器对象模型
    第一百一十节,JavaScript匿名函数和闭包
    第一百零九节,JavaScript面向对象与原型
    第一百零八节,JavaScript,内置对象,Global对象字符串编码解码,Math对象数学公式
    第一百零七节,JavaScript基本包装类型,数据类型的方法
    第一百零六节,JavaScript变量作用域及内存
    第一百零五节,JavaScript正则表达式
    第一百零四节,JavaScript时间与日期
    经典的图片上传并绘制缩略图的类的代码
    ecshop判断搜索引擎是否为蜘蛛
  • 原文地址:https://www.cnblogs.com/studyzy/p/10739490.html
Copyright © 2011-2022 走看看