/** * Source Code first verified at https://etherscan.io on Monday, May 6, 2019 (UTC) */ pragma solidity ^0.5.3; /** * @author ESPAY PTY LTD. */ /** * @title ERC223Interface * @dev ERC223 Contract Interface */ contract ERC20Interface { function transfer(address _to, uint256 _value) public returns (bool); function balanceOf(address who)public view returns (uint); } /** * @title Forwarder * @dev Contract that will forward any incoming Ether & token to wallet */ contract Forwarder { address payable public parentAddress; event ForwarderDeposited(address from, uint value, bytes data); event TokensFlushed(address forwarderAddress, uint value, address tokenContractAddress); /** * @dev Modifier that will execute internal code block only if the sender is the parent address */ modifier onlyParent { require(msg.sender == parentAddress); _; } /** * @dev Create the contract, and sets the destination address to that of the creator */ constructor() public{ parentAddress = msg.sender; } /** * @dev Default function; Gets called when Ether is deposited, and forwards it to the parent address. * Credit eth to contract creator. */ function() external payable { parentAddress.transfer(msg.value); emit ForwarderDeposited(msg.sender, msg.value, msg.data); } /** * @dev Execute a token transfer of the full balance from the forwarder token to the parent address * @param tokenContractAddress the address of the erc20 token contract */ function flushTokens(address tokenContractAddress) public onlyParent { ERC20Interface instance = ERC20Interface(tokenContractAddress); uint forwarderBalance = instance.balanceOf(address(this)); require(forwarderBalance > 0); require(instance.transfer(parentAddress, forwarderBalance)); emit TokensFlushed(address(this), forwarderBalance, tokenContractAddress); } /** * @dev Execute a specified token transfer from the forwarder token to the parent address. * @param _from the address of the erc20 token contract. * @param _value the amount of token. */ function flushToken(address _from, uint _value) external{ require(ERC20Interface(_from).transfer(parentAddress, _value), "instance error"); } /** * @dev It is possible that funds were sent to this address before the contract was deployed. * We can flush those funds to the parent address. */ function flush() public { parentAddress.transfer(address(this).balance); } } /** * @title MultiSignWallet */ contract MultiSignWallet { address[] public signers; bool public safeMode; uint forwarderCount; uint lastsequenceId; event Deposited(address from, uint value, bytes data); event SafeModeActivated(address msgSender); event SafeModeInActivated(address msgSender); event ForwarderCreated(address forwarderAddress); event Transacted(address msgSender, address otherSigner, bytes32 operation, address toAddress, uint value, bytes data); event TokensTransfer(address tokenContractAddress, uint value); /** * @dev Modifier that will execute internal code block only if the * sender is an authorized signer on this wallet */ modifier onlySigner { require(isSigner(msg.sender)); _; } /** * @dev Set up a simple multi-sig wallet by specifying the signers allowed to be used on this wallet. * 2 signers will be required to send a transaction from this wallet. * Note: The sender is NOT automatically added to the list of signers. * Signers CANNOT be changed once they are set * @param allowedSigners An array of signers on the wallet */ constructor(address[] memory allowedSigners) public { require(allowedSigners.length == 3); signers = allowedSigners; } /** * @dev Gets called when a transaction is received without calling a method */ function() external payable { if(msg.value > 0){ emit Deposited(msg.sender, msg.value, msg.data); } } /** * @dev Determine if an address is a signer on this wallet * @param signer address to check * @return boolean indicating whether address is signer or not */ function isSigner(address signer) public view returns (bool) { for (uint i = 0; i < signers.length; i++) { if (signers[i] == signer) { return true; } } return false; } /** * @dev Irrevocably puts contract into safe mode. When in this mode, * transactions may only be sent to signing addresses. */ function activateSafeMode() public onlySigner { require(!safeMode); safeMode = true; emit SafeModeActivated(msg.sender); } /** * @dev Irrevocably puts out contract into safe mode. */ function turnOffSafeMode() public onlySigner { require(safeMode); safeMode = false; emit SafeModeInActivated(msg.sender); } /** * @dev Create a new contract (and also address) that forwards funds to this contract * returns address of newly created forwarder address */ function createForwarder() public returns (address) { Forwarder f = new Forwarder(); forwarderCount += 1; emit ForwarderCreated(address(f)); return(address(f)); } /** * @dev for return No of forwarder generated. * @return total number of generated forwarder count. */ function getForwarder() public view returns(uint){ return forwarderCount; } /** * @dev Execute a token flush from one of the forwarder addresses. * This transfer needs only a single signature and can be done by any signer * @param forwarderAddress the address of the forwarder address to flush the tokens from * @param tokenContractAddress the address of the erc20 token contract */ function flushForwarderTokens(address payable forwarderAddress, address tokenContractAddress) public onlySigner { Forwarder forwarder = Forwarder(forwarderAddress); forwarder.flushTokens(tokenContractAddress); } /** * @dev Gets the next available sequence ID for signing when using executeAndConfirm * @return the sequenceId one higher than the highest currently stored */ function getNextSequenceId() public view returns (uint) { return lastsequenceId+1; } /** * @dev generate the hash for sendMultiSig * same parameter as sendMultiSig * @return the hash generated by parameters */ function getHash(address toAddress, uint value, bytes memory data, uint expireTime, uint sequenceId)public pure returns (bytes32){ return keccak256(abi.encodePacked("ETHER", toAddress, value, data, expireTime, sequenceId)); } /** * @dev Execute a multi-signature transaction from this wallet using 2 signers: * one from msg.sender and the other from ecrecover. * Sequence IDs are numbers starting from 1. They are used to prevent replay * attacks and may not be repeated. * @param toAddress the destination address to send an outgoing transaction * @param value the amount in Wei to be sent * @param data the data to send to the toAddress when invoking the transaction * @param expireTime the number of seconds since 1970 for which this transaction is valid * @param sequenceId the unique sequence id obtainable from getNextSequenceId * @param signature see Data Formats */ function sendMultiSig(address payable toAddress, uint value, bytes memory data, uint expireTime, uint sequenceId, bytes memory signature) public payable onlySigner { bytes32 operationHash = keccak256(abi.encodePacked("ETHER", toAddress, value, data, expireTime, sequenceId)); address otherSigner = verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId); toAddress.transfer(value); emit Transacted(msg.sender, otherSigner, operationHash, toAddress, value, data); } /** * @dev generate the hash for sendMultiSigToken and sendMultiSigForwarder. * same parameter as sendMultiSigToken and sendMultiSigForwarder. * @return the hash generated by parameters */ function getTokenHash( address toAddress, uint value, address tokenContractAddress, uint expireTime, uint sequenceId) public pure returns (bytes32){ return keccak256(abi.encodePacked("ERC20", toAddress, value, tokenContractAddress, expireTime, sequenceId)); } /** * @dev Execute a multi-signature token transfer from this wallet using 2 signers: * one from msg.sender and the other from ecrecover. * Sequence IDs are numbers starting from 1. They are used to prevent replay * attacks and may not be repeated. * @param toAddress the destination address to send an outgoing transaction * @param value the amount in tokens to be sent * @param tokenContractAddress the address of the erc20 token contract * @param expireTime the number of seconds since 1970 for which this transaction is valid * @param sequenceId the unique sequence id obtainable from getNextSequenceId * @param signature see Data Formats */ function sendMultiSigToken(address toAddress, uint value, address tokenContractAddress, uint expireTime, uint sequenceId, bytes memory signature) public onlySigner { bytes32 operationHash = keccak256(abi.encodePacked("ERC20", toAddress, value, tokenContractAddress, expireTime, sequenceId)); verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId); ERC20Interface instance = ERC20Interface(tokenContractAddress); require(instance.balanceOf(address(this)) > 0); require(instance.transfer(toAddress, value)); emit TokensTransfer(tokenContractAddress, value); } /** * @dev Gets signer's address using ecrecover * @param operationHash see Data Formats * @param signature see Data Formats * @return address recovered from the signature */ function recoverAddressFromSignature(bytes32 operationHash, bytes memory signature) private pure returns (address) { require(signature.length == 65); bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(signature, 32)) s := mload(add(signature, 64)) v := byte(0, mload(add(signature, 96))) } if (v < 27) { v += 27; } return ecrecover(operationHash, v, r, s); } /** * @dev Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted. * @param sequenceId to insert into array of stored ids */ function tryInsertSequenceId(uint sequenceId) private onlySigner { require(sequenceId > lastsequenceId && sequenceId <= (lastsequenceId+1000), "Enter Valid sequenceId"); lastsequenceId=sequenceId; } /** * @dev Do common multisig verification for both eth sends and erc20token transfers * @param toAddress the destination address to send an outgoing transaction * @param operationHash see Data Formats * @param signature see Data Formats * @param expireTime the number of seconds since 1970 for which this transaction is valid * @param sequenceId the unique sequence id obtainable from getNextSequenceId * @return address that has created the signature */ function verifyMultiSig(address toAddress, bytes32 operationHash, bytes memory signature, uint expireTime, uint sequenceId) private returns (address) { address otherSigner = recoverAddressFromSignature(operationHash, signature); if (safeMode && !isSigner(toAddress)) { revert("safemode error"); } require(isSigner(otherSigner) && expireTime > now); require(otherSigner != msg.sender); tryInsertSequenceId(sequenceId); return otherSigner; } }