熟悉比特币和以太坊的人应该都知道,在比特币中有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.  
  3. interface Token {
  4. function balanceOf(address _owner) public view returns (uint256 );
  5. function transfer(address _to, uint256 _value) public ;
  6. }
  7.  
  8. contract MultiSig {
  9. address private addrA;
  10. address private addrB;
  11. address private addrToken;
  12.  
  13. struct Permit {
  14. bool addrAYes;
  15. bool addrBYes;
  16. }
  17.  
  18. mapping (address => mapping (uint => Permit)) private permits;
  19.  
  20. event Transfer(address indexed from, address indexed to, uint256 value);
  21.  
  22. constructor(address a, address b, address tokenAddress) public{
  23. addrA = a;
  24. addrB = b;
  25. addrToken = tokenAddress;
  26. }
  27. function getAddrs() public view returns(address, address,address) {
  28. return (addrA, addrB,addrToken);
  29. }
  30. function transferTo(address to, uint amount) public{
  31. Token token = Token(addrToken);
  32. require(token.balanceOf(this) >= amount);
  33.  
  34. if (msg.sender == addrA) {
  35. permits[to][amount].addrAYes = true;
  36. } else if (msg.sender == addrB) {
  37. permits[to][amount].addrBYes = true;
  38. } else {
  39. require(false);
  40. }
  41.  
  42. if (permits[to][amount].addrAYes == true && permits[to][amount].addrBYes == true) {
  43. token.transfer(to, amount);
  44. permits[to][amount].addrAYes = false;
  45. permits[to][amount].addrBYes = false;
  46. }
  47. emit Transfer(msg.sender, to, amount);
  48. }
  49. }

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

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

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

一个简单的以太坊合约让imtoken支持多签的更多相关文章

  1. go打造以太坊合约测试框架

    传送门: 柏链项目学院 1 以太坊智能合约编译 以太坊智能合约编写使用solidity语言,一般情况下我们会在remix环境下进行编译测试,在线环境相对比较稳定.如果不想用在线环境,那我们就需要自己动 ...

  2. 以太坊智能合约Hello World示例程序

    简介 以太坊(Ethereum)是一提供个智能合约(smart contract)功能的公共区块链(BlockChain)平台. 本文介绍了一个简单的以太坊智能合约的开发过程. 开发环境 在以太坊上开 ...

  3. [币严区块链]简单易懂的以太坊(ETH)智能合约开发入门教程

    以太坊(Ethereum)是一提供个智能合约(smart contract)功能的公共区块链(BlockChain)平台. 本文介绍了一个简单的以太坊智能合约的开发过程. 开发环境 在以太坊上开发应用 ...

  4. c#实战开发:用.net core开发一个简单的Web以太坊钱包 (六)

    今天就来开发一个C# 版的简易钱包 先回顾以前的内容 c#实战开发:以太坊Geth 命令发布智能合约 (五) c#实战开发:以太坊Geth 常用命令 (四) c#实战开发:以太坊钱包快速同步区块和钱包 ...

  5. 如何在以太坊上搭建一个Dapp?

    原创: 前哨小兵甲 区块链前哨 昨天 策划|Tina作者|Mahesh Murthy俗话说,实践出真知!对于开发人员来说,最好的学习办法就是亲自动手做一个小项目.所以,接下来我们将会以一个投票程序为例 ...

  6. 区块链入门到实战(27)之以太坊(Ethereum) – 智能合约开发

    智能合约的优点 与传统合同相比,智能合约有一些显著优点: 不需要中间人 费用低 代码就是规则 区块链网络中有多个备份,不用担心丢失 避免人工错误 无需信任,就可履行协议 匿名履行协议 以太坊(Ethe ...

  7. 基于以太坊开发的类似58同城的DApp开发与应用案例

    今天,Origin开发团队很高兴地宣布在以太坊Rinkeby测试网络上推出Origin Protocol Demo DApp ! 在这个DApp中,你可以在不同垂直行业的solidarity econ ...

  8. C#以太坊基础入门

    在这一部分,我们将使用C#开发一个最简单的.Net控制台应用,来接入以太坊节点,并打印 所连接节点旳版本信息.通过这一部分的学习,你将掌握以下技能: 如何使用节点仿真器 如何在命令行访问以太坊节点 如 ...

  9. Geth命令用法-参数详解 and 以太坊源码文件目录

    本文是对以太坊客户端geth命令的解析 命令用法 geth [选项] 命令 [命令选项] [参数-] 版本 1.7.3-stable 命令 account 管理账户 attach 启动交互式JavaS ...

随机推荐

  1. 吴恩达机器学习笔记58-协同过滤算法(Collaborative Filtering Algorithm)

    在之前的基于内容的推荐系统中,对于每一部电影,我们都掌握了可用的特征,使用这些特征训练出了每一个用户的参数.相反地,如果我们拥有用户的参数,我们可以学习得出电影的特征. 但是如果我们既没有用户的参数, ...

  2. 找不到servlet对应的class

    javax.servlet.ServletException: Wrapper cannot find servlet class com.suntomor.lewan.pay.NotifyRecei ...

  3. html 和css 效果--整理集合篇

    一.如何用一张图片做背景图,并且图片自适应div的大小 background: url("../stu_wengu.png") center center no-repeat; b ...

  4. 深度解密HTTP通信细节

    目录 HTTP报文截获 背景介绍 抓包 mac本地 远程docker 请求 && 分析 关闭服务进程 关闭docker 重启docker 正常请求 HTTP协议分析 整体介绍 编码 M ...

  5. 爸爸又给Spring MVC生了个弟弟叫Spring WebFlux

    情景引入 很早之前,Java就火起来了,是因为它善于开发和处理网络方面的应用. Java有一个爱好,就是喜欢制定规范标准,但自己又不善于去实现. 反倒是一些服务提供商使用它的规范标准来制造应用服务器而 ...

  6. SLAM+语音机器人DIY系列:(四)差分底盘设计——2.stm32主控软件设计

    摘要 运动底盘是移动机器人的重要组成部分,不像激光雷达.IMU.麦克风.音响.摄像头这些通用部件可以直接买到,很难买到通用的底盘.一方面是因为底盘的尺寸结构和参数是要与具体机器人匹配的:另一方面是因为 ...

  7. 类和对象,以及 LeetCode 每日一题

    所有类都是引用类型. 1 定义类 类是某一批对象的抽象. 1.1 定义类的语法: [修饰符] class 类名{ 零到多个构造器定义 零到多个成员变量 零到多个方法 } 对于一个类定义而言,可以包含三 ...

  8. Odoo 菜单美化的扩展模块

    详见: http://www.oejia.net/blog/2018/07/07/oejia_menu_about.html Odoo 菜单美化主题,odoo默认的菜单算是简洁好用的,如果您觉得还少了 ...

  9. 自己动手写事件总线(EventBus)

    本文由云+社区发表 事件总线核心逻辑的实现. EventBus的作用 Android中存在各种通信场景,如Activity之间的跳转,Activity与Fragment以及其他组件之间的交互,以及在某 ...

  10. ElasticSearch、Logstash、Kibana 搭建高效率日志管理系统

    ELK (ElasticSearch.LogStash以及Kibana)三者组合是一个非常强大的工具,这里我们来实现监控日志文件并且收到日志到ElasticSearch搜索引擎,利用Kibana可视化 ...