Skip to content

Instantly share code, notes, and snippets.

@devrandom
Created June 18, 2018 17:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devrandom/5b476ddc794feeb813cbe2c79e3c6927 to your computer and use it in GitHub Desktop.
Save devrandom/5b476ddc794feeb813cbe2c79e3c6927 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.4.24+commit.e67f0147.js&optimize=false&gist=
pragma solidity ^0.4.22;
contract SimpleMultiSig {
uint public nonce; // (only) mutable state
uint public threshold; // immutable state
mapping (address => bool) isOwner; // immutable state
address[] public ownersArr; // immutable state
// Note that owners_ must be strictly increasing, in order to prevent duplicates
constructor(uint threshold_, address[] owners_) public {
require(owners_.length <= 10 && threshold_ <= owners_.length && threshold_ >= 0);
address lastAdd = address(0);
for (uint i = 0; i < owners_.length; i++) {
require(owners_[i] > lastAdd);
isOwner[owners_[i]] = true;
lastAdd = owners_[i];
}
ownersArr = owners_;
threshold = threshold_;
}
// Note that address recovered from signatures must be strictly increasing, in order to prevent duplicates
function execute(uint8[] sigV, bytes32[] sigR, bytes32[] sigS, address destination, uint value, bytes data) public {
require(sigR.length == threshold);
require(sigR.length == sigS.length && sigR.length == sigV.length);
// Follows ERC191 signature scheme: https://github.com/ethereum/EIPs/issues/191
bytes32 txHash = keccak256(abi.encodePacked(byte(0x19), byte(0), this, destination, value, data, nonce));
address lastAdd = address(0); // cannot have address(0) as an owner
for (uint i = 0; i < threshold; i++) {
address recovered = ecrecover(txHash, sigV[i], sigR[i], sigS[i]);
require(recovered > lastAdd && isOwner[recovered]);
lastAdd = recovered;
}
// If we make it here all signatures are accounted for.
// The address.call() syntax is no longer recommended, see:
// https://github.com/ethereum/solidity/issues/2884
nonce = nonce + 1;
bool success = false;
assembly { success := call(gas, destination, value, add(data, 0x20), mload(data), 0, 0) }
require(success);
}
function () payable public {}
}
pragma solidity ^0.4.18;
// This contract is only used for testing purposes.
contract TestRegistry {
mapping(address => uint) public registry;
function register(uint x) payable public {
registry[msg.sender] = x;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment