[
  {
    "path": ".gitignore",
    "content": "node_modules\nbuild\n*.orig"
  },
  {
    "path": "LICENSE.txt",
    "content": "Copyright (c) 2017 Christian Lundkvist\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# simple-multisig\n\n## Introduction\n\nThis is an Ethereum multisig contract designed to be as simple as possible. It is described further in this [medium post](https://medium.com/@ChrisLundkvist/exploring-simpler-ethereum-multisig-contracts-b71020c19037).\n\nThe main idea behind the contract is to pass in a threshold of detached signatures into the `execute` function and the contract will check the signatures and send off the transaction.\n\nThe audit report by [ConsenSys Diligence'](https://consensys.net/diligence/) can be found [here](./audit.pdf). \n\n## Version 2.0.0 Update to EIP712\n\nIn version 2.0.0 the Simple Multisig was updated to use the EIP712 signature standard. This means that the signature format of the previous version is no longer compatible. If your contract is already deployed and in use it still works but that version will no longer be supported in the future. We recommend moving ETH and tokens over to a newly deployed contract and using the EIP712 format going forward. Another change to be aware of is that the constructor now takes an extra parameter `chainId` to specify which network the contract is deployed on.\n\n## Data to be signed\n\nThe Simple MultiSig uses the [EIP712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) standard to package and hash the data to be signed. Each signer will sign a message over the following data fields, which encode the ethereum transaction to execute:\n\n* `address destination` - The target address for the transaction\n* `uint256 value` - The value of the transaction expressed in Wei\n* `bytes data` - The data of the transaction in hex format\n* `uint256 nonce` - The nonce for this transaction. Must match the current nonce in the multisig contract.\n* `address executor` - Specifies which Ethereum address is allowed to call the `execute` function. It is allowed to specify the zero address as an executor, in which case any address can call the `execute` function. This field is mainly to address concerns about replay attacks in some edge cases.\n* `uint256 gasLimit` - Specifies how much gas to pass on to the final `call`, independently of how much gas is supplied to the transaction calling the `execute` function. This can be used to constrain what kind of computations can be performed by the target smart contract. If the signers do not need this level of control a very high gasLimit of several million can be used for this data field.\n\nThe data to be signed also includes the following EIP712 Domain data that specifies the context of the signed data:\n\n* Name (`\"Simple MultiSig\"`)\n* Version (`\"1\"`)\n* ChainId (Integer marking current chain, e.g. 1 for mainnet)\n* Contract Address (Address of the specific multisig contract instance)\n* Salt (`0x251543af6a222378665a76fe38dbceae4871a070b7fdaf5c6c30cf758dc33cc0`, unique identifier specific to SimpleMultisig)\n\n## Installation and testing\n\nInstall global dependencies:\n\n* `npm install -g truffle`\n* `npm install -g ganache-cli`\n\nTo run the tests:\n\n* Make sure `ganache-cli` is running in its own terminal window.\n* `npm install`\n* `npm run test`\n\n## Testing signatures in a browser\n\nIf you have the [MetaMask](https://metamask.io) browser extension you can open the page `browsertest/index.html` in your browser and test signing data. The signature will be returned in a `(r,s,v)` format which can be plugged into the `execute` function.\n"
  },
  {
    "path": "RELEASE-NOTES.md",
    "content": "# Release Notes #\n\n## Version 2.0.1 - 2019-06-19\n\n* Fix misspellings in contract comments. By [ethers](https://github.com/ethers).\n\n* Update browser test with check for web3 object.\n\n* Fix faulty documentation of private key in MetaMask browser test.\n\n## Version 2.0.0 - 2018-08-18 ##\n\n* Backwards incompatible update of main contract to support [EIP712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md).\n\n* Add `executor` to the signed data in order to specify which address needs to call the `execute` function. Allows `address(0)` as valid executor if the signers want anyone to be able to execute the transaction.\n\n* Add `gasLimit` to the signed data in order to specify how much gas to supply to the function call.\n\n* Add input parameter `chainId` to the constructor.\n\n* Change fallback function from `public` to `external`.\n\n* Update tests for EIP712.\n\n* Add test for wrong nonce.\n\n* Update Solidity compiler version to 0.4.24.\n\n* Remove use of `bignumber.js` and replace with `web3.toBigNumber()` (Thanks to [barakman](https://github.com/barakman)).\n\n## Version 1.0.4 - 2018-06-12 ##\n\n* Document owners_ address being strictly increasing, by [ripper234](https://github.com/ripper234)\n\n* Update to new constructor syntax, by [ripper234](https://github.com/ripper234)\n\n* Check that threshold is positive instead of non-zero, by [ripper234](https://github.com/ripper234)\n\n* Update .gitignore, by [ripper234](https://github.com/ripper234)\n\n## Version 1.0.3 - 2018-06-11 ##\n\n* Moved the assembly to inside the `execute()` function and removed the `executeCall()` function. This is to avoid the possibility of the `internal` keyword on the `executeCall()` function being accidentally removed which would have catastrophic consequences.\n\n## Version 1.0.2 - 2018-05-04 ##\n\n* Updated to use assembly instead of `address.call()` syntax. Thanks to [ethers](https://github.com/ethers) for the suggestion. For more info about the problems with `address.call()` see [here](https://github.com/ethereum/solidity/issues/2884).\n\n* Fix indentation mismatch.\n\n## Version 1.0.1 - 2018-05-04 ##\n\n* Update to work with latest Solidity and Truffle version. By [grempe](https://github.com/grempe)\n\n* Add RELEASE-NOTES\n\n## Version 1.0.0 - 2017-03 to 2017-11 ##\n\n* Initial implementation\n\n* Tweaks by [naterush](https://github.com/naterush)\n\n* Informal review and fixes by [maurelian](https://github.com/maurelian)\n\n* Replace `sha3` with `keccak256` by [ethers](https://github.com/ethers)\n\n* Add MIT license\n"
  },
  {
    "path": "browsertest/index.html",
    "content": "<head>\n  <meta charset=\"utf-8\">\n  <title>EIP712 browser demo</title>\n</head>\n<body>\n  <script src=\"./sign.js\" language=\"javascript\"></script>\n  <p>\n    This page tests signing a SimpleMultiSig transaction using EIP712. It is based on <a href=\"https://weijiekoh.github.io/eip712-signing-demo/index.html\">this EIP712 demo</a> by Wei Jie Koh.\n  </p>\n\n  <table>\n    <thead>\n    </thead>\n    <tbody>\n      <tr>\n        <td>Wallet address</td>\n        <td><input type=\"text\" id=\"walletAddress\" size=\"40\" value=\"0xe3de7de481cbde9b4d5f62c6d228ec62277560c8\"></td>\n      </tr>\n      <tr>\n        <td>Destination</td>\n        <td><input type=\"text\" id=\"destination\" size=\"40\" value=\"0x8582afea2dd8e47297dbcdcf9ca289756ee21430\"></td>\n      </tr>\n      <tr>\n        <td>Value</td>\n        <td><input type=\"text\" id=\"value\" size=\"10\" value=\"10000000000000000\"></td>\n      </tr>\n      <tr>\n        <td>Data</td>\n        <td><input type=\"text\" id=\"data\" size=\"40\" value=\"0xf207564e0000000000000000000000000000000000000000000000000000000000003039\"></td>\n      </tr>\n      <tr>\n        <td>Nonce</td>\n        <td><input type=\"text\" id=\"nonce\" size=\"10\" value=\"2\"/></td>\n      </tr>\n      <tr>\n        <td>Executor</td>\n        <td><input type=\"text\" id=\"executor\" size=\"40\" value=\"0x0be430662ec0659ee786c04925c0146991fbdc0f\"></td>\n      </tr>\n      <tr>\n        <td>GasLimit</td>\n        <td><input type=\"text\" id=\"gasLimit\" size=\"10\" value=\"100000\"></td>\n      </tr>\n    </tbody>\n  </table>\n\n  <button id=\"signBtn\">Sign data</button>\n  \n  <h3>Signed Data</h3>\n  <div><textarea id='signedData' rows=8 cols=40></textarea></div>\n  \n\n</body>\n  \n"
  },
  {
    "path": "browsertest/sign.js",
    "content": "function parseSignature(signature) {\n  var r = signature.substring(0, 64);\n  var s = signature.substring(64, 128);\n  var v = signature.substring(128, 130);\n\n  return {\n      r: \"0x\" + r,\n      s: \"0x\" + s,\n      v: parseInt(v, 16)\n  }\n}\n\nwindow.onload = function (e) {\n\n  // force the user to unlock their MetaMask\n  if (web3.eth.accounts[0] == null) {\n    alert(\"Please unlock MetaMask first\");\n    web3.currentProvider.enable().catch(alert);\n  }\n\n  var signBtn = document.getElementById(\"signBtn\");\n  signBtn.onclick = function(e) {\n    if (web3.eth.accounts[0] == null) {\n      return;\n    }\n\n    const domain = [\n      { name: \"name\", type: \"string\" },\n      { name: \"version\", type: \"string\" },\n      { name: \"chainId\", type: \"uint256\" },\n      { name: \"verifyingContract\", type: \"address\" },\n      { name: \"salt\", type: \"bytes32\" }\n    ];\n\n    const multiSigTx = [\n      { name: \"destination\", type: \"address\" },\n      { name: \"value\", type: \"uint256\" },\n      { name: \"data\", type: \"bytes\" },\n      { name: \"nonce\", type: \"uint256\" },\n      { name: \"executor\", type: \"address\" },\n      { name: \"gasLimit\", type: \"uint256\" }\n    ];\n\n    const domainData = {\n      name: \"Simple MultiSig\",\n      version: \"1\",\n      chainId: parseInt(web3.version.network, 10),\n      verifyingContract: document.getElementById(\"walletAddress\").value,\n      salt: \"0x251543af6a222378665a76fe38dbceae4871a070b7fdaf5c6c30cf758dc33cc0\"\n    };\n\n    var message = {\n      destination: document.getElementById(\"destination\").value,\n      value: document.getElementById(\"value\").value,\n      data: document.getElementById(\"data\").value,\n      nonce: parseInt(document.getElementById(\"nonce\").value, 10),\n      executor: document.getElementById(\"executor\").value,\n      gasLimit: parseInt(document.getElementById(\"gasLimit\").value, 10),\n    };\n    \n    const data = JSON.stringify({\n      types: {\n        EIP712Domain: domain,\n        MultiSigTransaction: multiSigTx\n      },\n      domain: domainData,\n      primaryType: \"MultiSigTransaction\",\n      message: message\n    });\n\n    console.log(data)\n    \n    const signer = web3.eth.accounts[0];\n\n    console.log(signer)\n    web3.currentProvider.sendAsync(\n      {\n        method: \"eth_signTypedData_v3\",\n        params: [signer, data],\n        from: signer\n      }, \n      function(err, result) {\n        if (err || result.error) {\n          return console.error(result);\n        }\n\n        const signature = parseSignature(result.result.substring(2));\n        document.getElementById(\"signedData\").value = \"r: \" + signature.r + \"\\ns: \" + signature.s + \"\\nv: \" + signature.v\n      }\n    );\n  };\n}\n"
  },
  {
    "path": "contracts/SimpleMultiSig.sol",
    "content": "pragma solidity ^0.4.24;\n\ncontract SimpleMultiSig {\n\n// EIP712 Precomputed hashes:\n// keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)\")\nbytes32 constant EIP712DOMAINTYPE_HASH = 0xd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472;\n\n// keccak256(\"Simple MultiSig\")\nbytes32 constant NAME_HASH = 0xb7a0bfa1b79f2443f4d73ebb9259cddbcd510b18be6fc4da7d1aa7b1786e73e6;\n\n// keccak256(\"1\")\nbytes32 constant VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;\n\n// keccak256(\"MultiSigTransaction(address destination,uint256 value,bytes data,uint256 nonce,address executor,uint256 gasLimit)\")\nbytes32 constant TXTYPE_HASH = 0x3ee892349ae4bbe61dce18f95115b5dc02daf49204cc602458cd4c1f540d56d7;\n\nbytes32 constant SALT = 0x251543af6a222378665a76fe38dbceae4871a070b7fdaf5c6c30cf758dc33cc0;\n\n  uint public nonce;                 // (only) mutable state\n  uint public threshold;             // immutable state\n  mapping (address => bool) isOwner; // immutable state\n  address[] public ownersArr;        // immutable state\n\n  bytes32 DOMAIN_SEPARATOR;          // hash for EIP712, computed from contract address\n  \n  // Note that owners_ must be strictly increasing, in order to prevent duplicates\n  constructor(uint threshold_, address[] owners_, uint chainId) public {\n    require(owners_.length <= 10 && threshold_ <= owners_.length && threshold_ > 0);\n\n    address lastAdd = address(0);\n    for (uint i = 0; i < owners_.length; i++) {\n      require(owners_[i] > lastAdd);\n      isOwner[owners_[i]] = true;\n      lastAdd = owners_[i];\n    }\n    ownersArr = owners_;\n    threshold = threshold_;\n\n    DOMAIN_SEPARATOR = keccak256(abi.encode(EIP712DOMAINTYPE_HASH,\n                                            NAME_HASH,\n                                            VERSION_HASH,\n                                            chainId,\n                                            this,\n                                            SALT));\n  }\n\n  // Note that address recovered from signatures must be strictly increasing, in order to prevent duplicates\n  function execute(uint8[] sigV, bytes32[] sigR, bytes32[] sigS, address destination, uint value, bytes data, address executor, uint gasLimit) public {\n    require(sigR.length == threshold);\n    require(sigR.length == sigS.length && sigR.length == sigV.length);\n    require(executor == msg.sender || executor == address(0));\n\n    // EIP712 scheme: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md\n    bytes32 txInputHash = keccak256(abi.encode(TXTYPE_HASH, destination, value, keccak256(data), nonce, executor, gasLimit));\n    bytes32 totalHash = keccak256(abi.encodePacked(\"\\x19\\x01\", DOMAIN_SEPARATOR, txInputHash));\n\n    address lastAdd = address(0); // cannot have address(0) as an owner\n    for (uint i = 0; i < threshold; i++) {\n      address recovered = ecrecover(totalHash, sigV[i], sigR[i], sigS[i]);\n      require(recovered > lastAdd && isOwner[recovered]);\n      lastAdd = recovered;\n    }\n\n    // If we make it here all signatures are accounted for.\n    // The address.call() syntax is no longer recommended, see:\n    // https://github.com/ethereum/solidity/issues/2884\n    nonce = nonce + 1;\n    bool success = false;\n    assembly { success := call(gasLimit, destination, value, add(data, 0x20), mload(data), 0, 0) }\n    require(success);\n  }\n\n  function () payable external {}\n}\n"
  },
  {
    "path": "contracts/TestRegistry.sol",
    "content": "pragma solidity ^0.4.18;\n\n// This contract is only used for testing purposes.\ncontract TestRegistry {\n\n  mapping(address => uint) public registry;\n\n  function register(uint x) payable public {\n    registry[msg.sender] = x;\n  }\n\n}\n"
  },
  {
    "path": "maurelian_review.md",
    "content": "# Smart contract review by maurelian\n\n## Introduction by christianlundkvist\n\nThis is an informal review of the `SimpleMultisig` smart contract by [maurelian](https://github.com/maurelian). All the issues raised were also fixed by maurelian in subsequent PRs.\n\nThe original review is also available [here](https://gist.github.com/maurelian/f6b842854edec7d02a1f46be1f6e2a67). Some minor stylistic alterations were made.\n\n## Introduction\n\nThis is an informal... you might even say adhoc review of the `SimpleMultisig` contract, found here: <https://github.com/christianlundkvist/simple-multisig/tree/9d486cb280c1b0108a64a0e1c4bc0c636919c2d7>\n\nThis review makes no legally binding guarantees whatsoever. Use at your own risk. \n\n\n## Summary\n\nThe two findings listed under `Major` and `Medium` should be fixed. The `Minor` and `Note` issues don't pose a security risk, but should be fixed to adhere to best practices. \n\nOtherwise, no significant issues were identified. This contract appears to work as advertised, and its simplicity is excellent for the task.\n\n\n## Specific findings\n\n### Major: contract can be \"bricked\" on deployment if same address is used twice\n\nThe contract could be deployed, and instantly unusable. This would occur if the same address is added twice, and the threshold requires all owners to sign in order to execute. The constructor should be modified to prevent this using a similar approach to the `execute` function.\n\n### Medium: Upgrade to solidity ^0.4.14 \n\nPrior to 0.4.14, a bug existed in `ecrecover`. \n\nRef: <https://github.com/ConsenSys/0x_review/blob/final/report/3_general_findings.md#ecrecover-issue-in-solidity-0414>\n\n\n### Minor: use `require` instead of `throw`\n\nIt's easier to read, and `throw` is being deprecated.\n\n### Minor: Indentation of test suite\n\nThe utility functions were not properly indented from line 118 to 148 of `multisig.js`.\n\n### Note: Clarify the value of the requirement that signatures be submitted in ascending order\n\nI belive the purpose is to facilitate checking against duplicate signatures, but it would be nice to make that explicit. "
  },
  {
    "path": "migrations/placeholder.txt",
    "content": "So git adds folder, so truffle runs tests :~)\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"simple-multisig\",\n  \"version\": \"1.0.4\",\n  \"description\": \"Simple Ethereum multisig contract\",\n  \"main\": \"test/simplemultisig.js\",\n  \"directories\": {\n    \"test\": \"test\"\n  },\n  \"scripts\": {\n    \"test\": \"truffle test\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/christianlundkvist/simple-multisig.git\"\n  },\n  \"keywords\": [\n    \"Ethereum\",\n    \"Wallet\"\n  ],\n  \"author\": \"christian.lundkvist@gmail.com\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/christianlundkvist/simple-multisig/issues\"\n  },\n  \"homepage\": \"https://github.com/christianlundkvist/simple-multisig#readme\",\n  \"devDependencies\": {\n    \"eth-lightwallet\": \"*\",\n    \"bluebird\": \"*\"\n  }\n}\n"
  },
  {
    "path": "test/simplemultisig.js",
    "content": "var SimpleMultiSig = artifacts.require(\"./SimpleMultiSig.sol\")\nvar TestRegistry = artifacts.require(\"./TestRegistry.sol\")\nvar lightwallet = require('eth-lightwallet')\nconst Promise = require('bluebird')\n\nconst web3SendTransaction = Promise.promisify(web3.eth.sendTransaction)\nconst web3GetBalance = Promise.promisify(web3.eth.getBalance)\n\nlet DOMAIN_SEPARATOR\nconst TXTYPE_HASH = '0x3ee892349ae4bbe61dce18f95115b5dc02daf49204cc602458cd4c1f540d56d7'\nconst NAME_HASH = '0xb7a0bfa1b79f2443f4d73ebb9259cddbcd510b18be6fc4da7d1aa7b1786e73e6'\nconst VERSION_HASH = '0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6'\nconst EIP712DOMAINTYPE_HASH = '0xd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472'\nconst SALT = '0x251543af6a222378665a76fe38dbceae4871a070b7fdaf5c6c30cf758dc33cc0'\n\n\nconst CHAINID = 1\nconst ZEROADDR = '0x000000000000000000000000000000000000000000000'\n\ncontract('SimpleMultiSig', function(accounts) {\n\n  let keyFromPw\n  let acct\n  let lw\n\n  let createSigs = function(signers, multisigAddr, nonce, destinationAddr, value, data, executor, gasLimit) {\n\n    const domainData = EIP712DOMAINTYPE_HASH + NAME_HASH.slice(2) + VERSION_HASH.slice(2) + CHAINID.toString('16').padStart(64, '0') + multisigAddr.slice(2).padStart(64, '0') + SALT.slice(2)\n    DOMAIN_SEPARATOR = web3.sha3(domainData, {encoding: 'hex'})\n\n    let txInput = TXTYPE_HASH + destinationAddr.slice(2).padStart(64, '0') + value.toString('16').padStart(64, '0') + web3.sha3(data, {encoding: 'hex'}).slice(2) + nonce.toString('16').padStart(64, '0') + executor.slice(2).padStart(64, '0') + gasLimit.toString('16').padStart(64, '0')\n    let txInputHash = web3.sha3(txInput, {encoding: 'hex'})\n    \n    let input = '0x19' + '01' + DOMAIN_SEPARATOR.slice(2) + txInputHash.slice(2)\n    let hash = web3.sha3(input, {encoding: 'hex'})\n    \n    let sigV = []\n    let sigR = []\n    let sigS = []\n\n    for (var i=0; i<signers.length; i++) {\n      let sig = lightwallet.signing.signMsgHash(lw, keyFromPw, hash, signers[i])\n      sigV.push(sig.v)\n      sigR.push('0x' + sig.r.toString('hex'))\n      sigS.push('0x' + sig.s.toString('hex'))\n    }\n\n    // if (signers[0] == acct[0]) {\n    //   console.log(\"Signer: \" + signers[0])\n    //   console.log(\"Wallet address: \" + multisigAddr)\n    //   console.log(\"Destination: \" + destinationAddr)\n    //   console.log(\"Value: \" + value)\n    //   console.log(\"Data: \" + data)\n    //   console.log(\"Nonce: \" + nonce)\n    //   console.log(\"Executor: \" + executor)\n    //   console.log(\"gasLimit: \" + gasLimit)\n    //   console.log(\"r: \" + sigR[0])\n    //   console.log(\"s: \" + sigS[0])\n    //   console.log(\"v: \" + sigV[0])\n    // }\n      \n    return {sigV: sigV, sigR: sigR, sigS: sigS}\n\n  }\n\n  let executeSendSuccess = async function(owners, threshold, signers, done) {\n\n    let multisig = await SimpleMultiSig.new(threshold, owners, CHAINID, {from: accounts[0]})\n    let randomAddr = web3.sha3(Math.random().toString()).slice(0,42)\n    let executor = accounts[0]\n    let msgSender = accounts[0]\n    \n    // Receive funds\n    await web3SendTransaction({from: accounts[0], to: multisig.address, value: web3.toWei(web3.toBigNumber(0.1), 'ether')})\n\n    let nonce = await multisig.nonce.call()\n    assert.equal(nonce.toNumber(), 0)\n\n    let bal = await web3GetBalance(multisig.address)\n    assert.equal(bal, web3.toWei(0.1, 'ether'))\n\n    // check that owners are stored correctly\n    for (var i=0; i<owners.length; i++) {\n      let ownerFromContract = await multisig.ownersArr.call(i)\n      assert.equal(owners[i], ownerFromContract)\n    }\n\n    let value = web3.toWei(web3.toBigNumber(0.01), 'ether')\n\n    let sigs = createSigs(signers, multisig.address, nonce, randomAddr, value, '', executor, 21000)\n\n    await multisig.execute(sigs.sigV, sigs.sigR, sigs.sigS, randomAddr, value, '', executor, 21000, {from: msgSender, gasLimit: 1000000})\n\n    // Check funds sent\n    bal = await web3GetBalance(randomAddr)\n    assert.equal(bal.toString(), value.toString())\n\n    // Check nonce updated\n    nonce = await multisig.nonce.call()\n    assert.equal(nonce.toNumber(), 1)\n\n    // Send again\n    // Check that it succeeds with executor = Zero address\n    sigs = createSigs(signers, multisig.address, nonce, randomAddr, value, '', ZEROADDR, 21000)\n    await multisig.execute(sigs.sigV, sigs.sigR, sigs.sigS, randomAddr, value, '', ZEROADDR, 21000, {from: msgSender, gasLimit: 1000000})\n\n    // Check funds\n    bal = await web3GetBalance(randomAddr)\n    assert.equal(bal.toString(), (value*2).toString())\n\n    // Check nonce updated\n    nonce = await multisig.nonce.call()\n    assert.equal(nonce.toNumber(), 2)\n\n    // Test contract interactions\n    let reg = await TestRegistry.new({from: accounts[0]})\n\n    let number = 12345\n    let data = lightwallet.txutils._encodeFunctionTxData('register', ['uint256'], [number])\n\n    sigs = createSigs(signers, multisig.address, nonce, reg.address, value, data, executor, 100000)\n    await multisig.execute(sigs.sigV, sigs.sigR, sigs.sigS, reg.address, value, data, executor, 100000, {from: msgSender, gasLimit: 1000000})\n\n    // Check that number has been set in registry\n    let numFromRegistry = await reg.registry(multisig.address)\n    assert.equal(numFromRegistry.toNumber(), number)\n\n    // Check funds in registry\n    bal = await web3GetBalance(reg.address)\n    assert.equal(bal.toString(), value.toString())\n\n    // Check nonce updated\n    nonce = await multisig.nonce.call()\n    assert.equal(nonce.toNumber(), 3)\n\n    done()\n  }\n\n  let executeSendFailure = async function(owners, threshold, signers, nonceOffset, executor, gasLimit, done) {\n\n    let multisig = await SimpleMultiSig.new(threshold, owners, CHAINID, {from: accounts[0]})\n\n    let nonce = await multisig.nonce.call()\n    assert.equal(nonce.toNumber(), 0)\n\n    // Receive funds\n    await web3SendTransaction({from: accounts[0], to: multisig.address, value: web3.toWei(web3.toBigNumber(2), 'ether')})\n\n    let randomAddr = web3.sha3(Math.random().toString()).slice(0,42)\n    let value = web3.toWei(web3.toBigNumber(0.1), 'ether')\n    let sigs = createSigs(signers, multisig.address, nonce + nonceOffset, randomAddr, value, '', executor, gasLimit)\n\n    let errMsg = ''\n    try {\n      await multisig.execute(sigs.sigV, sigs.sigR, sigs.sigS, randomAddr, value, '', executor, gasLimit, {from: executor, gasLimit: 1000000})\n    }\n    catch(error) {\n      errMsg = error.message\n    }\n\n    assert.equal(errMsg, 'VM Exception while processing transaction: revert', 'Test did not throw')\n\n    done()\n  }\n\n  let creationFailure = async function(owners, threshold, done) {\n\n    try {\n      await SimpleMultiSig.new(threshold, owners, CHAINID, {from: accounts[0]})\n    }\n    catch(error) {\n      errMsg = error.message\n    }\n\n    assert.equal(errMsg, 'VM Exception while processing transaction: revert', 'Test did not throw')\n\n    done()\n  }\n  \n  before((done) => {\n\n    let seed = \"pull rent tower word science patrol economy legal yellow kit frequent fat\"\n\n    lightwallet.keystore.createVault(\n    {hdPathString: \"m/44'/60'/0'/0\",\n     seedPhrase: seed,\n     password: \"test\",\n     salt: \"testsalt\"\n    },\n    function (err, keystore) {\n\n      lw = keystore\n      lw.keyFromPassword(\"test\", function(e,k) {\n        keyFromPw = k\n\n        lw.generateNewAddress(keyFromPw, 20)\n        let acctWithout0x = lw.getAddresses()\n        acct = acctWithout0x.map((a) => {return a})\n        acct.sort()\n        done()\n      })\n    })\n  })\n\n  describe(\"3 signers, threshold 2\", () => {\n\n    it(\"should succeed with signers 0, 1\", (done) => {\n      let signers = [acct[0], acct[1]]\n      signers.sort()\n      executeSendSuccess(acct.slice(0,3), 2, signers, done)\n    })\n\n    it(\"should succeed with signers 0, 2\", (done) => {\n      let signers = [acct[0], acct[2]]\n      signers.sort()\n      executeSendSuccess(acct.slice(0,3), 2, signers, done)\n    })\n\n    it(\"should succeed with signers 1, 2\", (done) => {\n      let signers = [acct[1], acct[2]]\n      signers.sort()\n      executeSendSuccess(acct.slice(0,3), 2, signers, done)\n    })\n\n    it(\"should fail due to non-owner signer\", (done) => {\n      let signers = [acct[0], acct[3]]\n      signers.sort()\n      executeSendFailure(acct.slice(0,3), 2, signers, 0, accounts[0], 100000, done)\n    })\n\n    it(\"should fail with more signers than threshold\", (done) => {\n      executeSendFailure(acct.slice(0,3), 2, acct.slice(0,3), 0, accounts[0], 100000, done)\n    })\n\n    it(\"should fail with fewer signers than threshold\", (done) => {\n      executeSendFailure(acct.slice(0,3), 2, [acct[0]], 0, accounts[0], 100000, done)\n    })\n\n    it(\"should fail with one signer signing twice\", (done) => {\n      executeSendFailure(acct.slice(0,3), 2, [acct[0], acct[0]], 0, accounts[0], 100000, done)\n    })\n\n    it(\"should fail with signers in wrong order\", (done) => {\n      let signers = [acct[0], acct[1]]\n      signers.sort().reverse() //opposite order it should be\n      executeSendFailure(acct.slice(0,3), 2, signers, 0, accounts[0], 100000, done)\n    })\n\n    it(\"should fail with the wrong nonce\", (done) => {\n      const nonceOffset = 1\n      executeSendFailure(acct.slice(0,3), 2, [acct[0], acct[1]], nonceOffset, accounts[0], 100000, done)\n    })\n    \n  })  \n\n  describe(\"Edge cases\", () => {\n    it(\"should succeed with 10 owners, 10 signers\", (done) => {\n      executeSendSuccess(acct.slice(0,10), 10, acct.slice(0,10), done)\n    })\n\n    it(\"should fail to create with signers 0, 0, 2, and threshold 3\", (done) => { \n      creationFailure([acct[0],acct[0],acct[2]], 3, done)\n    })\n\n    it(\"should fail with 0 signers\", (done) => {\n      executeSendFailure(acct.slice(0,3), 2, [], 0, accounts[0], 100000, done)\n    })\n\n    it(\"should fail with 11 owners\", (done) => {\n      creationFailure(acct.slice(0,11), 2, done)\n    })\n  })\n\n  describe(\"Hash constants\", () => {\n    it(\"uses correct hash for EIP712DOMAINTYPE\", (done) => {\n      const eip712DomainType = 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)'\n      assert.equal(web3.sha3(eip712DomainType), EIP712DOMAINTYPE_HASH)\n      done()\n    })\n\n    it(\"uses correct hash for NAME\", (done) => {\n      assert.equal(web3.sha3('Simple MultiSig'), NAME_HASH)\n      done()\n    })\n\n    it(\"uses correct hash for VERSION\", (done) => {\n      assert.equal(web3.sha3('1'), VERSION_HASH)\n      done()\n    })\n\n    it(\"uses correct hash for MULTISIGTX\", (done) => {\n      const multiSigTxType = 'MultiSigTransaction(address destination,uint256 value,bytes data,uint256 nonce,address executor,uint256 gasLimit)'\n      assert.equal(web3.sha3(multiSigTxType), TXTYPE_HASH)\n      done()\n    })\n  })\n\n  describe(\"Browser MetaMask test\", () => {\n    it(\"Matches the signature from MetaMask\", (done) => {\n\n      // To test in MetaMask:\n      //\n      // Import the following private key in MetaMask:\n      // 0xac6d4b13220cd81f3630b7714f7e205494acc0823fb07a63bb40e65f669cbb9e\n      // It should give the address:\n      // 0x01BF9878a7099b2203838f3a8E7652Ad7B127A26\n      //\n      // Make sure you are on Mainnet with the above account\n      // Load the HTML page located at\n      // browsertest/index.html\n      // and click \"Sign data\" (using the default values).\n      // You should see the signature values r,s,v below:\n\n      const mmSigR = '0x91a622ccbd1c65debc16cfa1761b6200acc42099a19d753c7c59ceb12a8f5cfc'\n      const mmSigS = '0x6814fae69a6cc506b11adf971ca233fbcdbdca312ab96a58eb6b6b6792771fd4'\n      const mmSigV = 27\n\n      const walletAddress = '0xe3de7de481cbde9b4d5f62c6d228ec62277560c8'\n      const destination = '0x8582afea2dd8e47297dbcdcf9ca289756ee21430'\n      const value = web3.toWei(web3.toBigNumber(0.01), 'ether')\n      const data = '0xf207564e0000000000000000000000000000000000000000000000000000000000003039'\n      const nonce = 2\n      const executor = '0x0be430662ec0659ee786c04925c0146991fbdc0f'\n      const gasLimit = 100000\n      const signers = [acct[0]]\n\n      let sigs = createSigs(signers, walletAddress, nonce, destination, value, data, executor, gasLimit)\n      \n      assert.equal(sigs.sigR[0], mmSigR)\n      assert.equal(sigs.sigS[0], mmSigS)\n      assert.equal(sigs.sigV[0], mmSigV)\n      \n      done()\n    })\n  })\n\n  \n  \n})\n"
  },
  {
    "path": "truffle.js",
    "content": "module.exports = {\n  networks: {\n    development: {\n      host: \"localhost\",\n      port: 8545,\n      network_id: \"*\" // Match any network id\n    }\n  }\n};\n"
  }
]