一個簡單的以太坊合約讓imtoken支持多籤

熟悉比特幣和以太坊的人應該都知道,在比特幣中有2種類型的地址,1開頭的是P2PKH,就是我的地址,3開頭的是P2SH,通常是一個多籤地址。因此在原生上比特幣就支持多籤。多籤的一個優點就是能夠多方對一筆付款達成共識,才能支付成功。好比3我的合夥開公司,他們的對外付款是比特幣,爲了防止管理財務的人做惡,因而他們能夠建立2/3多籤的地址,每一個人持有一個私鑰,對於每一筆付款,必須任意2我的都簽名了才能支付出去。緩存

比特幣上的這個多籤地址在以太坊上是沒有原生支持的!以太坊最大的優勢是支持圖靈完備的智能合約,因此多籤功能須要靠智能合約來實現。app

爲了簡化代碼,咱們的需求是這樣的:建立一個AB兩個用戶建立2/2的多籤合約,該合約支持指定的ERC20 Token的支付。當某須要對外付款時,A用戶調用合約,發起對C的轉帳n個Token,B用戶也必須調用合約,發起對C的轉帳n個Token,只有A和B都調用了合約後,合約纔會真的付款。其餘用戶發起轉帳無效。ui

根據以上需求,我改了一款極其簡單的多籤合約。代碼以下:this

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進行多籤,而不須要任何複雜的技能。因此我改寫後的多籤合約以下:spa

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的緩存刷新比較慢,並非部署了合約後立刻就能搜索到的。以上代碼都實測沒問題。code

相關文章
相關標籤/搜索