ETH Price: $2,418.75 (-0.98%)
Gas: 0.95 Gwei

Transaction Decoder

Block:
14757112 at May-11-2022 09:00:29 PM +UTC
Transaction Fee:
0.038753939548489554 ETH $93.74
Gas Used:
54,414 Gas / 712.205306511 Gwei

Emitted Events:

13 SmoothLovePotion.Transfer( _from=Forwarder, _to=[Receiver] 0x131a99859a8bfa3251d899f0675607766736ffae, _value=941890 )
14 Forwarder.TokensFlushed( tokenContractAddress=SmoothLovePotion, value=941890 )

Account State Difference:

  Address   Before After State Difference Code
(Hiveon Pool)
17,916.575159796611241891 Eth17,916.575267536331241891 Eth0.00010773972
0x38c7eA86...5353CD202
3.437952544765184165 Eth
Nonce: 429762
3.399198605216694611 Eth
Nonce: 429763
0.038753939548489554
0xCC8Fa225...5D6cAAa25

Execution Trace

0x131a99859a8bfa3251d899f0675607766736ffae.2da03409( )
  • WalletSimple.flushForwarderTokens( forwarderAddress=0x8ad6100c21e5674942BF0644Dd34667CDccc1D68, tokenContractAddress=0xCC8Fa225D80b9c7D42F96e9570156c65D6cAAa25 )
    • Forwarder.flushTokens( tokenContractAddress=0xCC8Fa225D80b9c7D42F96e9570156c65D6cAAa25 )
      • SmoothLovePotion.balanceOf( 0x8ad6100c21e5674942BF0644Dd34667CDccc1D68 ) => ( 941890 )
      • SmoothLovePotion.transfer( _to=0x131A99859a8bfa3251D899F0675607766736FFaE, _value=941890 ) => ( _success=True )
        flushForwarderTokens[WalletSimple (ln:215)]
        File 1 of 3: Forwarder
        pragma solidity ^0.4.14;
        
        /**
         * Contract that exposes the needed erc20 token functions
         */
        
        contract ERC20Interface {
          // Send _value amount of tokens to address _to
          function transfer(address _to, uint256 _value) returns (bool success);
          // Get the account balance of another account with address _owner
          function balanceOf(address _owner) constant returns (uint256 balance);
        }
        
        /**
         * Contract that will forward any incoming Ether to its creator
         */
        contract Forwarder {
          // Address to which any funds sent to this contract will be forwarded
          address public parentAddress;
          event ForwarderDeposited(address from, uint value, bytes data);
        
          event TokensFlushed(
            address tokenContractAddress, // The contract address of the token
            uint value // Amount of token sent
          );
        
          /**
           * Create the contract, and set the destination address to that of the creator
           */
          function Forwarder() {
            parentAddress = msg.sender;
          }
        
          /**
           * Modifier that will execute internal code block only if the sender is a parent of the forwarder contract
           */
          modifier onlyParent {
            if (msg.sender != parentAddress) {
              throw;
            }
            _;
          }
        
          /**
           * Default function; Gets called when Ether is deposited, and forwards it to the destination address
           */
          function() payable {
            if (!parentAddress.call.value(msg.value)(msg.data))
              throw;
            // Fire off the deposited event if we can forward it  
            ForwarderDeposited(msg.sender, msg.value, msg.data);
          }
        
          /**
           * Execute a token transfer of the full balance from the forwarder token to the main wallet contract
           * @param tokenContractAddress the address of the erc20 token contract
           */
          function flushTokens(address tokenContractAddress) onlyParent {
            ERC20Interface instance = ERC20Interface(tokenContractAddress);
            var forwarderAddress = address(this);
            var forwarderBalance = instance.balanceOf(forwarderAddress);
            if (forwarderBalance == 0) {
              return;
            }
            if (!instance.transfer(parentAddress, forwarderBalance)) {
              throw;
            }
            TokensFlushed(tokenContractAddress, forwarderBalance);
          }
        
          /**
           * It is possible that funds were sent to this address before the contract was deployed.
           * We can flush those funds to the destination address.
           */
          function flush() {
            if (!parentAddress.call.value(this.balance)())
              throw;
          }
        }
        
        /**
         * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.
         * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.
         */
        contract WalletSimple {
          // Events
          event Deposited(address from, uint value, bytes data);
          event SafeModeActivated(address msgSender);
          event Transacted(
            address msgSender, // Address of the sender of the message initiating the transaction
            address otherSigner, // Address of the signer (second signature) used to initiate the transaction
            bytes32 operation, // Operation hash (sha3 of toAddress, value, data, expireTime, sequenceId)
            address toAddress, // The address the transaction was sent to
            uint value, // Amount of Wei sent to the address
            bytes data // Data sent when invoking the transaction
          );
          event TokenTransacted(
            address msgSender, // Address of the sender of the message initiating the transaction
            address otherSigner, // Address of the signer (second signature) used to initiate the transaction
            bytes32 operation, // Operation hash (sha3 of toAddress, value, tokenContractAddress, expireTime, sequenceId)
            address toAddress, // The address the transaction was sent to
            uint value, // Amount of token sent
            address tokenContractAddress // The contract address of the token
          );
        
          // Public fields
          address[] public signers; // The addresses that can co-sign transactions on the wallet
          bool public safeMode = false; // When active, wallet may only send to signer addresses
        
          // Internal fields
          uint constant SEQUENCE_ID_WINDOW_SIZE = 10;
          uint[10] recentSequenceIds;
        
          /**
           * Modifier that will execute internal code block only if the sender is an authorized signer on this wallet
           */
          modifier onlysigner {
            if (!isSigner(msg.sender)) {
              throw;
            }
            _;
          }
        
          /**
           * 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
           */
          function WalletSimple(address[] allowedSigners) {
            if (allowedSigners.length != 3) {
              // Invalid number of signers
              throw;
            }
            signers = allowedSigners;
          }
        
          /**
           * Gets called when a transaction is received without calling a method
           */
          function() payable {
            if (msg.value > 0) {
              // Fire deposited event if we are receiving funds
              Deposited(msg.sender, msg.value, msg.data);
            }
          }
        
          /**
           * Create a new contract (and also address) that forwards funds to this contract
           * returns address of newly created forwarder address
           */
          function createForwarder() onlysigner returns (address) {
            return new Forwarder();
          }
        
          /**
           * Execute a multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
           * The signature is a signed form (using eth.sign) of tightly packed toAddress, value, data, expireTime and sequenceId
           * 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 the result of eth.sign on the operationHash sha3(toAddress, value, data, expireTime, sequenceId)
           */
          function sendMultiSig(address toAddress, uint value, bytes data, uint expireTime, uint sequenceId, bytes signature) onlysigner {
            // Verify the other signer
            var operationHash = sha3("ETHER", toAddress, value, data, expireTime, sequenceId);
            
            var otherSigner = verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
        
            // Success, send the transaction
            if (!(toAddress.call.value(value)(data))) {
              // Failed executing transaction
              throw;
            }
            Transacted(msg.sender, otherSigner, operationHash, toAddress, value, data);
          }
          
          /**
           * Execute a multi-signature token transfer from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
           * The signature is a signed form (using eth.sign) of tightly packed toAddress, value, tokenContractAddress, expireTime and sequenceId
           * 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 the result of eth.sign on the operationHash sha3(toAddress, value, tokenContractAddress, expireTime, sequenceId)
           */
          function sendMultiSigToken(address toAddress, uint value, address tokenContractAddress, uint expireTime, uint sequenceId, bytes signature) onlysigner {
            // Verify the other signer
            var operationHash = sha3("ERC20", toAddress, value, tokenContractAddress, expireTime, sequenceId);
            
            var otherSigner = verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
            
            ERC20Interface instance = ERC20Interface(tokenContractAddress);
            if (!instance.transfer(toAddress, value)) {
                throw;
            }
            TokenTransacted(msg.sender, otherSigner, operationHash, toAddress, value, tokenContractAddress);
          }
        
          /**
           * 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 forwarderAddress, address tokenContractAddress) onlysigner {    
            Forwarder forwarder = Forwarder(forwarderAddress);
            forwarder.flushTokens(tokenContractAddress);
          }  
          
          /**
           * Do common multisig verification for both eth sends and erc20token transfers
           *
           * @param toAddress the destination address to send an outgoing transaction
           * @param operationHash the sha3 of the toAddress, value, data/tokenContractAddress and expireTime
           * @param signature the tightly packed signature of r, s, and v as an array of 65 bytes (returned by eth.sign)
           * @param expireTime the number of seconds since 1970 for which this transaction is valid
           * @param sequenceId the unique sequence id obtainable from getNextSequenceId
           * returns address of the address to send tokens or eth to
           */
          function verifyMultiSig(address toAddress, bytes32 operationHash, bytes signature, uint expireTime, uint sequenceId) private returns (address) {
        
            var otherSigner = recoverAddressFromSignature(operationHash, signature);
        
            // Verify if we are in safe mode. In safe mode, the wallet can only send to signers
            if (safeMode && !isSigner(toAddress)) {
              // We are in safe mode and the toAddress is not a signer. Disallow!
              throw;
            }
            // Verify that the transaction has not expired
            if (expireTime < block.timestamp) {
              // Transaction expired
              throw;
            }
        
            // Try to insert the sequence ID. Will throw if the sequence id was invalid
            tryInsertSequenceId(sequenceId);
        
            if (!isSigner(otherSigner)) {
              // Other signer not on this wallet or operation does not match arguments
              throw;
            }
            if (otherSigner == msg.sender) {
              // Cannot approve own transaction
              throw;
            }
        
            return otherSigner;
          }
        
          /**
           * Irrevocably puts contract into safe mode. When in this mode, transactions may only be sent to signing addresses.
           */
          function activateSafeMode() onlysigner {
            safeMode = true;
            SafeModeActivated(msg.sender);
          }
        
          /**
           * Determine if an address is a signer on this wallet
           * @param signer address to check
           * returns boolean indicating whether address is signer or not
           */
          function isSigner(address signer) returns (bool) {
            // Iterate through all signers on the wallet and
            for (uint i = 0; i < signers.length; i++) {
              if (signers[i] == signer) {
                return true;
              }
            }
            return false;
          }
        
          /**
           * Gets the second signer's address using ecrecover
           * @param operationHash the sha3 of the toAddress, value, data/tokenContractAddress and expireTime
           * @param signature the tightly packed signature of r, s, and v as an array of 65 bytes (returned by eth.sign)
           * returns address recovered from the signature
           */
          function recoverAddressFromSignature(bytes32 operationHash, bytes signature) private returns (address) {
            if (signature.length != 65) {
              throw;
            }
            // We need to unpack the signature, which is given as an array of 65 bytes (from eth.sign)
            bytes32 r;
            bytes32 s;
            uint8 v;
            assembly {
              r := mload(add(signature, 32))
              s := mload(add(signature, 64))
              v := and(mload(add(signature, 65)), 255)
            }
            if (v < 27) {
              v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs
            }
            return ecrecover(operationHash, v, r, s);
          }
        
          /**
           * Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted.
           * We collect a window of up to 10 recent sequence ids, and allow any sequence id that is not in the window and
           * greater than the minimum element in the window.
           * @param sequenceId to insert into array of stored ids
           */
          function tryInsertSequenceId(uint sequenceId) onlysigner private {
            // Keep a pointer to the lowest value element in the window
            uint lowestValueIndex = 0;
            for (uint i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
              if (recentSequenceIds[i] == sequenceId) {
                // This sequence ID has been used before. Disallow!
                throw;
              }
              if (recentSequenceIds[i] < recentSequenceIds[lowestValueIndex]) {
                lowestValueIndex = i;
              }
            }
            if (sequenceId < recentSequenceIds[lowestValueIndex]) {
              // The sequence ID being used is lower than the lowest value in the window
              // so we cannot accept it as it may have been used before
              throw;
            }
            if (sequenceId > (recentSequenceIds[lowestValueIndex] + 10000)) {
              // Block sequence IDs which are much higher than the lowest value
              // This prevents people blocking the contract by using very large sequence IDs quickly
              throw;
            }
            recentSequenceIds[lowestValueIndex] = sequenceId;
          }
        
          /**
           * Gets the next available sequence ID for signing when using executeAndConfirm
           * returns the sequenceId one higher than the highest currently stored
           */
          function getNextSequenceId() returns (uint) {
            uint highestSequenceId = 0;
            for (uint i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
              if (recentSequenceIds[i] > highestSequenceId) {
                highestSequenceId = recentSequenceIds[i];
              }
            }
            return highestSequenceId + 1;
          }
        }

        File 2 of 3: SmoothLovePotion
        // File: math/SafeMath.sol
        
        pragma solidity 0.5.17;
        
        
        library SafeMath {
          function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
            c = a + b;
            require(c >= a, "SafeMath: addition overflow");
          }
        
          function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
            require(b <= a, "SafeMath: subtraction overflow");
            return a - b;
          }
        
          function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
            if (a == 0) {
              return 0;
            }
        
            c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
          }
        
          function div(uint256 a, uint256 b) internal pure returns (uint256 c) {
            // Since Solidity automatically asserts when dividing by 0,
            // but we only need it to revert.
            require(b > 0, "SafeMath: division by zero");
            return a / b;
          }
        
          function mod(uint256 a, uint256 b) internal pure returns (uint256 c) {
            // Same reason as `div`.
            require(b > 0, "SafeMath: modulo by zero");
            return a % b;
          }
        }
        
        // File: token/erc20/IERC20.sol
        
        pragma solidity 0.5.17;
        
        
        interface IERC20 {
          event Transfer(address indexed _from, address indexed _to, uint256 _value);
          event Approval(address indexed _owner, address indexed _spender, uint256 _value);
        
          function totalSupply() external view returns (uint256 _supply);
          function balanceOf(address _owner) external view returns (uint256 _balance);
        
          function approve(address _spender, uint256 _value) external returns (bool _success);
          function allowance(address _owner, address _spender) external view returns (uint256 _value);
        
          function transfer(address _to, uint256 _value) external returns (bool _success);
          function transferFrom(address _from, address _to, uint256 _value) external returns (bool _success);
        }
        
        // File: token/erc20/ERC20.sol
        
        pragma solidity 0.5.17;
        
        
        
        
        contract ERC20 is IERC20 {
          using SafeMath for uint256;
        
          uint256 public totalSupply;
          mapping (address => uint256) public balanceOf;
          mapping (address => mapping (address => uint256)) internal _allowance;
        
          function approve(address _spender, uint256 _value) public returns (bool) {
            _approve(msg.sender, _spender, _value);
            return true;
          }
        
          function allowance(address _owner, address _spender) public view returns (uint256) {
            return _allowance[_owner][_spender];
          }
        
          function increaseAllowance(address _spender, uint256 _value) public returns (bool) {
            _approve(msg.sender, _spender, _allowance[msg.sender][_spender].add(_value));
            return true;
          }
        
          function decreaseAllowance(address _spender, uint256 _value) public returns (bool) {
            _approve(msg.sender, _spender, _allowance[msg.sender][_spender].sub(_value));
            return true;
          }
        
          function transfer(address _to, uint256 _value) public returns (bool _success) {
            _transfer(msg.sender, _to, _value);
            return true;
          }
        
          function transferFrom(address _from, address _to, uint256 _value) public returns (bool _success) {
            _transfer(_from, _to, _value);
            _approve(_from, msg.sender, _allowance[_from][msg.sender].sub(_value));
            return true;
          }
        
          function _approve(address _owner, address _spender, uint256 _amount) internal {
            require(_owner != address(0), "ERC20: approve from the zero address");
            require(_spender != address(0), "ERC20: approve to the zero address");
        
            _allowance[_owner][_spender] = _amount;
            emit Approval(_owner, _spender, _amount);
          }
        
          function _transfer(address _from, address _to, uint256 _value) internal {
            require(_from != address(0), "ERC20: transfer from the zero address");
            require(_to != address(0), "ERC20: transfer to the zero address");
            require(_to != address(this), "ERC20: transfer to this contract address");
        
            balanceOf[_from] = balanceOf[_from].sub(_value);
            balanceOf[_to] = balanceOf[_to].add(_value);
            emit Transfer(_from, _to, _value);
          }
        }
        
        // File: token/erc20/IERC20Detailed.sol
        
        pragma solidity 0.5.17;
        
        
        interface IERC20Detailed {
          function name() external view returns (string memory _name);
          function symbol() external view returns (string memory _symbol);
          function decimals() external view returns (uint8 _decimals);
        }
        
        // File: token/erc20/ERC20Detailed.sol
        
        pragma solidity 0.5.17;
        
        
        
        
        contract ERC20Detailed is ERC20, IERC20Detailed {
          string public name;
          string public symbol;
          uint8 public decimals;
        
          constructor(string memory _name, string memory _symbol, uint8 _decimals) public {
            name = _name;
            symbol = _symbol;
            decimals = _decimals;
          }
        }
        
        // File: token/erc20/ERC20GatewayWhitelist.sol
        
        pragma solidity 0.5.17;
        
        
        contract ERC20GatewayWhitelist is ERC20 {
          address public mainchainGateway;
        
          function allowance(address _owner, address _spender)
            public
            view
            returns (uint256 _value)
          {
            if (_spender == mainchainGateway) return uint256(-1);
        
            return _allowance[_owner][_spender];
          }
        
          function transferFrom(
            address _from,
            address _to,
            uint256 _value
          )
            public
            returns (bool _success)
          {
            if (allowance(_from, msg.sender) != uint256(-1)) {
              super._approve(_from, msg.sender, _allowance[_from][msg.sender].sub(_value));
            }
        
            _transfer(_from, _to, _value);
            return true;
          }
        
          function _setGateway(address _mainchainGateway) internal {
            require(
              _mainchainGateway != address(0),
              "ERC20GatewayWhitelist: setting gateway to the zero address"
            );
            mainchainGateway = _mainchainGateway;
          }
        }
        
        // File: access/HasAdmin.sol
        
        pragma solidity 0.5.17;
        
        
        contract HasAdmin {
          event AdminChanged(address indexed _oldAdmin, address indexed _newAdmin);
          event AdminRemoved(address indexed _oldAdmin);
        
          address public admin;
        
          modifier onlyAdmin {
            require(msg.sender == admin, "HasAdmin: not admin");
            _;
          }
        
          constructor() internal {
            admin = msg.sender;
            emit AdminChanged(address(0), admin);
          }
        
          function changeAdmin(address _newAdmin) external onlyAdmin {
            require(_newAdmin != address(0), "HasAdmin: new admin is the zero address");
            emit AdminChanged(admin, _newAdmin);
            admin = _newAdmin;
          }
        
          function removeAdmin() external onlyAdmin {
            emit AdminRemoved(admin);
            admin = address(0);
          }
        }
        
        // File: access/HasMinters.sol
        
        pragma solidity 0.5.17;
        
        
        
        contract HasMinters is HasAdmin {
          event MinterAdded(address indexed _minter);
          event MinterRemoved(address indexed _minter);
        
          address[] public minters;
          mapping (address => bool) public minter;
        
          modifier onlyMinter {
            require(minter[msg.sender]);
            _;
          }
        
          function addMinters(address[] memory _addedMinters) public onlyAdmin {
            address _minter;
        
            for (uint256 i = 0; i < _addedMinters.length; i++) {
              _minter = _addedMinters[i];
        
              if (!minter[_minter]) {
                minters.push(_minter);
                minter[_minter] = true;
                emit MinterAdded(_minter);
              }
            }
          }
        
          function removeMinters(address[] memory _removedMinters) public onlyAdmin {
            address _minter;
        
            for (uint256 i = 0; i < _removedMinters.length; i++) {
              _minter = _removedMinters[i];
        
              if (minter[_minter]) {
                minter[_minter] = false;
                emit MinterRemoved(_minter);
              }
            }
        
            uint256 i = 0;
        
            while (i < minters.length) {
              _minter = minters[i];
        
              if (!minter[_minter]) {
                minters[i] = minters[minters.length - 1];
                delete minters[minters.length - 1];
                minters.length--;
              } else {
                i++;
              }
            }
          }
        
          function isMinter(address _addr) public view returns (bool) {
            return minter[_addr];
          }
        }
        
        // File: token/erc20/ERC20Mintable.sol
        
        pragma solidity 0.5.17;
        
        
        
        
        contract ERC20Mintable is HasMinters, ERC20 {
          function mint(address _to, uint256 _value) public onlyMinter returns (bool _success) {
            return _mint(_to, _value);
          }
        
          function _mint(address _to, uint256 _value) internal returns (bool success) {
            totalSupply = totalSupply.add(_value);
            balanceOf[_to] = balanceOf[_to].add(_value);
            emit Transfer(address(0), _to, _value);
            return true;
          }
        }
        
        // File: SmoothLovePotion.sol
        
        pragma solidity 0.5.17;
        
        
        
        
        
        contract SmoothLovePotion is ERC20Detailed, ERC20Mintable, ERC20GatewayWhitelist {
          constructor(address _mainchainGateway)
            public
            ERC20Detailed("Smooth Love Potion", "SLP", 0)
          {
            _setGateway(_mainchainGateway);
        
            address[] memory _minters = new address[](1);
            _minters[0] = _mainchainGateway;
            addMinters(_minters);
          }
        }

        File 3 of 3: WalletSimple
        pragma solidity ^0.4.14;
        
        /**
         * Contract that exposes the needed erc20 token functions
         */
        
        contract ERC20Interface {
          // Send _value amount of tokens to address _to
          function transfer(address _to, uint256 _value) returns (bool success);
          // Get the account balance of another account with address _owner
          function balanceOf(address _owner) constant returns (uint256 balance);
        }
        
        /**
         * Contract that will forward any incoming Ether to its creator
         */
        contract Forwarder {
          // Address to which any funds sent to this contract will be forwarded
          address public parentAddress;
          event ForwarderDeposited(address from, uint value, bytes data);
        
          event TokensFlushed(
            address tokenContractAddress, // The contract address of the token
            uint value // Amount of token sent
          );
        
          /**
           * Create the contract, and set the destination address to that of the creator
           */
          function Forwarder() {
            parentAddress = msg.sender;
          }
        
          /**
           * Modifier that will execute internal code block only if the sender is a parent of the forwarder contract
           */
          modifier onlyParent {
            if (msg.sender != parentAddress) {
              throw;
            }
            _;
          }
        
          /**
           * Default function; Gets called when Ether is deposited, and forwards it to the destination address
           */
          function() payable {
            if (!parentAddress.call.value(msg.value)(msg.data))
              throw;
            // Fire off the deposited event if we can forward it  
            ForwarderDeposited(msg.sender, msg.value, msg.data);
          }
        
          /**
           * Execute a token transfer of the full balance from the forwarder token to the main wallet contract
           * @param tokenContractAddress the address of the erc20 token contract
           */
          function flushTokens(address tokenContractAddress) onlyParent {
            ERC20Interface instance = ERC20Interface(tokenContractAddress);
            var forwarderAddress = address(this);
            var forwarderBalance = instance.balanceOf(forwarderAddress);
            if (forwarderBalance == 0) {
              return;
            }
            if (!instance.transfer(parentAddress, forwarderBalance)) {
              throw;
            }
            TokensFlushed(tokenContractAddress, forwarderBalance);
          }
        
          /**
           * It is possible that funds were sent to this address before the contract was deployed.
           * We can flush those funds to the destination address.
           */
          function flush() {
            if (!parentAddress.call.value(this.balance)())
              throw;
          }
        }
        
        /**
         * Basic multi-signer wallet designed for use in a co-signing environment where 2 signatures are required to move funds.
         * Typically used in a 2-of-3 signing configuration. Uses ecrecover to allow for 2 signatures in a single transaction.
         */
        contract WalletSimple {
          // Events
          event Deposited(address from, uint value, bytes data);
          event SafeModeActivated(address msgSender);
          event Transacted(
            address msgSender, // Address of the sender of the message initiating the transaction
            address otherSigner, // Address of the signer (second signature) used to initiate the transaction
            bytes32 operation, // Operation hash (sha3 of toAddress, value, data, expireTime, sequenceId)
            address toAddress, // The address the transaction was sent to
            uint value, // Amount of Wei sent to the address
            bytes data // Data sent when invoking the transaction
          );
          event TokenTransacted(
            address msgSender, // Address of the sender of the message initiating the transaction
            address otherSigner, // Address of the signer (second signature) used to initiate the transaction
            bytes32 operation, // Operation hash (sha3 of toAddress, value, tokenContractAddress, expireTime, sequenceId)
            address toAddress, // The address the transaction was sent to
            uint value, // Amount of token sent
            address tokenContractAddress // The contract address of the token
          );
        
          // Public fields
          address[] public signers; // The addresses that can co-sign transactions on the wallet
          bool public safeMode = false; // When active, wallet may only send to signer addresses
        
          // Internal fields
          uint constant SEQUENCE_ID_WINDOW_SIZE = 10;
          uint[10] recentSequenceIds;
        
          /**
           * Modifier that will execute internal code block only if the sender is an authorized signer on this wallet
           */
          modifier onlysigner {
            if (!isSigner(msg.sender)) {
              throw;
            }
            _;
          }
        
          /**
           * 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
           */
          function WalletSimple(address[] allowedSigners) {
            if (allowedSigners.length != 3) {
              // Invalid number of signers
              throw;
            }
            signers = allowedSigners;
          }
        
          /**
           * Gets called when a transaction is received without calling a method
           */
          function() payable {
            if (msg.value > 0) {
              // Fire deposited event if we are receiving funds
              Deposited(msg.sender, msg.value, msg.data);
            }
          }
        
          /**
           * Create a new contract (and also address) that forwards funds to this contract
           * returns address of newly created forwarder address
           */
          function createForwarder() onlysigner returns (address) {
            return new Forwarder();
          }
        
          /**
           * Execute a multi-signature transaction from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
           * The signature is a signed form (using eth.sign) of tightly packed toAddress, value, data, expireTime and sequenceId
           * 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 the result of eth.sign on the operationHash sha3(toAddress, value, data, expireTime, sequenceId)
           */
          function sendMultiSig(address toAddress, uint value, bytes data, uint expireTime, uint sequenceId, bytes signature) onlysigner {
            // Verify the other signer
            var operationHash = sha3("ETHER", toAddress, value, data, expireTime, sequenceId);
            
            var otherSigner = verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
        
            // Success, send the transaction
            if (!(toAddress.call.value(value)(data))) {
              // Failed executing transaction
              throw;
            }
            Transacted(msg.sender, otherSigner, operationHash, toAddress, value, data);
          }
          
          /**
           * Execute a multi-signature token transfer from this wallet using 2 signers: one from msg.sender and the other from ecrecover.
           * The signature is a signed form (using eth.sign) of tightly packed toAddress, value, tokenContractAddress, expireTime and sequenceId
           * 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 the result of eth.sign on the operationHash sha3(toAddress, value, tokenContractAddress, expireTime, sequenceId)
           */
          function sendMultiSigToken(address toAddress, uint value, address tokenContractAddress, uint expireTime, uint sequenceId, bytes signature) onlysigner {
            // Verify the other signer
            var operationHash = sha3("ERC20", toAddress, value, tokenContractAddress, expireTime, sequenceId);
            
            var otherSigner = verifyMultiSig(toAddress, operationHash, signature, expireTime, sequenceId);
            
            ERC20Interface instance = ERC20Interface(tokenContractAddress);
            if (!instance.transfer(toAddress, value)) {
                throw;
            }
            TokenTransacted(msg.sender, otherSigner, operationHash, toAddress, value, tokenContractAddress);
          }
        
          /**
           * 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 forwarderAddress, address tokenContractAddress) onlysigner {    
            Forwarder forwarder = Forwarder(forwarderAddress);
            forwarder.flushTokens(tokenContractAddress);
          }  
          
          /**
           * Do common multisig verification for both eth sends and erc20token transfers
           *
           * @param toAddress the destination address to send an outgoing transaction
           * @param operationHash the sha3 of the toAddress, value, data/tokenContractAddress and expireTime
           * @param signature the tightly packed signature of r, s, and v as an array of 65 bytes (returned by eth.sign)
           * @param expireTime the number of seconds since 1970 for which this transaction is valid
           * @param sequenceId the unique sequence id obtainable from getNextSequenceId
           * returns address of the address to send tokens or eth to
           */
          function verifyMultiSig(address toAddress, bytes32 operationHash, bytes signature, uint expireTime, uint sequenceId) private returns (address) {
        
            var otherSigner = recoverAddressFromSignature(operationHash, signature);
        
            // Verify if we are in safe mode. In safe mode, the wallet can only send to signers
            if (safeMode && !isSigner(toAddress)) {
              // We are in safe mode and the toAddress is not a signer. Disallow!
              throw;
            }
            // Verify that the transaction has not expired
            if (expireTime < block.timestamp) {
              // Transaction expired
              throw;
            }
        
            // Try to insert the sequence ID. Will throw if the sequence id was invalid
            tryInsertSequenceId(sequenceId);
        
            if (!isSigner(otherSigner)) {
              // Other signer not on this wallet or operation does not match arguments
              throw;
            }
            if (otherSigner == msg.sender) {
              // Cannot approve own transaction
              throw;
            }
        
            return otherSigner;
          }
        
          /**
           * Irrevocably puts contract into safe mode. When in this mode, transactions may only be sent to signing addresses.
           */
          function activateSafeMode() onlysigner {
            safeMode = true;
            SafeModeActivated(msg.sender);
          }
        
          /**
           * Determine if an address is a signer on this wallet
           * @param signer address to check
           * returns boolean indicating whether address is signer or not
           */
          function isSigner(address signer) returns (bool) {
            // Iterate through all signers on the wallet and
            for (uint i = 0; i < signers.length; i++) {
              if (signers[i] == signer) {
                return true;
              }
            }
            return false;
          }
        
          /**
           * Gets the second signer's address using ecrecover
           * @param operationHash the sha3 of the toAddress, value, data/tokenContractAddress and expireTime
           * @param signature the tightly packed signature of r, s, and v as an array of 65 bytes (returned by eth.sign)
           * returns address recovered from the signature
           */
          function recoverAddressFromSignature(bytes32 operationHash, bytes signature) private returns (address) {
            if (signature.length != 65) {
              throw;
            }
            // We need to unpack the signature, which is given as an array of 65 bytes (from eth.sign)
            bytes32 r;
            bytes32 s;
            uint8 v;
            assembly {
              r := mload(add(signature, 32))
              s := mload(add(signature, 64))
              v := and(mload(add(signature, 65)), 255)
            }
            if (v < 27) {
              v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs
            }
            return ecrecover(operationHash, v, r, s);
          }
        
          /**
           * Verify that the sequence id has not been used before and inserts it. Throws if the sequence ID was not accepted.
           * We collect a window of up to 10 recent sequence ids, and allow any sequence id that is not in the window and
           * greater than the minimum element in the window.
           * @param sequenceId to insert into array of stored ids
           */
          function tryInsertSequenceId(uint sequenceId) onlysigner private {
            // Keep a pointer to the lowest value element in the window
            uint lowestValueIndex = 0;
            for (uint i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
              if (recentSequenceIds[i] == sequenceId) {
                // This sequence ID has been used before. Disallow!
                throw;
              }
              if (recentSequenceIds[i] < recentSequenceIds[lowestValueIndex]) {
                lowestValueIndex = i;
              }
            }
            if (sequenceId < recentSequenceIds[lowestValueIndex]) {
              // The sequence ID being used is lower than the lowest value in the window
              // so we cannot accept it as it may have been used before
              throw;
            }
            if (sequenceId > (recentSequenceIds[lowestValueIndex] + 10000)) {
              // Block sequence IDs which are much higher than the lowest value
              // This prevents people blocking the contract by using very large sequence IDs quickly
              throw;
            }
            recentSequenceIds[lowestValueIndex] = sequenceId;
          }
        
          /**
           * Gets the next available sequence ID for signing when using executeAndConfirm
           * returns the sequenceId one higher than the highest currently stored
           */
          function getNextSequenceId() returns (uint) {
            uint highestSequenceId = 0;
            for (uint i = 0; i < SEQUENCE_ID_WINDOW_SIZE; i++) {
              if (recentSequenceIds[i] > highestSequenceId) {
                highestSequenceId = recentSequenceIds[i];
              }
            }
            return highestSequenceId + 1;
          }
        }