ETH Price: $2,010.55 (+1.33%)

Transaction Decoder

Block:
10547403 at Jul-28-2020 10:06:07 AM +UTC
Transaction Fee:
0.0153853875 ETH $30.93
Gas Used:
175,833 Gas / 87.5 Gwei

Emitted Events:

8 ProxyERC20.Approval( owner=Proxy, spender=0xE4C91949...641c6eD2E, value=5600497822028625040004 )
9 Proxy.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x000000000000000000000000103675510a219bd84ce91d1bcb82ca194d665a09, 0x00000000000000000000000057ab1ec28d129707052df4df418d58a2d46d5f51, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000044, 095ea7b3000000000000000000000000e4c9194962532feb467dce8b3d424196, 41c6ed2e00000000000000000000000000000000000000000000012f9a85386d, 050f528400000000000000000000000000000000000000000000000000000000 )
10 TransferManager.Approved( wallet=Proxy, token=ProxyERC20, amount=5600497822028625040004, spender=0xE4C91949...641c6eD2E )
11 Proxy.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x000000000000000000000000103675510a219bd84ce91d1bcb82ca194d665a09, 0x0000000000000000000000000385b3f162a0e001b60ecb84d3cb06199d78f666, 0x0000000000000000000000000000000000000000000000000027fe094d7e5000, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000000 )
12 TransferManager.TransactionExecuted( wallet=Proxy, success=True, signedHash=DDD2BEB069657A0599E72C3F1C8A0FA1E6C62F786AE4D67C41D0B403B30120FA )

Account State Difference:

  Address   Before After State Difference Code
0x0385b3F1...99d78f666
(Argent: Relayer 2)
3.0156612133 Eth
Nonce: 98466
3.0115326658 Eth
Nonce: 98467
0.0041285475
0x05a9CBe7...E0e5343e8
(Synthetix: Token State sUSD)
0x10367551...94D665a09
(Argent: Transfer Manager)
0x4bAaeaA6...4d2a9F8f1 1.694074129463783899 Eth1.682817289463783899 Eth0.01125684
0xAe38b814...603D84480
(Ethermine)
1,118.597776418684510615 Eth1,118.613161806184510615 Eth0.0153853875

Execution Trace

TransferManager.execute( _wallet=0x4bAaeaA617A9Ee657E7ab63DcF3c0d24d2a9F8f1, _data=0x12EF080D0000000000000000000000004BAAEAA617A9EE657E7AB63DCF3C0D24D2A9F8F100000000000000000000000057AB1EC28D129707052DF4DF418D58A2D46D5F51000000000000000000000000E4C9194962532FEB467DCE8B3D42419641C6ED2E00000000000000000000000000000000000000000000012F9A85386D050F5284, _nonce=3589094577144273270472060797800439466875156618, _signatures=0x07CE5B6929108CFFDEB5685D467053419799DAD17E1B77D1207169E9731957C06223DAB5328B4E094991F2A6D4AC2D6D9AC0CF0447ACFA19941EEA41E10B9FE11C, _gasPrice=70000000000, _gasLimit=1000000 ) => ( success=True )
  • Proxy.d6eb1bbf( )
    • BaseWallet.authorised( 0x103675510a219bd84CE91d1bcb82Ca194D665a09 ) => ( True )
    • Null: 0x000...001.ddd2beb0( )
    • Proxy.STATICCALL( )
    • TransferManager.approveToken( _wallet=0x4bAaeaA617A9Ee657E7ab63DcF3c0d24d2a9F8f1, _token=0x57Ab1ec28D129707052df4dF418D58a2D46d5f51, _spender=0xE4C9194962532fEB467DCe8b3d42419641c6eD2E, _amount=5600497822028625040004 )
      • 0x44da3a8051ba88eab0440db3779cab9d679ae76f.4a4fbeec( )
      • 0x391f0e86da951c03b1183c60b195090671adea88.13f4a0ea( )
      • ProxyERC20.allowance( owner=0x4bAaeaA617A9Ee657E7ab63DcF3c0d24d2a9F8f1, spender=0xE4C9194962532fEB467DCe8b3d42419641c6eD2E ) => ( 0 )
        • Synth.allowance( owner=0x4bAaeaA617A9Ee657E7ab63DcF3c0d24d2a9F8f1, spender=0xE4C9194962532fEB467DCe8b3d42419641c6eD2E ) => ( 0 )
          • TokenState.allowance( 0x4bAaeaA617A9Ee657E7ab63DcF3c0d24d2a9F8f1, 0xE4C9194962532fEB467DCe8b3d42419641c6eD2E ) => ( 0 )
          • TokenPriceProvider.getEtherValue( _amount=5600497822028625040004, _token=0x57Ab1ec28D129707052df4dF418D58a2D46d5f51 ) => ( 17383917583425825988 )
          • Proxy.8f6f0332( )
            • BaseWallet.invoke( _target=0x57Ab1ec28D129707052df4dF418D58a2D46d5f51, _value=0, _data=0x095EA7B3000000000000000000000000E4C9194962532FEB467DCE8B3D42419641C6ED2E00000000000000000000000000000000000000000000012F9A85386D050F5284 ) => ( _result=0x0000000000000000000000000000000000000000000000000000000000000001 )
              • ProxyERC20.approve( spender=0xE4C9194962532fEB467DCe8b3d42419641c6eD2E, value=5600497822028625040004 ) => ( True )
                • Synth.setMessageSender( sender=0x4bAaeaA617A9Ee657E7ab63DcF3c0d24d2a9F8f1 )
                • Synth.approve( spender=0xE4C9194962532fEB467DCe8b3d42419641c6eD2E, value=5600497822028625040004 ) => ( True )
                  • TokenState.setAllowance( tokenOwner=0x4bAaeaA617A9Ee657E7ab63DcF3c0d24d2a9F8f1, spender=0xE4C9194962532fEB467DCe8b3d42419641c6eD2E, value=5600497822028625040004 )
                  • ProxyERC20._emit( callData=0x00000000000000000000000000000000000000000000012F9A85386D050F5284, numTopics=3, topic1=8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925, topic2=0000000000000000000000004BAAEAA617A9EE657E7AB63DCF3C0D24D2A9F8F1, topic3=000000000000000000000000E4C9194962532FEB467DCE8B3D42419641C6ED2E, topic4=0000000000000000000000000000000000000000000000000000000000000000 )
                  • Proxy.8f6f0332( )
                    • BaseWallet.invoke( _target=0x0385b3F162a0e001b60Ecb84D3CB06199d78f666, _value=11256840000000000, _data=0x ) => ( _result=0x )
                      • ETH 0.01125684 Argent: Relayer 2.CALL( )
                        File 1 of 7: TransferManager
                        pragma solidity ^0.5.4;// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        /**
                         * @title Module
                         * @dev Interface for a module.
                         * A module MUST implement the addModule() method to ensure that a wallet with at least one module
                         * can never end up in a "frozen" state.
                         * @author Julien Niset - <julien@argent.xyz>
                         */
                        interface Module {
                        
                            /**
                             * @dev Inits a module for a wallet by e.g. setting some wallet specific parameters in storage.
                             * @param _wallet The wallet.
                             */
                            function init(BaseWallet _wallet) external;
                        
                            /**
                             * @dev Adds a module to a wallet.
                             * @param _wallet The target wallet.
                             * @param _module The modules to authorise.
                             */
                            function addModule(BaseWallet _wallet, Module _module) external;
                        
                            /**
                            * @dev Utility method to recover any ERC20 token that was sent to the
                            * module by mistake.
                            * @param _token The token to recover.
                            */
                            function recoverToken(address _token) external;
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        /**
                         * @title BaseWallet
                         * @dev Simple modular wallet that authorises modules to call its invoke() method.
                         * @author Julien Niset - <julien@argent.xyz>
                         */
                        contract BaseWallet {
                        
                            // The implementation of the proxy
                            address public implementation;
                            // The owner
                            address public owner;
                            // The authorised modules
                            mapping (address => bool) public authorised;
                            // The enabled static calls
                            mapping (bytes4 => address) public enabled;
                            // The number of modules
                            uint public modules;
                        
                            event AuthorisedModule(address indexed module, bool value);
                            event EnabledStaticCall(address indexed module, bytes4 indexed method);
                            event Invoked(address indexed module, address indexed target, uint indexed value, bytes data);
                            event Received(uint indexed value, address indexed sender, bytes data);
                            event OwnerChanged(address owner);
                        
                            /**
                             * @dev Throws if the sender is not an authorised module.
                             */
                            modifier moduleOnly {
                                require(authorised[msg.sender], "BW: msg.sender not an authorized module");
                                _;
                            }
                        
                            /**
                             * @dev Inits the wallet by setting the owner and authorising a list of modules.
                             * @param _owner The owner.
                             * @param _modules The modules to authorise.
                             */
                            function init(address _owner, address[] calldata _modules) external {
                                require(owner == address(0) && modules == 0, "BW: wallet already initialised");
                                require(_modules.length > 0, "BW: construction requires at least 1 module");
                                owner = _owner;
                                modules = _modules.length;
                                for (uint256 i = 0; i < _modules.length; i++) {
                                    require(authorised[_modules[i]] == false, "BW: module is already added");
                                    authorised[_modules[i]] = true;
                                    Module(_modules[i]).init(this);
                                    emit AuthorisedModule(_modules[i], true);
                                }
                                if (address(this).balance > 0) {
                                    emit Received(address(this).balance, address(0), "");
                                }
                            }
                        
                            /**
                             * @dev Enables/Disables a module.
                             * @param _module The target module.
                             * @param _value Set to true to authorise the module.
                             */
                            function authoriseModule(address _module, bool _value) external moduleOnly {
                                if (authorised[_module] != _value) {
                                    emit AuthorisedModule(_module, _value);
                                    if (_value == true) {
                                        modules += 1;
                                        authorised[_module] = true;
                                        Module(_module).init(this);
                                    } else {
                                        modules -= 1;
                                        require(modules > 0, "BW: wallet must have at least one module");
                                        delete authorised[_module];
                                    }
                                }
                            }
                        
                            /**
                            * @dev Enables a static method by specifying the target module to which the call
                            * must be delegated.
                            * @param _module The target module.
                            * @param _method The static method signature.
                            */
                            function enableStaticCall(address _module, bytes4 _method) external moduleOnly {
                                require(authorised[_module], "BW: must be an authorised module for static call");
                                enabled[_method] = _module;
                                emit EnabledStaticCall(_module, _method);
                            }
                        
                            /**
                             * @dev Sets a new owner for the wallet.
                             * @param _newOwner The new owner.
                             */
                            function setOwner(address _newOwner) external moduleOnly {
                                require(_newOwner != address(0), "BW: address cannot be null");
                                owner = _newOwner;
                                emit OwnerChanged(_newOwner);
                            }
                        
                            /**
                             * @dev Performs a generic transaction.
                             * @param _target The address for the transaction.
                             * @param _value The value of the transaction.
                             * @param _data The data of the transaction.
                             */
                            function invoke(address _target, uint _value, bytes calldata _data) external moduleOnly returns (bytes memory _result) {
                                bool success;
                                // solium-disable-next-line security/no-call-value
                                (success, _result) = _target.call.value(_value)(_data);
                                if (!success) {
                                    // solium-disable-next-line security/no-inline-assembly
                                    assembly {
                                        returndatacopy(0, 0, returndatasize)
                                        revert(0, returndatasize)
                                    }
                                }
                                emit Invoked(msg.sender, _target, _value, _data);
                            }
                        
                            /**
                             * @dev This method makes it possible for the wallet to comply to interfaces expecting the wallet to
                             * implement specific static methods. It delegates the static call to a target contract if the data corresponds
                             * to an enabled method, or logs the call otherwise.
                             */
                            function() external payable {
                                if (msg.data.length > 0) {
                                    address module = enabled[msg.sig];
                                    if (module == address(0)) {
                                        emit Received(msg.value, msg.sender, msg.data);
                                    } else {
                                        require(authorised[module], "BW: must be an authorised module for static call");
                                        // solium-disable-next-line security/no-inline-assembly
                                        assembly {
                                            calldatacopy(0, 0, calldatasize())
                                            let result := staticcall(gas, module, 0, calldatasize(), 0, 0)
                                            returndatacopy(0, 0, returndatasize())
                                            switch result
                                            case 0 {revert(0, returndatasize())}
                                            default {return (0, returndatasize())}
                                        }
                                    }
                                }
                            }
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        
                        /**
                         * @title Owned
                         * @dev Basic contract to define an owner.
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract Owned {
                        
                            // The owner
                            address public owner;
                        
                            event OwnerChanged(address indexed _newOwner);
                        
                            /**
                             * @dev Throws if the sender is not the owner.
                             */
                            modifier onlyOwner {
                                require(msg.sender == owner, "Must be owner");
                                _;
                            }
                        
                            constructor() public {
                                owner = msg.sender;
                            }
                        
                            /**
                             * @dev Lets the owner transfer ownership of the contract to a new owner.
                             * @param _newOwner The new owner.
                             */
                            function changeOwner(address _newOwner) external onlyOwner {
                                require(_newOwner != address(0), "Address must not be null");
                                owner = _newOwner;
                                emit OwnerChanged(_newOwner);
                            }
                        }
                        
                        /**
                         * ERC20 contract interface.
                         */
                        contract ERC20 {
                            function totalSupply() public view returns (uint);
                            function decimals() public view returns (uint);
                            function balanceOf(address tokenOwner) public view returns (uint balance);
                            function allowance(address tokenOwner, address spender) public view returns (uint remaining);
                            function transfer(address to, uint tokens) public returns (bool success);
                            function approve(address spender, uint tokens) public returns (bool success);
                            function transferFrom(address from, address to, uint tokens) public returns (bool success);
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        
                        /**
                         * @title ModuleRegistry
                         * @dev Registry of authorised modules.
                         * Modules must be registered before they can be authorised on a wallet.
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract ModuleRegistry is Owned {
                        
                            mapping (address => Info) internal modules;
                            mapping (address => Info) internal upgraders;
                        
                            event ModuleRegistered(address indexed module, bytes32 name);
                            event ModuleDeRegistered(address module);
                            event UpgraderRegistered(address indexed upgrader, bytes32 name);
                            event UpgraderDeRegistered(address upgrader);
                        
                            struct Info {
                                bool exists;
                                bytes32 name;
                            }
                        
                            /**
                             * @dev Registers a module.
                             * @param _module The module.
                             * @param _name The unique name of the module.
                             */
                            function registerModule(address _module, bytes32 _name) external onlyOwner {
                                require(!modules[_module].exists, "MR: module already exists");
                                modules[_module] = Info({exists: true, name: _name});
                                emit ModuleRegistered(_module, _name);
                            }
                        
                            /**
                             * @dev Deregisters a module.
                             * @param _module The module.
                             */
                            function deregisterModule(address _module) external onlyOwner {
                                require(modules[_module].exists, "MR: module does not exist");
                                delete modules[_module];
                                emit ModuleDeRegistered(_module);
                            }
                        
                                /**
                             * @dev Registers an upgrader.
                             * @param _upgrader The upgrader.
                             * @param _name The unique name of the upgrader.
                             */
                            function registerUpgrader(address _upgrader, bytes32 _name) external onlyOwner {
                                require(!upgraders[_upgrader].exists, "MR: upgrader already exists");
                                upgraders[_upgrader] = Info({exists: true, name: _name});
                                emit UpgraderRegistered(_upgrader, _name);
                            }
                        
                            /**
                             * @dev Deregisters an upgrader.
                             * @param _upgrader The _upgrader.
                             */
                            function deregisterUpgrader(address _upgrader) external onlyOwner {
                                require(upgraders[_upgrader].exists, "MR: upgrader does not exist");
                                delete upgraders[_upgrader];
                                emit UpgraderDeRegistered(_upgrader);
                            }
                        
                            /**
                            * @dev Utility method enbaling the owner of the registry to claim any ERC20 token that was sent to the
                            * registry.
                            * @param _token The token to recover.
                            */
                            function recoverToken(address _token) external onlyOwner {
                                uint total = ERC20(_token).balanceOf(address(this));
                                ERC20(_token).transfer(msg.sender, total);
                            }
                        
                            /**
                             * @dev Gets the name of a module from its address.
                             * @param _module The module address.
                             * @return the name.
                             */
                            function moduleInfo(address _module) external view returns (bytes32) {
                                return modules[_module].name;
                            }
                        
                            /**
                             * @dev Gets the name of an upgrader from its address.
                             * @param _upgrader The upgrader address.
                             * @return the name.
                             */
                            function upgraderInfo(address _upgrader) external view returns (bytes32) {
                                return upgraders[_upgrader].name;
                            }
                        
                            /**
                             * @dev Checks if a module is registered.
                             * @param _module The module address.
                             * @return true if the module is registered.
                             */
                            function isRegisteredModule(address _module) external view returns (bool) {
                                return modules[_module].exists;
                            }
                        
                            /**
                             * @dev Checks if a list of modules are registered.
                             * @param _modules The list of modules address.
                             * @return true if all the modules are registered.
                             */
                            function isRegisteredModule(address[] calldata _modules) external view returns (bool) {
                                for (uint i = 0; i < _modules.length; i++) {
                                    if (!modules[_modules[i]].exists) {
                                        return false;
                                    }
                                }
                                return true;
                            }
                        
                            /**
                             * @dev Checks if an upgrader is registered.
                             * @param _upgrader The upgrader address.
                             * @return true if the upgrader is registered.
                             */
                            function isRegisteredUpgrader(address _upgrader) external view returns (bool) {
                                return upgraders[_upgrader].exists;
                            }
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        /**
                         * @title Storage
                         * @dev Base contract for the storage of a wallet.
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract Storage {
                        
                            /**
                             * @dev Throws if the caller is not an authorised module.
                             */
                            modifier onlyModule(BaseWallet _wallet) {
                                require(_wallet.authorised(msg.sender), "TS: must be an authorized module to call this method");
                                _;
                            }
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        interface IGuardianStorage{
                        
                            /**
                             * @dev Lets an authorised module add a guardian to a wallet.
                             * @param _wallet The target wallet.
                             * @param _guardian The guardian to add.
                             */
                            function addGuardian(BaseWallet _wallet, address _guardian) external;
                        
                            /**
                             * @dev Lets an authorised module revoke a guardian from a wallet.
                             * @param _wallet The target wallet.
                             * @param _guardian The guardian to revoke.
                             */
                            function revokeGuardian(BaseWallet _wallet, address _guardian) external;
                        
                            /**
                             * @dev Checks if an account is a guardian for a wallet.
                             * @param _wallet The target wallet.
                             * @param _guardian The account.
                             * @return true if the account is a guardian for a wallet.
                             */
                            function isGuardian(BaseWallet _wallet, address _guardian) external view returns (bool);
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        
                        
                        /**
                         * @title GuardianStorage
                         * @dev Contract storing the state of wallets related to guardians and lock.
                         * The contract only defines basic setters and getters with no logic. Only modules authorised
                         * for a wallet can modify its state.
                         * @author Julien Niset - <julien@argent.im>
                         * @author Olivier Van Den Biggelaar - <olivier@argent.im>
                         */
                        contract GuardianStorage is IGuardianStorage, Storage {
                        
                            struct GuardianStorageConfig {
                                // the list of guardians
                                address[] guardians;
                                // the info about guardians
                                mapping (address => GuardianInfo) info;
                                // the lock's release timestamp
                                uint256 lock;
                                // the module that set the last lock
                                address locker;
                            }
                        
                            struct GuardianInfo {
                                bool exists;
                                uint128 index;
                            }
                        
                            // wallet specific storage
                            mapping (address => GuardianStorageConfig) internal configs;
                        
                            // *************** External Functions ********************* //
                        
                            /**
                             * @dev Lets an authorised module add a guardian to a wallet.
                             * @param _wallet The target wallet.
                             * @param _guardian The guardian to add.
                             */
                            function addGuardian(BaseWallet _wallet, address _guardian) external onlyModule(_wallet) {
                                GuardianStorageConfig storage config = configs[address(_wallet)];
                                config.info[_guardian].exists = true;
                                config.info[_guardian].index = uint128(config.guardians.push(_guardian) - 1);
                            }
                        
                            /**
                             * @dev Lets an authorised module revoke a guardian from a wallet.
                             * @param _wallet The target wallet.
                             * @param _guardian The guardian to revoke.
                             */
                            function revokeGuardian(BaseWallet _wallet, address _guardian) external onlyModule(_wallet) {
                                GuardianStorageConfig storage config = configs[address(_wallet)];
                                address lastGuardian = config.guardians[config.guardians.length - 1];
                                if (_guardian != lastGuardian) {
                                    uint128 targetIndex = config.info[_guardian].index;
                                    config.guardians[targetIndex] = lastGuardian;
                                    config.info[lastGuardian].index = targetIndex;
                                }
                                config.guardians.length--;
                                delete config.info[_guardian];
                            }
                        
                            /**
                             * @dev Returns the number of guardians for a wallet.
                             * @param _wallet The target wallet.
                             * @return the number of guardians.
                             */
                            function guardianCount(BaseWallet _wallet) external view returns (uint256) {
                                return configs[address(_wallet)].guardians.length;
                            }
                        
                            /**
                             * @dev Gets the list of guaridans for a wallet.
                             * @param _wallet The target wallet.
                             * @return the list of guardians.
                             */
                            function getGuardians(BaseWallet _wallet) external view returns (address[] memory) {
                                GuardianStorageConfig storage config = configs[address(_wallet)];
                                address[] memory guardians = new address[](config.guardians.length);
                                for (uint256 i = 0; i < config.guardians.length; i++) {
                                    guardians[i] = config.guardians[i];
                                }
                                return guardians;
                            }
                        
                            /**
                             * @dev Checks if an account is a guardian for a wallet.
                             * @param _wallet The target wallet.
                             * @param _guardian The account.
                             * @return true if the account is a guardian for a wallet.
                             */
                            function isGuardian(BaseWallet _wallet, address _guardian) external view returns (bool) {
                                return configs[address(_wallet)].info[_guardian].exists;
                            }
                        
                            /**
                             * @dev Lets an authorised module set the lock for a wallet.
                             * @param _wallet The target wallet.
                             * @param _releaseAfter The epoch time at which the lock should automatically release.
                             */
                            function setLock(BaseWallet _wallet, uint256 _releaseAfter) external onlyModule(_wallet) {
                                configs[address(_wallet)].lock = _releaseAfter;
                                if (_releaseAfter != 0 && msg.sender != configs[address(_wallet)].locker) {
                                    configs[address(_wallet)].locker = msg.sender;
                                }
                            }
                        
                            /**
                             * @dev Checks if the lock is set for a wallet.
                             * @param _wallet The target wallet.
                             * @return true if the lock is set for the wallet.
                             */
                            function isLocked(BaseWallet _wallet) external view returns (bool) {
                                return configs[address(_wallet)].lock > now;
                            }
                        
                            /**
                             * @dev Gets the time at which the lock of a wallet will release.
                             * @param _wallet The target wallet.
                             * @return the time at which the lock of a wallet will release, or zero if there is no lock set.
                             */
                            function getLock(BaseWallet _wallet) external view returns (uint256) {
                                return configs[address(_wallet)].lock;
                            }
                        
                            /**
                             * @dev Gets the address of the last module that modified the lock for a wallet.
                             * @param _wallet The target wallet.
                             * @return the address of the last module that modified the lock for a wallet.
                             */
                            function getLocker(BaseWallet _wallet) external view returns (address) {
                                return configs[address(_wallet)].locker;
                            }
                        }/* The MIT License (MIT)
                        
                        Copyright (c) 2016 Smart Contract Solutions, Inc.
                        
                        Permission is hereby granted, free of charge, to any person obtaining
                        a copy of this software and associated documentation files (the
                        "Software"), to deal in the Software without restriction, including
                        without limitation the rights to use, copy, modify, merge, publish,
                        distribute, sublicense, and/or sell copies of the Software, and to
                        permit persons to whom the Software is furnished to do so, subject to
                        the following conditions:
                        
                        The above copyright notice and this permission notice shall be included
                        in all copies or substantial portions of the Software.
                        
                        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
                        OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
                        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
                        IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
                        CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
                        TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
                        SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
                        
                        
                        
                        /**
                         * @title SafeMath
                         * @dev Math operations with safety checks that throw on error
                         */
                        library SafeMath {
                        
                            /**
                            * @dev Multiplies two numbers, reverts on overflow.
                            */
                            function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                                // benefit is lost if 'b' is also tested.
                                // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                                if (a == 0) {
                                    return 0;
                                }
                        
                                uint256 c = a * b;
                                require(c / a == b);
                        
                                return c;
                            }
                        
                            /**
                            * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
                            */
                            function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                require(b > 0); // Solidity only automatically asserts when dividing by 0
                                uint256 c = a / b;
                                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                        
                                return c;
                            }
                        
                            /**
                            * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                            */
                            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                require(b <= a);
                                uint256 c = a - b;
                        
                                return c;
                            }
                        
                            /**
                            * @dev Adds two numbers, reverts on overflow.
                            */
                            function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                uint256 c = a + b;
                                require(c >= a);
                        
                                return c;
                            }
                        
                            /**
                            * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
                            * reverts when dividing by zero.
                            */
                            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                require(b != 0);
                                return a % b;
                            }
                        
                            /**
                            * @dev Returns ceil(a / b).
                            */
                            function ceil(uint256 a, uint256 b) internal pure returns (uint256) {
                                uint256 c = a / b;
                                if(a % b == 0) {
                                    return c;
                                }
                                else {
                                    return c + 1;
                                }
                            }
                        
                            // from DSMath - operations on fixed precision floats
                        
                            uint256 constant WAD = 10 ** 18;
                            uint256 constant RAY = 10 ** 27;
                        
                            function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                z = add(mul(x, y), WAD / 2) / WAD;
                            }
                            function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                z = add(mul(x, y), RAY / 2) / RAY;
                            }
                            function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                z = add(mul(x, WAD), y / 2) / y;
                            }
                            function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                z = add(mul(x, RAY), y / 2) / y;
                            }
                        }
                        // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        
                        
                        
                        
                        
                        /**
                         * @title BaseModule
                         * @dev Basic module that contains some methods common to all modules.
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract BaseModule is Module {
                        
                            // Empty calldata
                            bytes constant internal EMPTY_BYTES = "";
                        
                            // The adddress of the module registry.
                            ModuleRegistry internal registry;
                            // The address of the Guardian storage
                            GuardianStorage internal guardianStorage;
                        
                            /**
                             * @dev Throws if the wallet is locked.
                             */
                            modifier onlyWhenUnlocked(BaseWallet _wallet) {
                                // solium-disable-next-line security/no-block-members
                                require(!guardianStorage.isLocked(_wallet), "BM: wallet must be unlocked");
                                _;
                            }
                        
                            event ModuleCreated(bytes32 name);
                            event ModuleInitialised(address wallet);
                        
                            constructor(ModuleRegistry _registry, GuardianStorage _guardianStorage, bytes32 _name) public {
                                registry = _registry;
                                guardianStorage = _guardianStorage;
                                emit ModuleCreated(_name);
                            }
                        
                            /**
                             * @dev Throws if the sender is not the target wallet of the call.
                             */
                            modifier onlyWallet(BaseWallet _wallet) {
                                require(msg.sender == address(_wallet), "BM: caller must be wallet");
                                _;
                            }
                        
                            /**
                             * @dev Throws if the sender is not the owner of the target wallet or the module itself.
                             */
                            modifier onlyWalletOwner(BaseWallet _wallet) {
                                require(msg.sender == address(this) || isOwner(_wallet, msg.sender), "BM: must be an owner for the wallet");
                                _;
                            }
                        
                            /**
                             * @dev Throws if the sender is not the owner of the target wallet.
                             */
                            modifier strictOnlyWalletOwner(BaseWallet _wallet) {
                                require(isOwner(_wallet, msg.sender), "BM: msg.sender must be an owner for the wallet");
                                _;
                            }
                        
                            /**
                             * @dev Inits the module for a wallet by logging an event.
                             * The method can only be called by the wallet itself.
                             * @param _wallet The wallet.
                             */
                            function init(BaseWallet _wallet) public onlyWallet(_wallet) {
                                emit ModuleInitialised(address(_wallet));
                            }
                        
                            /**
                             * @dev Adds a module to a wallet. First checks that the module is registered.
                             * @param _wallet The target wallet.
                             * @param _module The modules to authorise.
                             */
                            function addModule(BaseWallet _wallet, Module _module) external strictOnlyWalletOwner(_wallet) {
                                require(registry.isRegisteredModule(address(_module)), "BM: module is not registered");
                                _wallet.authoriseModule(address(_module), true);
                            }
                        
                            /**
                            * @dev Utility method enbaling anyone to recover ERC20 token sent to the
                            * module by mistake and transfer them to the Module Registry.
                            * @param _token The token to recover.
                            */
                            function recoverToken(address _token) external {
                                uint total = ERC20(_token).balanceOf(address(this));
                                ERC20(_token).transfer(address(registry), total);
                            }
                        
                            /**
                             * @dev Helper method to check if an address is the owner of a target wallet.
                             * @param _wallet The target wallet.
                             * @param _addr The address.
                             */
                            function isOwner(BaseWallet _wallet, address _addr) internal view returns (bool) {
                                return _wallet.owner() == _addr;
                            }
                        
                            /**
                             * @dev Helper method to invoke a wallet.
                             * @param _wallet The target wallet.
                             * @param _to The target address for the transaction.
                             * @param _value The value of the transaction.
                             * @param _data The data of the transaction.
                             */
                            function invokeWallet(address _wallet, address _to, uint256 _value, bytes memory _data) internal returns (bytes memory _res) {
                                bool success;
                                // solium-disable-next-line security/no-call-value
                                (success, _res) = _wallet.call(abi.encodeWithSignature("invoke(address,uint256,bytes)", _to, _value, _data));
                                if (success && _res.length > 0) { //_res is empty if _wallet is an "old" BaseWallet that can't return output values
                                    (_res) = abi.decode(_res, (bytes));
                                } else if (_res.length > 0) {
                                    // solium-disable-next-line security/no-inline-assembly
                                    assembly {
                                        returndatacopy(0, 0, returndatasize)
                                        revert(0, returndatasize)
                                    }
                                } else if (!success) {
                                    revert("BM: wallet invoke reverted");
                                }
                            }
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        
                        /**
                         * @title RelayerModule
                         * @dev Base module containing logic to execute transactions signed by eth-less accounts and sent by a relayer.
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract RelayerModule is BaseModule {
                        
                            uint256 constant internal BLOCKBOUND = 10000;
                        
                            mapping (address => RelayerConfig) public relayer;
                        
                            struct RelayerConfig {
                                uint256 nonce;
                                mapping (bytes32 => bool) executedTx;
                            }
                        
                            event TransactionExecuted(address indexed wallet, bool indexed success, bytes32 signedHash);
                        
                            /**
                             * @dev Throws if the call did not go through the execute() method.
                             */
                            modifier onlyExecute {
                                require(msg.sender == address(this), "RM: must be called via execute()");
                                _;
                            }
                        
                            /* ***************** Abstract method ************************* */
                        
                            /**
                            * @dev Gets the number of valid signatures that must be provided to execute a
                            * specific relayed transaction.
                            * @param _wallet The target wallet.
                            * @param _data The data of the relayed transaction.
                            * @return The number of required signatures.
                            */
                            function getRequiredSignatures(BaseWallet _wallet, bytes memory _data) internal view returns (uint256);
                        
                            /**
                            * @dev Validates the signatures provided with a relayed transaction.
                            * The method MUST throw if one or more signatures are not valid.
                            * @param _wallet The target wallet.
                            * @param _data The data of the relayed transaction.
                            * @param _signHash The signed hash representing the relayed transaction.
                            * @param _signatures The signatures as a concatenated byte array.
                            */
                            function validateSignatures(
                                BaseWallet _wallet,
                                bytes memory _data,
                                bytes32 _signHash,
                                bytes memory _signatures) internal view returns (bool);
                        
                            /* ************************************************************ */
                        
                            /**
                            * @dev Executes a relayed transaction.
                            * @param _wallet The target wallet.
                            * @param _data The data for the relayed transaction
                            * @param _nonce The nonce used to prevent replay attacks.
                            * @param _signatures The signatures as a concatenated byte array.
                            * @param _gasPrice The gas price to use for the gas refund.
                            * @param _gasLimit The gas limit to use for the gas refund.
                            */
                            function execute(
                                BaseWallet _wallet,
                                bytes calldata _data,
                                uint256 _nonce,
                                bytes calldata _signatures,
                                uint256 _gasPrice,
                                uint256 _gasLimit
                            )
                                external
                                returns (bool success)
                            {
                                uint startGas = gasleft();
                                bytes32 signHash = getSignHash(address(this), address(_wallet), 0, _data, _nonce, _gasPrice, _gasLimit);
                                require(checkAndUpdateUniqueness(_wallet, _nonce, signHash), "RM: Duplicate request");
                                require(verifyData(address(_wallet), _data), "RM: the wallet authorized is different then the target of the relayed data");
                                uint256 requiredSignatures = getRequiredSignatures(_wallet, _data);
                                if ((requiredSignatures * 65) == _signatures.length) {
                                    if (verifyRefund(_wallet, _gasLimit, _gasPrice, requiredSignatures)) {
                                        if (requiredSignatures == 0 || validateSignatures(_wallet, _data, signHash, _signatures)) {
                                            // solium-disable-next-line security/no-call-value
                                            (success,) = address(this).call(_data);
                                            refund(_wallet, startGas - gasleft(), _gasPrice, _gasLimit, requiredSignatures, msg.sender);
                                        }
                                    }
                                }
                                emit TransactionExecuted(address(_wallet), success, signHash);
                            }
                        
                            /**
                            * @dev Gets the current nonce for a wallet.
                            * @param _wallet The target wallet.
                            */
                            function getNonce(BaseWallet _wallet) external view returns (uint256 nonce) {
                                return relayer[address(_wallet)].nonce;
                            }
                        
                            /**
                            * @dev Generates the signed hash of a relayed transaction according to ERC 1077.
                            * @param _from The starting address for the relayed transaction (should be the module)
                            * @param _to The destination address for the relayed transaction (should be the wallet)
                            * @param _value The value for the relayed transaction
                            * @param _data The data for the relayed transaction
                            * @param _nonce The nonce used to prevent replay attacks.
                            * @param _gasPrice The gas price to use for the gas refund.
                            * @param _gasLimit The gas limit to use for the gas refund.
                            */
                            function getSignHash(
                                address _from,
                                address _to,
                                uint256 _value,
                                bytes memory _data,
                                uint256 _nonce,
                                uint256 _gasPrice,
                                uint256 _gasLimit
                            )
                                internal
                                pure
                                returns (bytes32)
                            {
                                return keccak256(
                                    abi.encodePacked(
                                        "\x19Ethereum Signed Message:\n32",
                                        keccak256(abi.encodePacked(byte(0x19), byte(0), _from, _to, _value, _data, _nonce, _gasPrice, _gasLimit))
                                ));
                            }
                        
                            /**
                            * @dev Checks if the relayed transaction is unique.
                            * @param _wallet The target wallet.
                            * @param _signHash The signed hash of the transaction
                            */
                            function checkAndUpdateUniqueness(BaseWallet _wallet, uint256 /* _nonce */, bytes32 _signHash) internal returns (bool) {
                                if (relayer[address(_wallet)].executedTx[_signHash] == true) {
                                    return false;
                                }
                                relayer[address(_wallet)].executedTx[_signHash] = true;
                                return true;
                            }
                        
                            /**
                            * @dev Checks that a nonce has the correct format and is valid.
                            * It must be constructed as nonce = {block number}{timestamp} where each component is 16 bytes.
                            * @param _wallet The target wallet.
                            * @param _nonce The nonce
                            */
                            function checkAndUpdateNonce(BaseWallet _wallet, uint256 _nonce) internal returns (bool) {
                                if (_nonce <= relayer[address(_wallet)].nonce) {
                                    return false;
                                }
                                uint256 nonceBlock = (_nonce & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) >> 128;
                                if (nonceBlock > block.number + BLOCKBOUND) {
                                    return false;
                                }
                                relayer[address(_wallet)].nonce = _nonce;
                                return true;
                            }
                        
                            /**
                            * @dev Recovers the signer at a given position from a list of concatenated signatures.
                            * @param _signedHash The signed hash
                            * @param _signatures The concatenated signatures.
                            * @param _index The index of the signature to recover.
                            */
                            function recoverSigner(bytes32 _signedHash, bytes memory _signatures, uint _index) internal pure returns (address) {
                                uint8 v;
                                bytes32 r;
                                bytes32 s;
                                // we jump 32 (0x20) as the first slot of bytes contains the length
                                // we jump 65 (0x41) per signature
                                // for v we load 32 bytes ending with v (the first 31 come from s) then apply a mask
                                // solium-disable-next-line security/no-inline-assembly
                                assembly {
                                    r := mload(add(_signatures, add(0x20,mul(0x41,_index))))
                                    s := mload(add(_signatures, add(0x40,mul(0x41,_index))))
                                    v := and(mload(add(_signatures, add(0x41,mul(0x41,_index)))), 0xff)
                                }
                                require(v == 27 || v == 28); // solium-disable-line error-reason
                                return ecrecover(_signedHash, v, r, s);
                            }
                        
                            /**
                            * @dev Refunds the gas used to the Relayer.
                            * For security reasons the default behavior is to not refund calls with 0 or 1 signatures.
                            * @param _wallet The target wallet.
                            * @param _gasUsed The gas used.
                            * @param _gasPrice The gas price for the refund.
                            * @param _gasLimit The gas limit for the refund.
                            * @param _signatures The number of signatures used in the call.
                            * @param _relayer The address of the Relayer.
                            */
                            function refund(BaseWallet _wallet, uint _gasUsed, uint _gasPrice, uint _gasLimit, uint _signatures, address _relayer) internal {
                                uint256 amount = 29292 + _gasUsed; // 21000 (transaction) + 7620 (execution of refund) + 672 to log the event + _gasUsed
                                // only refund if gas price not null, more than 1 signatures, gas less than gasLimit
                                if (_gasPrice > 0 && _signatures > 1 && amount <= _gasLimit) {
                                    if (_gasPrice > tx.gasprice) {
                                        amount = amount * tx.gasprice;
                                    } else {
                                        amount = amount * _gasPrice;
                                    }
                                    invokeWallet(address(_wallet), _relayer, amount, EMPTY_BYTES);
                                }
                            }
                        
                            /**
                            * @dev Returns false if the refund is expected to fail.
                            * @param _wallet The target wallet.
                            * @param _gasUsed The expected gas used.
                            * @param _gasPrice The expected gas price for the refund.
                            */
                            function verifyRefund(BaseWallet _wallet, uint _gasUsed, uint _gasPrice, uint _signatures) internal view returns (bool) {
                                if (_gasPrice > 0 &&
                                    _signatures > 1 &&
                                    (address(_wallet).balance < _gasUsed * _gasPrice || _wallet.authorised(address(this)) == false)) {
                                    return false;
                                }
                                return true;
                            }
                        
                            /**
                            * @dev Checks that the wallet address provided as the first parameter of the relayed data is the same
                            * as the wallet passed as the input of the execute() method.
                            @return false if the addresses are different.
                            */
                            function verifyData(address _wallet, bytes memory _data) private pure returns (bool) {
                                require(_data.length >= 36, "RM: Invalid dataWallet");
                                address dataWallet;
                                // solium-disable-next-line security/no-inline-assembly
                                assembly {
                                    //_data = {length:32}{sig:4}{_wallet:32}{...}
                                    dataWallet := mload(add(_data, 0x24))
                                }
                                return dataWallet == _wallet;
                            }
                        
                            /**
                            * @dev Parses the data to extract the method signature.
                            */
                            function functionPrefix(bytes memory _data) internal pure returns (bytes4 prefix) {
                                require(_data.length >= 4, "RM: Invalid functionPrefix");
                                // solium-disable-next-line security/no-inline-assembly
                                assembly {
                                    prefix := mload(add(_data, 0x20))
                                }
                            }
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        
                        
                        /**
                         * @title OnlyOwnerModule
                         * @dev Module that extends BaseModule and RelayerModule for modules where the execute() method
                         * must be called with one signature frm the owner.
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract OnlyOwnerModule is BaseModule, RelayerModule {
                        
                            // bytes4 private constant IS_ONLY_OWNER_MODULE = bytes4(keccak256("isOnlyOwnerModule()"));
                        
                           /**
                            * @dev Returns a constant that indicates that the module is an OnlyOwnerModule.
                            * @return The constant bytes4(keccak256("isOnlyOwnerModule()"))
                            */
                            function isOnlyOwnerModule() external pure returns (bytes4) {
                                // return IS_ONLY_OWNER_MODULE;
                                return this.isOnlyOwnerModule.selector;
                            }
                        
                            /**
                             * @dev Adds a module to a wallet. First checks that the module is registered.
                             * Unlike its overrided parent, this method can be called via the RelayerModule's execute()
                             * @param _wallet The target wallet.
                             * @param _module The modules to authorise.
                             */
                            function addModule(BaseWallet _wallet, Module _module) external onlyWalletOwner(_wallet) {
                                require(registry.isRegisteredModule(address(_module)), "BM: module is not registered");
                                _wallet.authoriseModule(address(_module), true);
                            }
                        
                            // *************** Implementation of RelayerModule methods ********************* //
                        
                            // Overrides to use the incremental nonce and save some gas
                            function checkAndUpdateUniqueness(BaseWallet _wallet, uint256 _nonce, bytes32 /* _signHash */) internal returns (bool) {
                                return checkAndUpdateNonce(_wallet, _nonce);
                            }
                        
                            function validateSignatures(
                                BaseWallet _wallet,
                                bytes memory /* _data */,
                                bytes32 _signHash,
                                bytes memory _signatures
                            )
                                internal
                                view
                                returns (bool)
                            {
                                address signer = recoverSigner(_signHash, _signatures, 0);
                                return isOwner(_wallet, signer); // "OOM: signer must be owner"
                            }
                        
                            function getRequiredSignatures(BaseWallet /* _wallet */, bytes memory /* _data */) internal view returns (uint256) {
                                return 1;
                            }
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        /**
                         * @title BaseTransfer
                         * @dev Module containing internal methods to execute or approve transfers
                         * @author Olivier VDB - <olivier@argent.xyz>
                         */
                        contract BaseTransfer is BaseModule {
                        
                            // Mock token address for ETH
                            address constant internal ETH_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                        
                            // *************** Events *************************** //
                        
                            event Transfer(address indexed wallet, address indexed token, uint256 indexed amount, address to, bytes data);
                            event Approved(address indexed wallet, address indexed token, uint256 amount, address spender);
                            event CalledContract(address indexed wallet, address indexed to, uint256 amount, bytes data);
                            event ApprovedAndCalledContract(
                                address indexed wallet,
                                address indexed to,
                                address spender,
                                address indexed token,
                                uint256 amountApproved,
                                uint256 amountSpent,
                                bytes data
                            );
                            // *************** Internal Functions ********************* //
                        
                            /**
                            * @dev Helper method to transfer ETH or ERC20 for a wallet.
                            * @param _wallet The target wallet.
                            * @param _token The ERC20 address.
                            * @param _to The recipient.
                            * @param _value The amount of ETH to transfer
                            * @param _data The data to *log* with the transfer.
                            */
                            function doTransfer(BaseWallet _wallet, address _token, address _to, uint256 _value, bytes memory _data) internal {
                                if (_token == ETH_TOKEN) {
                                    invokeWallet(address(_wallet), _to, _value, EMPTY_BYTES);
                                } else {
                                    bytes memory methodData = abi.encodeWithSignature("transfer(address,uint256)", _to, _value);
                                    invokeWallet(address(_wallet), _token, 0, methodData);
                                }
                                emit Transfer(address(_wallet), _token, _value, _to, _data);
                            }
                        
                            /**
                            * @dev Helper method to approve spending the ERC20 of a wallet.
                            * @param _wallet The target wallet.
                            * @param _token The ERC20 address.
                            * @param _spender The spender address.
                            * @param _value The amount of token to transfer.
                            */
                            function doApproveToken(BaseWallet _wallet, address _token, address _spender, uint256 _value) internal {
                                bytes memory methodData = abi.encodeWithSignature("approve(address,uint256)", _spender, _value);
                                invokeWallet(address(_wallet), _token, 0, methodData);
                                emit Approved(address(_wallet), _token, _value, _spender);
                            }
                        
                            /**
                            * @dev Helper method to call an external contract.
                            * @param _wallet The target wallet.
                            * @param _contract The contract address.
                            * @param _value The ETH value to transfer.
                            * @param _data The method data.
                            */
                            function doCallContract(BaseWallet _wallet, address _contract, uint256 _value, bytes memory _data) internal {
                                invokeWallet(address(_wallet), _contract, _value, _data);
                                emit CalledContract(address(_wallet), _contract, _value, _data);
                            }
                        
                            /**
                            * @dev Helper method to approve a certain amount of token and call an external contract.
                            * The address that spends the _token and the address that is called with _data can be different.
                            * @param _wallet The target wallet.
                            * @param _token The ERC20 address.
                            * @param _spender The spender address.
                            * @param _amount The amount of tokens to transfer.
                            * @param _contract The contract address.
                            * @param _data The method data.
                            */
                            function doApproveTokenAndCallContract(
                                BaseWallet _wallet,
                                address _token,
                                address _spender,
                                uint256 _amount,
                                address _contract,
                                bytes memory _data
                            )
                                internal
                            {
                                uint256 existingAllowance = ERC20(_token).allowance(address(_wallet), _spender);
                                uint256 totalAllowance = SafeMath.add(existingAllowance, _amount);
                                // Approve the desired amount plus existing amount. This logic allows for potential gas saving later
                                // when restoring the original approved amount, in cases where the _spender uses the exact approved _amount.
                                bytes memory methodData = abi.encodeWithSignature("approve(address,uint256)", _spender, totalAllowance);
                        
                                invokeWallet(address(_wallet), _token, 0, methodData);
                                invokeWallet(address(_wallet), _contract, 0, _data);
                        
                                // Calculate the approved amount that was spent after the call
                                uint256 unusedAllowance = ERC20(_token).allowance(address(_wallet), _spender);
                                uint256 usedAllowance = SafeMath.sub(totalAllowance, unusedAllowance);
                                // Ensure the amount spent does not exceed the amount approved for this call
                                require(usedAllowance <= _amount, "BT: insufficient amount for call");
                        
                                if (unusedAllowance != existingAllowance) {
                                    // Restore the original allowance amount if the amount spent was different (can be lower).
                                    methodData = abi.encodeWithSignature("approve(address,uint256)", _spender, existingAllowance);
                                    invokeWallet(address(_wallet), _token, 0, methodData);
                                }
                        
                                emit ApprovedAndCalledContract(
                                    address(_wallet),
                                    _contract,
                                    _spender,
                                    _token,
                                    _amount,
                                    usedAllowance,
                                    _data);
                            }
                        }
                        // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        
                        /**
                         * @title LimitManager
                         * @dev Module to manage a daily spending limit
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract LimitManager is BaseModule {
                        
                            // large limit when the limit can be considered disabled
                            uint128 constant private LIMIT_DISABLED = uint128(-1); // 3.40282366920938463463374607431768211455e+38
                        
                            using SafeMath for uint256;
                        
                            struct LimitManagerConfig {
                                // The daily limit
                                Limit limit;
                                // The current usage
                                DailySpent dailySpent;
                            }
                        
                            struct Limit {
                                // the current limit
                                uint128 current;
                                // the pending limit if any
                                uint128 pending;
                                // when the pending limit becomes the current limit
                                uint64 changeAfter;
                            }
                        
                            struct DailySpent {
                                // The amount already spent during the current period
                                uint128 alreadySpent;
                                // The end of the current period
                                uint64 periodEnd;
                            }
                        
                            // wallet specific storage
                            mapping (address => LimitManagerConfig) internal limits;
                            // The default limit
                            uint256 public defaultLimit;
                        
                            // *************** Events *************************** //
                        
                            event LimitChanged(address indexed wallet, uint indexed newLimit, uint64 indexed startAfter);
                        
                            // *************** Constructor ********************** //
                        
                            constructor(uint256 _defaultLimit) public {
                                defaultLimit = _defaultLimit;
                            }
                        
                            // *************** External/Public Functions ********************* //
                        
                            /**
                             * @dev Inits the module for a wallet by setting the limit to the default value.
                             * @param _wallet The target wallet.
                             */
                            function init(BaseWallet _wallet) public onlyWallet(_wallet) {
                                Limit storage limit = limits[address(_wallet)].limit;
                                if (limit.current == 0 && limit.changeAfter == 0) {
                                    limit.current = uint128(defaultLimit);
                                }
                            }
                        
                            // *************** Internal Functions ********************* //
                        
                            /**
                             * @dev Changes the daily limit.
                             * The limit is expressed in ETH and the change is pending for the security period.
                             * @param _wallet The target wallet.
                             * @param _newLimit The new limit.
                             * @param _securityPeriod The security period.
                             */
                            function changeLimit(BaseWallet _wallet, uint256 _newLimit, uint256 _securityPeriod) internal {
                                Limit storage limit = limits[address(_wallet)].limit;
                                // solium-disable-next-line security/no-block-members
                                uint128 current = (limit.changeAfter > 0 && limit.changeAfter < now) ? limit.pending : limit.current;
                                limit.current = current;
                                limit.pending = uint128(_newLimit);
                                // solium-disable-next-line security/no-block-members
                                limit.changeAfter = uint64(now.add(_securityPeriod));
                                // solium-disable-next-line security/no-block-members
                                emit LimitChanged(address(_wallet), _newLimit, uint64(now.add(_securityPeriod)));
                            }
                        
                             /**
                             * @dev Disable the daily limit.
                             * The change is pending for the security period.
                             * @param _wallet The target wallet.
                             * @param _securityPeriod The security period.
                             */
                            function disableLimit(BaseWallet _wallet, uint256 _securityPeriod) internal {
                                changeLimit(_wallet, LIMIT_DISABLED, _securityPeriod);
                            }
                        
                            /**
                            * @dev Gets the current daily limit for a wallet.
                            * @param _wallet The target wallet.
                            * @return the current limit expressed in ETH.
                            */
                            function getCurrentLimit(BaseWallet _wallet) public view returns (uint256 _currentLimit) {
                                Limit storage limit = limits[address(_wallet)].limit;
                                _currentLimit = uint256(currentLimit(limit.current, limit.pending, limit.changeAfter));
                            }
                        
                            /**
                            * @dev Returns whether the daily limit is disabled for a wallet.
                            * @param _wallet The target wallet.
                            * @return true if the daily limit is disabled, false otherwise.
                            */
                            function isLimitDisabled(BaseWallet _wallet) public view returns (bool _limitDisabled) {
                                uint256 currentLimit = getCurrentLimit(_wallet);
                                _limitDisabled = currentLimit == LIMIT_DISABLED;
                            }
                        
                            /**
                            * @dev Gets a pending limit for a wallet if any.
                            * @param _wallet The target wallet.
                            * @return the pending limit (in ETH) and the time at chich it will become effective.
                            */
                            function getPendingLimit(BaseWallet _wallet) external view returns (uint256 _pendingLimit, uint64 _changeAfter) {
                                Limit storage limit = limits[address(_wallet)].limit;
                                // solium-disable-next-line security/no-block-members
                                return ((now < limit.changeAfter)? (uint256(limit.pending), limit.changeAfter) : (0,0));
                            }
                        
                            /**
                            * @dev Gets the amount of tokens that has not yet been spent during the current period.
                            * @param _wallet The target wallet.
                            * @return the amount of tokens (in ETH) that has not been spent yet and the end of the period.
                            */
                            function getDailyUnspent(BaseWallet _wallet) external view returns (uint256 _unspent, uint64 _periodEnd) {
                                uint256 limit = getCurrentLimit(_wallet);
                                DailySpent storage expense = limits[address(_wallet)].dailySpent;
                                // solium-disable-next-line security/no-block-members
                                if (now > expense.periodEnd) {
                                    _unspent = limit;
                                    // solium-disable-next-line security/no-block-members
                                    _periodEnd = uint64(now + 24 hours);
                                } else {
                                    _periodEnd = expense.periodEnd;
                                    if (expense.alreadySpent < limit) {
                                        _unspent = limit - expense.alreadySpent;
                                    }
                                }
                            }
                        
                            /**
                            * @dev Helper method to check if a transfer is within the limit.
                            * If yes the daily unspent for the current period is updated.
                            * @param _wallet The target wallet.
                            * @param _amount The amount for the transfer
                            */
                            function checkAndUpdateDailySpent(BaseWallet _wallet, uint _amount) internal returns (bool) {
                                if (_amount == 0)
                                    return true;
                                Limit storage limit = limits[address(_wallet)].limit;
                                uint128 current = currentLimit(limit.current, limit.pending, limit.changeAfter);
                                if (isWithinDailyLimit(_wallet, current, _amount)) {
                                    updateDailySpent(_wallet, current, _amount);
                                    return true;
                                }
                                return false;
                            }
                        
                            /**
                            * @dev Helper method to update the daily spent for the current period.
                            * @param _wallet The target wallet.
                            * @param _limit The current limit for the wallet.
                            * @param _amount The amount to add to the daily spent.
                            */
                            function updateDailySpent(BaseWallet _wallet, uint128 _limit, uint _amount) internal {
                                if (_limit != LIMIT_DISABLED) {
                                    DailySpent storage expense = limits[address(_wallet)].dailySpent;
                                    // solium-disable-next-line security/no-block-members
                                    if (expense.periodEnd < now) {
                                        // solium-disable-next-line security/no-block-members
                                        expense.periodEnd = uint64(now + 24 hours);
                                        expense.alreadySpent = uint128(_amount);
                                    } else {
                                        expense.alreadySpent += uint128(_amount);
                                    }
                                }
                            }
                        
                            /**
                            * @dev Checks if a transfer amount is withing the daily limit for a wallet.
                            * @param _wallet The target wallet.
                            * @param _limit The current limit for the wallet.
                            * @param _amount The transfer amount.
                            * @return true if the transfer amount is withing the daily limit.
                            */
                            function isWithinDailyLimit(BaseWallet _wallet, uint _limit, uint _amount) internal view returns (bool) {
                                if (_limit == LIMIT_DISABLED) {
                                    return true;
                                }
                                DailySpent storage expense = limits[address(_wallet)].dailySpent;
                                // solium-disable-next-line security/no-block-members
                                if (expense.periodEnd < now) {
                                    return (_amount <= _limit);
                                } else {
                                    return (expense.alreadySpent + _amount <= _limit && expense.alreadySpent + _amount >= expense.alreadySpent);
                                }
                            }
                        
                            /**
                            * @dev Helper method to get the current limit from a Limit struct.
                            * @param _current The value of the current parameter
                            * @param _pending The value of the pending parameter
                            * @param _changeAfter The value of the changeAfter parameter
                            */
                            function currentLimit(uint128 _current, uint128 _pending, uint64 _changeAfter) internal view returns (uint128) {
                                // solium-disable-next-line security/no-block-members
                                if (_changeAfter > 0 && _changeAfter < now) {
                                    return _pending;
                                }
                                return _current;
                            }
                        
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        
                        /**
                         * @title TransferStorage
                         * @dev Contract storing the state of wallets related to transfers (limit and whitelist).
                         * The contract only defines basic setters and getters with no logic. Only modules authorised
                         * for a wallet can modify its state.
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract TransferStorage is Storage {
                        
                            // wallet specific storage
                            mapping (address => mapping (address => uint256)) internal whitelist;
                        
                            // *************** External Functions ********************* //
                        
                            /**
                             * @dev Lets an authorised module add or remove an account from the whitelist of a wallet.
                             * @param _wallet The target wallet.
                             * @param _target The account to add/remove.
                             * @param _value True for addition, false for revokation.
                             */
                            function setWhitelist(BaseWallet _wallet, address _target, uint256 _value) external onlyModule(_wallet) {
                                whitelist[address(_wallet)][_target] = _value;
                            }
                        
                            /**
                             * @dev Gets the whitelist state of an account for a wallet.
                             * @param _wallet The target wallet.
                             * @param _target The account.
                             * @return the epoch time at which an account strats to be whitelisted, or zero if the account is not whitelisted.
                             */
                            function getWhitelist(BaseWallet _wallet, address _target) external view returns (uint256) {
                                return whitelist[address(_wallet)][_target];
                            }
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        /**
                         * @title Managed
                         * @dev Basic contract that defines a set of managers. Only the owner can add/remove managers.
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract Managed is Owned {
                        
                            // The managers
                            mapping (address => bool) public managers;
                        
                            /**
                             * @dev Throws if the sender is not a manager.
                             */
                            modifier onlyManager {
                                require(managers[msg.sender] == true, "M: Must be manager");
                                _;
                            }
                        
                            event ManagerAdded(address indexed _manager);
                            event ManagerRevoked(address indexed _manager);
                        
                            /**
                            * @dev Adds a manager.
                            * @param _manager The address of the manager.
                            */
                            function addManager(address _manager) external onlyOwner {
                                require(_manager != address(0), "M: Address must not be null");
                                if (managers[_manager] == false) {
                                    managers[_manager] = true;
                                    emit ManagerAdded(_manager);
                                }
                            }
                        
                            /**
                            * @dev Revokes a manager.
                            * @param _manager The address of the manager.
                            */
                            function revokeManager(address _manager) external onlyOwner {
                                require(managers[_manager] == true, "M: Target must be an existing manager");
                                delete managers[_manager];
                                emit ManagerRevoked(_manager);
                            }
                        }
                        
                        contract KyberNetwork {
                        
                            function getExpectedRate(
                                ERC20 src,
                                ERC20 dest,
                                uint srcQty
                            )
                                public
                                view
                                returns (uint expectedRate, uint slippageRate);
                        
                            function trade(
                                ERC20 src,
                                uint srcAmount,
                                ERC20 dest,
                                address payable destAddress,
                                uint maxDestAmount,
                                uint minConversionRate,
                                address walletId
                            )
                                public
                                payable
                                returns(uint);
                        }
                        // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        
                        
                        
                        contract TokenPriceProvider is Managed {
                        
                            // Mock token address for ETH
                            address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                        
                            using SafeMath for uint256;
                        
                            mapping(address => uint256) public cachedPrices;
                        
                            // Address of the KyberNetwork contract
                            KyberNetwork public kyberNetwork;
                        
                            constructor(KyberNetwork _kyberNetwork) public {
                                kyberNetwork = _kyberNetwork;
                            }
                        
                            function setPrice(ERC20 _token, uint256 _price) public onlyManager {
                                cachedPrices[address(_token)] = _price;
                            }
                        
                            function setPriceForTokenList(ERC20[] calldata _tokens, uint256[] calldata _prices) external onlyManager {
                                for (uint16 i = 0; i < _tokens.length; i++) {
                                    setPrice(_tokens[i], _prices[i]);
                                }
                            }
                        
                            /**
                             * @dev Converts the value of _amount tokens in ether.
                             * @param _amount the amount of tokens to convert (in 'token wei' twei)
                             * @param _token the ERC20 token contract
                             * @return the ether value (in wei) of _amount tokens with contract _token
                             */
                            function getEtherValue(uint256 _amount, address _token) external view returns (uint256) {
                                uint256 decimals = ERC20(_token).decimals();
                                uint256 price = cachedPrices[_token];
                                return price.mul(_amount).div(10**decimals);
                            }
                        
                            //
                            // The following is added to be backward-compatible with Argent's old backend
                            //
                        
                            function setKyberNetwork(KyberNetwork _kyberNetwork) external onlyManager {
                                kyberNetwork = _kyberNetwork;
                            }
                        
                            function syncPrice(ERC20 _token) external {
                                require(address(kyberNetwork) != address(0), "Kyber sync is disabled");
                                (uint256 expectedRate,) = kyberNetwork.getExpectedRate(_token, ERC20(ETH_TOKEN_ADDRESS), 10000);
                                cachedPrices[address(_token)] = expectedRate;
                            }
                        
                            function syncPriceForTokenList(ERC20[] calldata _tokens) external {
                                require(address(kyberNetwork) != address(0), "Kyber sync is disabled");
                                for (uint16 i = 0; i < _tokens.length; i++) {
                                    (uint256 expectedRate,) = kyberNetwork.getExpectedRate(_tokens[i], ERC20(ETH_TOKEN_ADDRESS), 10000);
                                    cachedPrices[address(_tokens[i])] = expectedRate;
                                }
                            }
                        }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        
                        /**
                         * @title TransferManager
                         * @dev Module to transfer and approve tokens (ETH or ERC20) or data (contract call) based on a security context (daily limit, whitelist, etc).
                         * This module is the V2 of TokenTransfer.
                         * @author Julien Niset - <julien@argent.xyz>
                         */
                        contract TransferManager is BaseModule, RelayerModule, OnlyOwnerModule, BaseTransfer, LimitManager {
                        
                            bytes32 constant NAME = "TransferManager";
                        
                            bytes4 private constant ERC1271_ISVALIDSIGNATURE_BYTES = bytes4(keccak256("isValidSignature(bytes,bytes)"));
                            bytes4 private constant ERC1271_ISVALIDSIGNATURE_BYTES32 = bytes4(keccak256("isValidSignature(bytes32,bytes)"));
                        
                            enum ActionType { Transfer }
                        
                            using SafeMath for uint256;
                        
                            struct TokenManagerConfig {
                                // Mapping between pending action hash and their timestamp
                                mapping (bytes32 => uint256) pendingActions;
                            }
                        
                            // wallet specific storage
                            mapping (address => TokenManagerConfig) internal configs;
                        
                            // The security period
                            uint256 public securityPeriod;
                            // The execution window
                            uint256 public securityWindow;
                            // The Token storage
                            TransferStorage public transferStorage;
                            // The Token price provider
                            TokenPriceProvider public priceProvider;
                            // The previous limit manager needed to migrate the limits
                            LimitManager public oldLimitManager;
                        
                            // *************** Events *************************** //
                        
                            event AddedToWhitelist(address indexed wallet, address indexed target, uint64 whitelistAfter);
                            event RemovedFromWhitelist(address indexed wallet, address indexed target);
                            event PendingTransferCreated(address indexed wallet, bytes32 indexed id, uint256 indexed executeAfter,
                            address token, address to, uint256 amount, bytes data);
                            event PendingTransferExecuted(address indexed wallet, bytes32 indexed id);
                            event PendingTransferCanceled(address indexed wallet, bytes32 indexed id);
                        
                            // *************** Constructor ********************** //
                        
                            constructor(
                                ModuleRegistry _registry,
                                TransferStorage _transferStorage,
                                GuardianStorage _guardianStorage,
                                address _priceProvider,
                                uint256 _securityPeriod,
                                uint256 _securityWindow,
                                uint256 _defaultLimit,
                                LimitManager _oldLimitManager
                            )
                                BaseModule(_registry, _guardianStorage, NAME)
                                LimitManager(_defaultLimit)
                                public
                            {
                                transferStorage = _transferStorage;
                                priceProvider = TokenPriceProvider(_priceProvider);
                                securityPeriod = _securityPeriod;
                                securityWindow = _securityWindow;
                                oldLimitManager = _oldLimitManager;
                            }
                        
                            /**
                             * @dev Inits the module for a wallet by setting up the isValidSignature (EIP 1271)
                             * static call redirection from the wallet to the module and copying all the parameters
                             * of the daily limit from the previous implementation of the LimitManager module.
                             * @param _wallet The target wallet.
                             */
                            function init(BaseWallet _wallet) public onlyWallet(_wallet) {
                        
                                // setup static calls
                                _wallet.enableStaticCall(address(this), ERC1271_ISVALIDSIGNATURE_BYTES);
                                _wallet.enableStaticCall(address(this), ERC1271_ISVALIDSIGNATURE_BYTES32);
                        
                                // setup default limit for new deployment
                                if (address(oldLimitManager) == address(0)) {
                                    super.init(_wallet);
                                    return;
                                }
                                // get limit from previous LimitManager
                                uint256 current = oldLimitManager.getCurrentLimit(_wallet);
                                (uint256 pending, uint64 changeAfter) = oldLimitManager.getPendingLimit(_wallet);
                                // setup default limit for new wallets
                                if (current == 0 && changeAfter == 0) {
                                    super.init(_wallet);
                                    return;
                                }
                                // migrate existing limit for existing wallets
                                if (current == pending) {
                                    limits[address(_wallet)].limit.current = uint128(current);
                                } else {
                                    limits[address(_wallet)].limit = Limit(uint128(current), uint128(pending), changeAfter);
                                }
                                // migrate daily pending if we are within a rolling period
                                (uint256 unspent, uint64 periodEnd) = oldLimitManager.getDailyUnspent(_wallet);
                                // solium-disable-next-line security/no-block-members
                                if (periodEnd > now) {
                                    limits[address(_wallet)].dailySpent = DailySpent(uint128(current.sub(unspent)), periodEnd);
                                }
                            }
                        
                            // *************** External/Public Functions ********************* //
                        
                            /**
                            * @dev lets the owner transfer tokens (ETH or ERC20) from a wallet.
                            * @param _wallet The target wallet.
                            * @param _token The address of the token to transfer.
                            * @param _to The destination address
                            * @param _amount The amoutn of token to transfer
                            * @param _data The data for the transaction
                            */
                            function transferToken(
                                BaseWallet _wallet,
                                address _token,
                                address _to,
                                uint256 _amount,
                                bytes calldata _data
                            )
                                external
                                onlyWalletOwner(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                if (isWhitelisted(_wallet, _to)) {
                                    // transfer to whitelist
                                    doTransfer(_wallet, _token, _to, _amount, _data);
                                } else {
                                    uint256 etherAmount = (_token == ETH_TOKEN) ? _amount : priceProvider.getEtherValue(_amount, _token);
                                    if (checkAndUpdateDailySpent(_wallet, etherAmount)) {
                                        // transfer under the limit
                                        doTransfer(_wallet, _token, _to, _amount, _data);
                                    } else {
                                        // transfer above the limit
                                        (bytes32 id, uint256 executeAfter) = addPendingAction(ActionType.Transfer, _wallet, _token, _to, _amount, _data);
                                        emit PendingTransferCreated(address(_wallet), id, executeAfter, _token, _to, _amount, _data);
                                    }
                                }
                            }
                        
                            /**
                            * @dev lets the owner approve an allowance of ERC20 tokens for a spender (dApp).
                            * @param _wallet The target wallet.
                            * @param _token The address of the token to transfer.
                            * @param _spender The address of the spender
                            * @param _amount The amount of tokens to approve
                            */
                            function approveToken(
                                BaseWallet _wallet,
                                address _token,
                                address _spender,
                                uint256 _amount
                            )
                                external
                                onlyWalletOwner(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                if (isWhitelisted(_wallet, _spender)) {
                                    // approve to whitelist
                                    doApproveToken(_wallet, _token, _spender, _amount);
                                } else {
                                    // get current alowance
                                    uint256 currentAllowance = ERC20(_token).allowance(address(_wallet), _spender);
                                    if (_amount <= currentAllowance) {
                                        // approve if we reduce the allowance
                                        doApproveToken(_wallet, _token, _spender, _amount);
                                    } else {
                                        // check if delta is under the limit
                                        uint delta = _amount - currentAllowance;
                                        uint256 deltaInEth = priceProvider.getEtherValue(delta, _token);
                                        require(checkAndUpdateDailySpent(_wallet, deltaInEth), "TM: Approve above daily limit");
                                        // approve if under the limit
                                        doApproveToken(_wallet, _token, _spender, _amount);
                                    }
                                }
                            }
                        
                            /**
                            * @dev lets the owner call a contract.
                            * @param _wallet The target wallet.
                            * @param _contract The address of the contract.
                            * @param _value The amount of ETH to transfer as part of call
                            * @param _data The encoded method data
                            */
                            function callContract(
                                BaseWallet _wallet,
                                address _contract,
                                uint256 _value,
                                bytes calldata _data
                            )
                                external
                                onlyWalletOwner(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                // Make sure we don't call a module, the wallet itself, or a supported ERC20
                                authoriseContractCall(_wallet, _contract);
                        
                                if (isWhitelisted(_wallet, _contract)) {
                                    // call to whitelist
                                    doCallContract(_wallet, _contract, _value, _data);
                                } else {
                                    require(checkAndUpdateDailySpent(_wallet, _value), "TM: Call contract above daily limit");
                                    // call under the limit
                                    doCallContract(_wallet, _contract, _value, _data);
                                }
                            }
                        
                            /**
                            * @dev lets the owner do an ERC20 approve followed by a call to a contract.
                            * We assume that the contract will pull the tokens and does not require ETH.
                            * @param _wallet The target wallet.
                            * @param _token The token to approve.
                            * @param _spender The address to approve.
                            * @param _amount The amount of ERC20 tokens to approve.
                            * @param _contract The address of the contract.
                            * @param _data The encoded method data
                            */
                            function approveTokenAndCallContract(
                                BaseWallet _wallet,
                                address _token,
                                address _spender,
                                uint256 _amount,
                                address _contract,
                                bytes calldata _data
                            )
                                external
                                onlyWalletOwner(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                // Make sure we don't call a module, the wallet itself, or a supported ERC20
                                authoriseContractCall(_wallet, _contract);
                        
                                if (!isWhitelisted(_wallet, _spender)) {
                                    // check if the amount is under the daily limit
                                    // check the entire amount because the currently approved amount will be restored and should still count towards the daily limit
                                    uint256 valueInEth = priceProvider.getEtherValue(_amount, _token);
                                    require(checkAndUpdateDailySpent(_wallet, valueInEth), "TM: Approve above daily limit");
                                }
                        
                                doApproveTokenAndCallContract(_wallet, _token, _spender, _amount, _contract, _data);
                            }
                        
                            /**
                             * @dev Adds an address to the whitelist of a wallet.
                             * @param _wallet The target wallet.
                             * @param _target The address to add.
                             */
                            function addToWhitelist(
                                BaseWallet _wallet,
                                address _target
                            )
                                external
                                onlyWalletOwner(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                require(!isWhitelisted(_wallet, _target), "TT: target already whitelisted");
                                // solium-disable-next-line security/no-block-members
                                uint256 whitelistAfter = now.add(securityPeriod);
                                transferStorage.setWhitelist(_wallet, _target, whitelistAfter);
                                emit AddedToWhitelist(address(_wallet), _target, uint64(whitelistAfter));
                            }
                        
                            /**
                             * @dev Removes an address from the whitelist of a wallet.
                             * @param _wallet The target wallet.
                             * @param _target The address to remove.
                             */
                            function removeFromWhitelist(
                                BaseWallet _wallet,
                                address _target
                            )
                                external
                                onlyWalletOwner(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                require(isWhitelisted(_wallet, _target), "TT: target not whitelisted");
                                transferStorage.setWhitelist(_wallet, _target, 0);
                                emit RemovedFromWhitelist(address(_wallet), _target);
                            }
                        
                            /**
                            * @dev Executes a pending transfer for a wallet.
                            * The method can be called by anyone to enable orchestration.
                            * @param _wallet The target wallet.
                            * @param _token The token of the pending transfer.
                            * @param _to The destination address of the pending transfer.
                            * @param _amount The amount of token to transfer of the pending transfer.
                            * @param _data The data associated to the pending transfer.
                            * @param _block The block at which the pending transfer was created.
                            */
                            function executePendingTransfer(
                                BaseWallet _wallet,
                                address _token,
                                address _to,
                                uint _amount,
                                bytes calldata _data,
                                uint _block
                            )
                                external
                                onlyWhenUnlocked(_wallet)
                            {
                                bytes32 id = keccak256(abi.encodePacked(ActionType.Transfer, _token, _to, _amount, _data, _block));
                                uint executeAfter = configs[address(_wallet)].pendingActions[id];
                                require(executeAfter > 0, "TT: unknown pending transfer");
                                uint executeBefore = executeAfter.add(securityWindow);
                                // solium-disable-next-line security/no-block-members
                                require(executeAfter <= now && now <= executeBefore, "TT: transfer outside of the execution window");
                                delete configs[address(_wallet)].pendingActions[id];
                                doTransfer(_wallet, _token, _to, _amount, _data);
                                emit PendingTransferExecuted(address(_wallet), id);
                            }
                        
                            function cancelPendingTransfer(
                                BaseWallet _wallet,
                                bytes32 _id
                            )
                                external
                                onlyWalletOwner(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                require(configs[address(_wallet)].pendingActions[_id] > 0, "TT: unknown pending action");
                                delete configs[address(_wallet)].pendingActions[_id];
                                emit PendingTransferCanceled(address(_wallet), _id);
                            }
                        
                            /**
                             * @dev Lets the owner of a wallet change its daily limit.
                             * The limit is expressed in ETH. Changes to the limit take 24 hours.
                             * @param _wallet The target wallet.
                             * @param _newLimit The new limit.
                             */
                            function changeLimit(BaseWallet _wallet, uint256 _newLimit) external onlyWalletOwner(_wallet) onlyWhenUnlocked(_wallet) {
                                changeLimit(_wallet, _newLimit, securityPeriod);
                            }
                        
                            /**
                             * @dev Convenience method to disable the limit
                             * The limit is disabled by setting it to an arbitrary large value.
                             * @param _wallet The target wallet.
                             */
                            function disableLimit(BaseWallet _wallet) external onlyWalletOwner(_wallet) onlyWhenUnlocked(_wallet) {
                                disableLimit(_wallet, securityPeriod);
                            }
                        
                            /**
                            * @dev Checks if an address is whitelisted for a wallet.
                            * @param _wallet The target wallet.
                            * @param _target The address.
                            * @return true if the address is whitelisted.
                            */
                            function isWhitelisted(BaseWallet _wallet, address _target) public view returns (bool _isWhitelisted) {
                                uint whitelistAfter = transferStorage.getWhitelist(_wallet, _target);
                                // solium-disable-next-line security/no-block-members
                                return whitelistAfter > 0 && whitelistAfter < now;
                            }
                        
                            /**
                            * @dev Gets the info of a pending transfer for a wallet.
                            * @param _wallet The target wallet.
                            * @param _id The pending transfer ID.
                            * @return the epoch time at which the pending transfer can be executed.
                            */
                            function getPendingTransfer(BaseWallet _wallet, bytes32 _id) external view returns (uint64 _executeAfter) {
                                _executeAfter = uint64(configs[address(_wallet)].pendingActions[_id]);
                            }
                        
                            /**
                            * @dev Implementation of EIP 1271.
                            * Should return whether the signature provided is valid for the provided data.
                            * @param _data Arbitrary length data signed on the behalf of address(this)
                            * @param _signature Signature byte array associated with _data
                            */
                            function isValidSignature(bytes calldata _data, bytes calldata _signature) external view returns (bytes4) {
                                bytes32 msgHash = keccak256(abi.encodePacked(_data));
                                isValidSignature(msgHash, _signature);
                                return ERC1271_ISVALIDSIGNATURE_BYTES;
                            }
                        
                            /**
                            * @dev Implementation of EIP 1271.
                            * Should return whether the signature provided is valid for the provided data.
                            * @param _msgHash Hash of a message signed on the behalf of address(this)
                            * @param _signature Signature byte array associated with _msgHash
                            */
                            function isValidSignature(bytes32 _msgHash, bytes memory _signature) public view returns (bytes4) {
                                require(_signature.length == 65, "TM: invalid signature length");
                                address signer = recoverSigner(_msgHash, _signature, 0);
                                require(isOwner(BaseWallet(msg.sender), signer), "TM: Invalid signer");
                                return ERC1271_ISVALIDSIGNATURE_BYTES32;
                            }
                        
                            // *************** Internal Functions ********************* //
                        
                            /**
                             * @dev Creates a new pending action for a wallet.
                             * @param _action The target action.
                             * @param _wallet The target wallet.
                             * @param _token The target token for the action.
                             * @param _to The recipient of the action.
                             * @param _amount The amount of token associated to the action.
                             * @param _data The data associated to the action.
                             * @return the identifier for the new pending action and the time when the action can be executed
                             */
                            function addPendingAction(
                                ActionType _action,
                                BaseWallet _wallet,
                                address _token,
                                address _to,
                                uint _amount,
                                bytes memory _data
                            )
                                internal
                                returns (bytes32 id, uint256 executeAfter)
                            {
                                id = keccak256(abi.encodePacked(_action, _token, _to, _amount, _data, block.number));
                                require(configs[address(_wallet)].pendingActions[id] == 0, "TM: duplicate pending action");
                                // solium-disable-next-line security/no-block-members
                                executeAfter = now.add(securityPeriod);
                                configs[address(_wallet)].pendingActions[id] = executeAfter;
                            }
                        
                            /**
                            * @dev Make sure a contract call is not trying to call a module, the wallet itself, or a supported ERC20.
                            * @param _wallet The target wallet.
                            * @param _contract The address of the contract.
                             */
                            function authoriseContractCall(BaseWallet _wallet, address _contract) internal view {
                                require(
                                    _contract != address(_wallet) && // not the wallet itself
                                    !_wallet.authorised(_contract) && // not an authorised module
                                    (priceProvider.cachedPrices(_contract) == 0 || isLimitDisabled(_wallet)), // not an ERC20 listed in the provider (or limit disabled)
                                    "TM: Forbidden contract");
                            }
                        
                            // *************** Implementation of RelayerModule methods ********************* //
                        
                            // Overrides refund to add the refund in the daily limit.
                            function refund(BaseWallet _wallet, uint _gasUsed, uint _gasPrice, uint _gasLimit, uint _signatures, address _relayer) internal {
                                // 21000 (transaction) + 7620 (execution of refund) + 7324 (execution of updateDailySpent) + 672 to log the event + _gasUsed
                                uint256 amount = 36616 + _gasUsed;
                                if (_gasPrice > 0 && _signatures > 0 && amount <= _gasLimit) {
                                    if (_gasPrice > tx.gasprice) {
                                        amount = amount * tx.gasprice;
                                    } else {
                                        amount = amount * _gasPrice;
                                    }
                                    checkAndUpdateDailySpent(_wallet, amount);
                                    invokeWallet(address(_wallet), _relayer, amount, EMPTY_BYTES);
                                }
                            }
                        
                            // Overrides verifyRefund to add the refund in the daily limit.
                            function verifyRefund(BaseWallet _wallet, uint _gasUsed, uint _gasPrice, uint _signatures) internal view returns (bool) {
                                if (_gasPrice > 0 && _signatures > 0 && (
                                    address(_wallet).balance < _gasUsed * _gasPrice ||
                                    isWithinDailyLimit(_wallet, getCurrentLimit(_wallet), _gasUsed * _gasPrice) == false ||
                                    _wallet.authorised(address(this)) == false
                                ))
                                {
                                    return false;
                                }
                                return true;
                            }
                        }

                        File 2 of 7: ProxyERC20
                        /**
                         *Submitted for verification at Etherscan.io on 2019-08-08
                        */
                        
                        /* ===============================================
                        * Flattened with Solidifier by Coinage
                        * 
                        * https://solidifier.coina.ge
                        * ===============================================
                        */
                        
                        
                        /*
                        -----------------------------------------------------------------
                        FILE INFORMATION
                        -----------------------------------------------------------------
                        
                        file:       Owned.sol
                        version:    1.1
                        author:     Anton Jurisevic
                                    Dominic Romanowski
                        
                        date:       2018-2-26
                        
                        -----------------------------------------------------------------
                        MODULE DESCRIPTION
                        -----------------------------------------------------------------
                        
                        An Owned contract, to be inherited by other contracts.
                        Requires its owner to be explicitly set in the constructor.
                        Provides an onlyOwner access modifier.
                        
                        To change owner, the current owner must nominate the next owner,
                        who then has to accept the nomination. The nomination can be
                        cancelled before it is accepted by the new owner by having the
                        previous owner change the nomination (setting it to 0).
                        
                        -----------------------------------------------------------------
                        */
                        
                        pragma solidity 0.4.25;
                        
                        /**
                         * @title A contract with an owner.
                         * @notice Contract ownership can be transferred by first nominating the new owner,
                         * who must then accept the ownership, which prevents accidental incorrect ownership transfers.
                         */
                        contract Owned {
                            address public owner;
                            address public nominatedOwner;
                        
                            /**
                             * @dev Owned Constructor
                             */
                            constructor(address _owner)
                                public
                            {
                                require(_owner != address(0), "Owner address cannot be 0");
                                owner = _owner;
                                emit OwnerChanged(address(0), _owner);
                            }
                        
                            /**
                             * @notice Nominate a new owner of this contract.
                             * @dev Only the current owner may nominate a new owner.
                             */
                            function nominateNewOwner(address _owner)
                                external
                                onlyOwner
                            {
                                nominatedOwner = _owner;
                                emit OwnerNominated(_owner);
                            }
                        
                            /**
                             * @notice Accept the nomination to be owner.
                             */
                            function acceptOwnership()
                                external
                            {
                                require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
                                emit OwnerChanged(owner, nominatedOwner);
                                owner = nominatedOwner;
                                nominatedOwner = address(0);
                            }
                        
                            modifier onlyOwner
                            {
                                require(msg.sender == owner, "Only the contract owner may perform this action");
                                _;
                            }
                        
                            event OwnerNominated(address newOwner);
                            event OwnerChanged(address oldOwner, address newOwner);
                        }
                        
                        
                        /*
                        -----------------------------------------------------------------
                        FILE INFORMATION
                        -----------------------------------------------------------------
                        
                        file:       Proxy.sol
                        version:    1.3
                        author:     Anton Jurisevic
                        
                        date:       2018-05-29
                        
                        -----------------------------------------------------------------
                        MODULE DESCRIPTION
                        -----------------------------------------------------------------
                        
                        A proxy contract that, if it does not recognise the function
                        being called on it, passes all value and call data to an
                        underlying target contract.
                        
                        This proxy has the capacity to toggle between DELEGATECALL
                        and CALL style proxy functionality.
                        
                        The former executes in the proxy's context, and so will preserve 
                        msg.sender and store data at the proxy address. The latter will not.
                        Therefore, any contract the proxy wraps in the CALL style must
                        implement the Proxyable interface, in order that it can pass msg.sender
                        into the underlying contract as the state parameter, messageSender.
                        
                        -----------------------------------------------------------------
                        */
                        
                        
                        contract Proxy is Owned {
                        
                            Proxyable public target;
                            bool public useDELEGATECALL;
                        
                            constructor(address _owner)
                                Owned(_owner)
                                public
                            {}
                        
                            function setTarget(Proxyable _target)
                                external
                                onlyOwner
                            {
                                target = _target;
                                emit TargetUpdated(_target);
                            }
                        
                            function setUseDELEGATECALL(bool value) 
                                external
                                onlyOwner
                            {
                                useDELEGATECALL = value;
                            }
                        
                            function _emit(bytes callData, uint numTopics, bytes32 topic1, bytes32 topic2, bytes32 topic3, bytes32 topic4)
                                external
                                onlyTarget
                            {
                                uint size = callData.length;
                                bytes memory _callData = callData;
                        
                                assembly {
                                    /* The first 32 bytes of callData contain its length (as specified by the abi). 
                                     * Length is assumed to be a uint256 and therefore maximum of 32 bytes
                                     * in length. It is also leftpadded to be a multiple of 32 bytes.
                                     * This means moving call_data across 32 bytes guarantees we correctly access
                                     * the data itself. */
                                    switch numTopics
                                    case 0 {
                                        log0(add(_callData, 32), size)
                                    } 
                                    case 1 {
                                        log1(add(_callData, 32), size, topic1)
                                    }
                                    case 2 {
                                        log2(add(_callData, 32), size, topic1, topic2)
                                    }
                                    case 3 {
                                        log3(add(_callData, 32), size, topic1, topic2, topic3)
                                    }
                                    case 4 {
                                        log4(add(_callData, 32), size, topic1, topic2, topic3, topic4)
                                    }
                                }
                            }
                        
                            function()
                                external
                                payable
                            {
                                if (useDELEGATECALL) {
                                    assembly {
                                        /* Copy call data into free memory region. */
                                        let free_ptr := mload(0x40)
                                        calldatacopy(free_ptr, 0, calldatasize)
                        
                                        /* Forward all gas and call data to the target contract. */
                                        let result := delegatecall(gas, sload(target_slot), free_ptr, calldatasize, 0, 0)
                                        returndatacopy(free_ptr, 0, returndatasize)
                        
                                        /* Revert if the call failed, otherwise return the result. */
                                        if iszero(result) { revert(free_ptr, returndatasize) }
                                        return(free_ptr, returndatasize)
                                    }
                                } else {
                                    /* Here we are as above, but must send the messageSender explicitly 
                                     * since we are using CALL rather than DELEGATECALL. */
                                    target.setMessageSender(msg.sender);
                                    assembly {
                                        let free_ptr := mload(0x40)
                                        calldatacopy(free_ptr, 0, calldatasize)
                        
                                        /* We must explicitly forward ether to the underlying contract as well. */
                                        let result := call(gas, sload(target_slot), callvalue, free_ptr, calldatasize, 0, 0)
                                        returndatacopy(free_ptr, 0, returndatasize)
                        
                                        if iszero(result) { revert(free_ptr, returndatasize) }
                                        return(free_ptr, returndatasize)
                                    }
                                }
                            }
                        
                            modifier onlyTarget {
                                require(Proxyable(msg.sender) == target, "Must be proxy target");
                                _;
                            }
                        
                            event TargetUpdated(Proxyable newTarget);
                        }
                        
                        
                        /*
                        -----------------------------------------------------------------
                        FILE INFORMATION
                        -----------------------------------------------------------------
                        
                        file:       Proxyable.sol
                        version:    1.1
                        author:     Anton Jurisevic
                        
                        date:       2018-05-15
                        
                        checked:    Mike Spain
                        approved:   Samuel Brooks
                        
                        -----------------------------------------------------------------
                        MODULE DESCRIPTION
                        -----------------------------------------------------------------
                        
                        A proxyable contract that works hand in hand with the Proxy contract
                        to allow for anyone to interact with the underlying contract both
                        directly and through the proxy.
                        
                        -----------------------------------------------------------------
                        */
                        
                        
                        // This contract should be treated like an abstract contract
                        contract Proxyable is Owned {
                            /* The proxy this contract exists behind. */
                            Proxy public proxy;
                            Proxy public integrationProxy;
                        
                            /* The caller of the proxy, passed through to this contract.
                             * Note that every function using this member must apply the onlyProxy or
                             * optionalProxy modifiers, otherwise their invocations can use stale values. */
                            address messageSender;
                        
                            constructor(address _proxy, address _owner)
                                Owned(_owner)
                                public
                            {
                                proxy = Proxy(_proxy);
                                emit ProxyUpdated(_proxy);
                            }
                        
                            function setProxy(address _proxy)
                                external
                                onlyOwner
                            {
                                proxy = Proxy(_proxy);
                                emit ProxyUpdated(_proxy);
                            }
                        
                            function setIntegrationProxy(address _integrationProxy)
                                external
                                onlyOwner
                            {
                                integrationProxy = Proxy(_integrationProxy);
                            }
                        
                            function setMessageSender(address sender)
                                external
                                onlyProxy
                            {
                                messageSender = sender;
                            }
                        
                            modifier onlyProxy {
                                require(Proxy(msg.sender) == proxy || Proxy(msg.sender) == integrationProxy, "Only the proxy can call");
                                _;
                            }
                        
                            modifier optionalProxy
                            {
                                if (Proxy(msg.sender) != proxy && Proxy(msg.sender) != integrationProxy) {
                                    messageSender = msg.sender;
                                }
                                _;
                            }
                        
                            modifier optionalProxy_onlyOwner
                            {
                                if (Proxy(msg.sender) != proxy && Proxy(msg.sender) != integrationProxy) {
                                    messageSender = msg.sender;
                                }
                                require(messageSender == owner, "Owner only function");
                                _;
                            }
                        
                            event ProxyUpdated(address proxyAddress);
                        }
                        
                        
                        /**
                         * @title ERC20 interface
                         * @dev see https://github.com/ethereum/EIPs/issues/20
                         */
                        contract IERC20 {
                            function totalSupply() public view returns (uint);
                        
                            function balanceOf(address owner) public view returns (uint);
                        
                            function allowance(address owner, address spender) public view returns (uint);
                        
                            function transfer(address to, uint value) public returns (bool);
                        
                            function approve(address spender, uint value) public returns (bool);
                        
                            function transferFrom(address from, address to, uint value) public returns (bool);
                        
                            // ERC20 Optional
                            function name() public view returns (string);
                            function symbol() public view returns (string);
                            function decimals() public view returns (uint8);
                        
                            event Transfer(
                              address indexed from,
                              address indexed to,
                              uint value
                            );
                        
                            event Approval(
                              address indexed owner,
                              address indexed spender,
                              uint value
                            );
                        }
                        
                        
                        /*
                        -----------------------------------------------------------------
                        FILE INFORMATION
                        -----------------------------------------------------------------
                        
                        file:       ProxyERC20.sol
                        version:    1.0
                        author:     Jackson Chan, Clinton Ennis
                        
                        date:       2019-06-19
                        
                        -----------------------------------------------------------------
                        MODULE DESCRIPTION
                        -----------------------------------------------------------------
                        
                        A proxy contract that is ERC20 compliant for the Synthetix Network.
                        
                        If it does not recognise a function being called on it, passes all
                        value and call data to an underlying target contract.
                        
                        The ERC20 standard has been explicitly implemented to ensure
                        contract to contract calls are compatable on MAINNET
                        
                        -----------------------------------------------------------------
                        */
                        
                        
                        contract ProxyERC20 is Proxy, IERC20 {
                        
                            constructor(address _owner)
                                Proxy(_owner)
                                public
                            {}
                        
                            // ------------- ERC20 Details ------------- //
                        
                            function name() public view returns (string){
                                // Immutable static call from target contract
                                return IERC20(target).name();
                            }
                        
                            function symbol() public view returns (string){
                                 // Immutable static call from target contract
                                return IERC20(target).symbol();
                            }
                        
                            function decimals() public view returns (uint8){
                                 // Immutable static call from target contract
                                return IERC20(target).decimals();
                            }
                        
                            // ------------- ERC20 Interface ------------- //
                        
                            /**
                            * @dev Total number of tokens in existence
                            */
                            function totalSupply() public view returns (uint256) {
                                // Immutable static call from target contract
                                return IERC20(target).totalSupply();
                            }
                        
                            /**
                            * @dev Gets the balance of the specified address.
                            * @param owner The address to query the balance of.
                            * @return An uint256 representing the amount owned by the passed address.
                            */
                            function balanceOf(address owner) public view returns (uint256) {
                                // Immutable static call from target contract
                                return IERC20(target).balanceOf(owner);
                            }
                        
                            /**
                            * @dev Function to check the amount of tokens that an owner allowed to a spender.
                            * @param owner address The address which owns the funds.
                            * @param spender address The address which will spend the funds.
                            * @return A uint256 specifying the amount of tokens still available for the spender.
                            */
                            function allowance(
                                address owner,
                                address spender
                            )
                                public
                                view
                                returns (uint256)
                            {
                                // Immutable static call from target contract
                                return IERC20(target).allowance(owner, spender);
                            }
                        
                            /**
                            * @dev Transfer token for a specified address
                            * @param to The address to transfer to.
                            * @param value The amount to be transferred.
                            */
                            function transfer(address to, uint256 value) public returns (bool) {
                                // Mutable state call requires the proxy to tell the target who the msg.sender is.
                                target.setMessageSender(msg.sender);
                        
                                // Forward the ERC20 call to the target contract
                                IERC20(target).transfer(to, value);
                        
                                // Event emitting will occur via Synthetix.Proxy._emit()
                                return true;
                            }
                        
                            /**
                            * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                            * Beware that changing an allowance with this method brings the risk that someone may use both the old
                            * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
                            * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
                            * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                            * @param spender The address which will spend the funds.
                            * @param value The amount of tokens to be spent.
                            */
                            function approve(address spender, uint256 value) public returns (bool) {
                                // Mutable state call requires the proxy to tell the target who the msg.sender is.
                                target.setMessageSender(msg.sender);
                        
                                // Forward the ERC20 call to the target contract
                                IERC20(target).approve(spender, value);
                        
                                // Event emitting will occur via Synthetix.Proxy._emit()
                                return true;
                            }
                        
                            /**
                            * @dev Transfer tokens from one address to another
                            * @param from address The address which you want to send tokens from
                            * @param to address The address which you want to transfer to
                            * @param value uint256 the amount of tokens to be transferred
                            */
                            function transferFrom(
                                address from,
                                address to,
                                uint256 value
                            )
                                public
                                returns (bool)
                            {
                                // Mutable state call requires the proxy to tell the target who the msg.sender is.
                                target.setMessageSender(msg.sender);
                        
                                // Forward the ERC20 call to the target contract
                                IERC20(target).transferFrom(from, to, value);
                        
                                // Event emitting will occur via Synthetix.Proxy._emit()
                                return true;
                            }
                        }

                        File 3 of 7: Proxy
                        // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        pragma solidity ^0.5.4;
                        
                        /**
                         * @title Proxy
                         * @dev Basic proxy that delegates all calls to a fixed implementing contract.
                         * The implementing contract cannot be upgraded.
                         * @author Julien Niset - <julien@argent.xyz>
                         */
                        contract Proxy {
                        
                            address implementation;
                        
                            event Received(uint indexed value, address indexed sender, bytes data);
                        
                            constructor(address _implementation) public {
                                implementation = _implementation;
                            }
                        
                            function() external payable {
                        
                                if (msg.data.length == 0 && msg.value > 0) {
                                    emit Received(msg.value, msg.sender, msg.data);
                                } else {
                                    // solium-disable-next-line security/no-inline-assembly
                                    assembly {
                                        let target := sload(0)
                                        calldatacopy(0, 0, calldatasize())
                                        let result := delegatecall(gas, target, 0, calldatasize(), 0, 0)
                                        returndatacopy(0, 0, returndatasize())
                                        switch result
                                        case 0 {revert(0, returndatasize())}
                                        default {return (0, returndatasize())}
                                    }
                                }
                            }
                        }

                        File 4 of 7: BaseWallet
                        // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                        
                        // This program is free software: you can redistribute it and/or modify
                        // it under the terms of the GNU General Public License as published by
                        // the Free Software Foundation, either version 3 of the License, or
                        // (at your option) any later version.
                        
                        // This program is distributed in the hope that it will be useful,
                        // but WITHOUT ANY WARRANTY; without even the implied warranty of
                        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                        // GNU General Public License for more details.
                        
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        
                        pragma solidity ^0.5.4;
                        
                        /**
                         * @title Module
                         * @dev Interface for a module.
                         * A module MUST implement the addModule() method to ensure that a wallet with at least one module
                         * can never end up in a "frozen" state.
                         * @author Julien Niset - <julien@argent.xyz>
                         */
                        interface Module {
                        
                            /**
                             * @dev Inits a module for a wallet by e.g. setting some wallet specific parameters in storage.
                             * @param _wallet The wallet.
                             */
                            function init(BaseWallet _wallet) external;
                        
                            /**
                             * @dev Adds a module to a wallet.
                             * @param _wallet The target wallet.
                             * @param _module The modules to authorise.
                             */
                            function addModule(BaseWallet _wallet, Module _module) external;
                        
                            /**
                            * @dev Utility method to recover any ERC20 token that was sent to the
                            * module by mistake.
                            * @param _token The token to recover.
                            */
                            function recoverToken(address _token) external;
                        }
                        
                        /**
                         * @title BaseWallet
                         * @dev Simple modular wallet that authorises modules to call its invoke() method.
                         * @author Julien Niset - <julien@argent.xyz>
                         */
                        contract BaseWallet {
                        
                            // The implementation of the proxy
                            address public implementation;
                            // The owner
                            address public owner;
                            // The authorised modules
                            mapping (address => bool) public authorised;
                            // The enabled static calls
                            mapping (bytes4 => address) public enabled;
                            // The number of modules
                            uint public modules;
                        
                            event AuthorisedModule(address indexed module, bool value);
                            event EnabledStaticCall(address indexed module, bytes4 indexed method);
                            event Invoked(address indexed module, address indexed target, uint indexed value, bytes data);
                            event Received(uint indexed value, address indexed sender, bytes data);
                            event OwnerChanged(address owner);
                        
                            /**
                             * @dev Throws if the sender is not an authorised module.
                             */
                            modifier moduleOnly {
                                require(authorised[msg.sender], "BW: msg.sender not an authorized module");
                                _;
                            }
                        
                            /**
                             * @dev Inits the wallet by setting the owner and authorising a list of modules.
                             * @param _owner The owner.
                             * @param _modules The modules to authorise.
                             */
                            function init(address _owner, address[] calldata _modules) external {
                                require(owner == address(0) && modules == 0, "BW: wallet already initialised");
                                require(_modules.length > 0, "BW: construction requires at least 1 module");
                                owner = _owner;
                                modules = _modules.length;
                                for (uint256 i = 0; i < _modules.length; i++) {
                                    require(authorised[_modules[i]] == false, "BW: module is already added");
                                    authorised[_modules[i]] = true;
                                    Module(_modules[i]).init(this);
                                    emit AuthorisedModule(_modules[i], true);
                                }
                                if (address(this).balance > 0) {
                                    emit Received(address(this).balance, address(0), "");
                                }
                            }
                        
                            /**
                             * @dev Enables/Disables a module.
                             * @param _module The target module.
                             * @param _value Set to true to authorise the module.
                             */
                            function authoriseModule(address _module, bool _value) external moduleOnly {
                                if (authorised[_module] != _value) {
                                    emit AuthorisedModule(_module, _value);
                                    if (_value == true) {
                                        modules += 1;
                                        authorised[_module] = true;
                                        Module(_module).init(this);
                                    } else {
                                        modules -= 1;
                                        require(modules > 0, "BW: wallet must have at least one module");
                                        delete authorised[_module];
                                    }
                                }
                            }
                        
                            /**
                            * @dev Enables a static method by specifying the target module to which the call
                            * must be delegated.
                            * @param _module The target module.
                            * @param _method The static method signature.
                            */
                            function enableStaticCall(address _module, bytes4 _method) external moduleOnly {
                                require(authorised[_module], "BW: must be an authorised module for static call");
                                enabled[_method] = _module;
                                emit EnabledStaticCall(_module, _method);
                            }
                        
                            /**
                             * @dev Sets a new owner for the wallet.
                             * @param _newOwner The new owner.
                             */
                            function setOwner(address _newOwner) external moduleOnly {
                                require(_newOwner != address(0), "BW: address cannot be null");
                                owner = _newOwner;
                                emit OwnerChanged(_newOwner);
                            }
                        
                            /**
                             * @dev Performs a generic transaction.
                             * @param _target The address for the transaction.
                             * @param _value The value of the transaction.
                             * @param _data The data of the transaction.
                             */
                            function invoke(address _target, uint _value, bytes calldata _data) external moduleOnly returns (bytes memory _result) {
                                bool success;
                                // solium-disable-next-line security/no-call-value
                                (success, _result) = _target.call.value(_value)(_data);
                                if (!success) {
                                    // solium-disable-next-line security/no-inline-assembly
                                    assembly {
                                        returndatacopy(0, 0, returndatasize)
                                        revert(0, returndatasize)
                                    }
                                }
                                emit Invoked(msg.sender, _target, _value, _data);
                            }
                        
                            /**
                             * @dev This method makes it possible for the wallet to comply to interfaces expecting the wallet to
                             * implement specific static methods. It delegates the static call to a target contract if the data corresponds
                             * to an enabled method, or logs the call otherwise.
                             */
                            function() external payable {
                                if (msg.data.length > 0) {
                                    address module = enabled[msg.sig];
                                    if (module == address(0)) {
                                        emit Received(msg.value, msg.sender, msg.data);
                                    } else {
                                        require(authorised[module], "BW: must be an authorised module for static call");
                                        // solium-disable-next-line security/no-inline-assembly
                                        assembly {
                                            calldatacopy(0, 0, calldatasize())
                                            let result := staticcall(gas, module, 0, calldatasize(), 0, 0)
                                            returndatacopy(0, 0, returndatasize())
                                            switch result
                                            case 0 {revert(0, returndatasize())}
                                            default {return (0, returndatasize())}
                                        }
                                    }
                                }
                            }
                        }

                        File 5 of 7: Synth
                        /*
                        
                        ⚠⚠⚠ WARNING WARNING WARNING ⚠⚠⚠
                        
                        This is a TARGET contract - DO NOT CONNECT TO IT DIRECTLY IN YOUR CONTRACTS or DAPPS!
                        
                        This contract has an associated PROXY that MUST be used for all integrations - this TARGET will be REPLACED in an upcoming Synthetix release!
                        The proxy can be found by looking up the PROXY property on this contract.
                        
                        *//*
                           ____            __   __        __   _
                          / __/__ __ ___  / /_ / /  ___  / /_ (_)__ __
                         _\ \ / // // _ \/ __// _ \/ -_)/ __// / \ \ /
                        /___/ \_, //_//_/\__//_//_/\__/ \__//_/ /_\_\
                             /___/
                        
                        * Synthetix: Synth.sol
                        *
                        * Latest source (may be newer): https://github.com/Synthetixio/synthetix/blob/master/contracts/Synth.sol
                        * Docs: https://docs.synthetix.io/contracts/Synth
                        *
                        * Contract Dependencies: 
                        *	- ExternStateToken
                        *	- MixinResolver
                        *	- Owned
                        *	- Proxyable
                        *	- SelfDestructible
                        *	- State
                        * Libraries: 
                        *	- SafeDecimalMath
                        *	- SafeMath
                        *
                        * MIT License
                        * ===========
                        *
                        * Copyright (c) 2020 Synthetix
                        *
                        * Permission is hereby granted, free of charge, to any person obtaining a copy
                        * of this software and associated documentation files (the "Software"), to deal
                        * in the Software without restriction, including without limitation the rights
                        * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
                        * copies of the Software, and to permit persons to whom the Software is
                        * furnished to do so, subject to the following conditions:
                        *
                        * The above copyright notice and this permission notice shall be included in all
                        * copies or substantial portions of the Software.
                        *
                        * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
                        * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
                        * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
                        * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
                        * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
                        * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
                        */
                        
                        /* ===============================================
                        * Flattened with Solidifier by Coinage
                        * 
                        * https://solidifier.coina.ge
                        * ===============================================
                        */
                        
                        
                        pragma solidity ^0.4.24;
                        
                        /**
                         * @title SafeMath
                         * @dev Math operations with safety checks that revert on error
                         */
                        library SafeMath {
                        
                          /**
                          * @dev Multiplies two numbers, reverts on overflow.
                          */
                          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                            // benefit is lost if 'b' is also tested.
                            // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                            if (a == 0) {
                              return 0;
                            }
                        
                            uint256 c = a * b;
                            require(c / a == b);
                        
                            return c;
                          }
                        
                          /**
                          * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
                          */
                          function div(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b > 0); // Solidity only automatically asserts when dividing by 0
                            uint256 c = a / b;
                            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                        
                            return c;
                          }
                        
                          /**
                          * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                          */
                          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b <= a);
                            uint256 c = a - b;
                        
                            return c;
                          }
                        
                          /**
                          * @dev Adds two numbers, reverts on overflow.
                          */
                          function add(uint256 a, uint256 b) internal pure returns (uint256) {
                            uint256 c = a + b;
                            require(c >= a);
                        
                            return c;
                          }
                        
                          /**
                          * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
                          * reverts when dividing by zero.
                          */
                          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                            require(b != 0);
                            return a % b;
                          }
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/SafeDecimalMath
                        library SafeDecimalMath {
                            using SafeMath for uint;
                        
                            /* Number of decimal places in the representations. */
                            uint8 public constant decimals = 18;
                            uint8 public constant highPrecisionDecimals = 27;
                        
                            /* The number representing 1.0. */
                            uint public constant UNIT = 10**uint(decimals);
                        
                            /* The number representing 1.0 for higher fidelity numbers. */
                            uint public constant PRECISE_UNIT = 10**uint(highPrecisionDecimals);
                            uint private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR = 10**uint(highPrecisionDecimals - decimals);
                        
                            /**
                             * @return Provides an interface to UNIT.
                             */
                            function unit() external pure returns (uint) {
                                return UNIT;
                            }
                        
                            /**
                             * @return Provides an interface to PRECISE_UNIT.
                             */
                            function preciseUnit() external pure returns (uint) {
                                return PRECISE_UNIT;
                            }
                        
                            /**
                             * @return The result of multiplying x and y, interpreting the operands as fixed-point
                             * decimals.
                             *
                             * @dev A unit factor is divided out after the product of x and y is evaluated,
                             * so that product must be less than 2**256. As this is an integer division,
                             * the internal division always rounds down. This helps save on gas. Rounding
                             * is more expensive on gas.
                             */
                            function multiplyDecimal(uint x, uint y) internal pure returns (uint) {
                                /* Divide by UNIT to remove the extra factor introduced by the product. */
                                return x.mul(y) / UNIT;
                            }
                        
                            /**
                             * @return The result of safely multiplying x and y, interpreting the operands
                             * as fixed-point decimals of the specified precision unit.
                             *
                             * @dev The operands should be in the form of a the specified unit factor which will be
                             * divided out after the product of x and y is evaluated, so that product must be
                             * less than 2**256.
                             *
                             * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
                             * Rounding is useful when you need to retain fidelity for small decimal numbers
                             * (eg. small fractions or percentages).
                             */
                            function _multiplyDecimalRound(uint x, uint y, uint precisionUnit) private pure returns (uint) {
                                /* Divide by UNIT to remove the extra factor introduced by the product. */
                                uint quotientTimesTen = x.mul(y) / (precisionUnit / 10);
                        
                                if (quotientTimesTen % 10 >= 5) {
                                    quotientTimesTen += 10;
                                }
                        
                                return quotientTimesTen / 10;
                            }
                        
                            /**
                             * @return The result of safely multiplying x and y, interpreting the operands
                             * as fixed-point decimals of a precise unit.
                             *
                             * @dev The operands should be in the precise unit factor which will be
                             * divided out after the product of x and y is evaluated, so that product must be
                             * less than 2**256.
                             *
                             * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
                             * Rounding is useful when you need to retain fidelity for small decimal numbers
                             * (eg. small fractions or percentages).
                             */
                            function multiplyDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) {
                                return _multiplyDecimalRound(x, y, PRECISE_UNIT);
                            }
                        
                            /**
                             * @return The result of safely multiplying x and y, interpreting the operands
                             * as fixed-point decimals of a standard unit.
                             *
                             * @dev The operands should be in the standard unit factor which will be
                             * divided out after the product of x and y is evaluated, so that product must be
                             * less than 2**256.
                             *
                             * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
                             * Rounding is useful when you need to retain fidelity for small decimal numbers
                             * (eg. small fractions or percentages).
                             */
                            function multiplyDecimalRound(uint x, uint y) internal pure returns (uint) {
                                return _multiplyDecimalRound(x, y, UNIT);
                            }
                        
                            /**
                             * @return The result of safely dividing x and y. The return value is a high
                             * precision decimal.
                             *
                             * @dev y is divided after the product of x and the standard precision unit
                             * is evaluated, so the product of x and UNIT must be less than 2**256. As
                             * this is an integer division, the result is always rounded down.
                             * This helps save on gas. Rounding is more expensive on gas.
                             */
                            function divideDecimal(uint x, uint y) internal pure returns (uint) {
                                /* Reintroduce the UNIT factor that will be divided out by y. */
                                return x.mul(UNIT).div(y);
                            }
                        
                            /**
                             * @return The result of safely dividing x and y. The return value is as a rounded
                             * decimal in the precision unit specified in the parameter.
                             *
                             * @dev y is divided after the product of x and the specified precision unit
                             * is evaluated, so the product of x and the specified precision unit must
                             * be less than 2**256. The result is rounded to the nearest increment.
                             */
                            function _divideDecimalRound(uint x, uint y, uint precisionUnit) private pure returns (uint) {
                                uint resultTimesTen = x.mul(precisionUnit * 10).div(y);
                        
                                if (resultTimesTen % 10 >= 5) {
                                    resultTimesTen += 10;
                                }
                        
                                return resultTimesTen / 10;
                            }
                        
                            /**
                             * @return The result of safely dividing x and y. The return value is as a rounded
                             * standard precision decimal.
                             *
                             * @dev y is divided after the product of x and the standard precision unit
                             * is evaluated, so the product of x and the standard precision unit must
                             * be less than 2**256. The result is rounded to the nearest increment.
                             */
                            function divideDecimalRound(uint x, uint y) internal pure returns (uint) {
                                return _divideDecimalRound(x, y, UNIT);
                            }
                        
                            /**
                             * @return The result of safely dividing x and y. The return value is as a rounded
                             * high precision decimal.
                             *
                             * @dev y is divided after the product of x and the high precision unit
                             * is evaluated, so the product of x and the high precision unit must
                             * be less than 2**256. The result is rounded to the nearest increment.
                             */
                            function divideDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) {
                                return _divideDecimalRound(x, y, PRECISE_UNIT);
                            }
                        
                            /**
                             * @dev Convert a standard decimal representation to a high precision one.
                             */
                            function decimalToPreciseDecimal(uint i) internal pure returns (uint) {
                                return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR);
                            }
                        
                            /**
                             * @dev Convert a high precision decimal to a standard decimal representation.
                             */
                            function preciseDecimalToDecimal(uint i) internal pure returns (uint) {
                                uint quotientTimesTen = i / (UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10);
                        
                                if (quotientTimesTen % 10 >= 5) {
                                    quotientTimesTen += 10;
                                }
                        
                                return quotientTimesTen / 10;
                            }
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/Owned
                        contract Owned {
                            address public owner;
                            address public nominatedOwner;
                        
                            /**
                             * @dev Owned Constructor
                             */
                            constructor(address _owner) public {
                                require(_owner != address(0), "Owner address cannot be 0");
                                owner = _owner;
                                emit OwnerChanged(address(0), _owner);
                            }
                        
                            /**
                             * @notice Nominate a new owner of this contract.
                             * @dev Only the current owner may nominate a new owner.
                             */
                            function nominateNewOwner(address _owner) external onlyOwner {
                                nominatedOwner = _owner;
                                emit OwnerNominated(_owner);
                            }
                        
                            /**
                             * @notice Accept the nomination to be owner.
                             */
                            function acceptOwnership() external {
                                require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
                                emit OwnerChanged(owner, nominatedOwner);
                                owner = nominatedOwner;
                                nominatedOwner = address(0);
                            }
                        
                            modifier onlyOwner {
                                require(msg.sender == owner, "Only the contract owner may perform this action");
                                _;
                            }
                        
                            event OwnerNominated(address newOwner);
                            event OwnerChanged(address oldOwner, address newOwner);
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/SelfDestructible
                        contract SelfDestructible is Owned {
                            uint public initiationTime;
                            bool public selfDestructInitiated;
                            address public selfDestructBeneficiary;
                            uint public constant SELFDESTRUCT_DELAY = 4 weeks;
                        
                            /**
                             * @dev Constructor
                             * @param _owner The account which controls this contract.
                             */
                            constructor(address _owner) public Owned(_owner) {
                                require(_owner != address(0), "Owner must not be zero");
                                selfDestructBeneficiary = _owner;
                                emit SelfDestructBeneficiaryUpdated(_owner);
                            }
                        
                            /**
                             * @notice Set the beneficiary address of this contract.
                             * @dev Only the contract owner may call this. The provided beneficiary must be non-null.
                             * @param _beneficiary The address to pay any eth contained in this contract to upon self-destruction.
                             */
                            function setSelfDestructBeneficiary(address _beneficiary) external onlyOwner {
                                require(_beneficiary != address(0), "Beneficiary must not be zero");
                                selfDestructBeneficiary = _beneficiary;
                                emit SelfDestructBeneficiaryUpdated(_beneficiary);
                            }
                        
                            /**
                             * @notice Begin the self-destruction counter of this contract.
                             * Once the delay has elapsed, the contract may be self-destructed.
                             * @dev Only the contract owner may call this.
                             */
                            function initiateSelfDestruct() external onlyOwner {
                                initiationTime = now;
                                selfDestructInitiated = true;
                                emit SelfDestructInitiated(SELFDESTRUCT_DELAY);
                            }
                        
                            /**
                             * @notice Terminate and reset the self-destruction timer.
                             * @dev Only the contract owner may call this.
                             */
                            function terminateSelfDestruct() external onlyOwner {
                                initiationTime = 0;
                                selfDestructInitiated = false;
                                emit SelfDestructTerminated();
                            }
                        
                            /**
                             * @notice If the self-destruction delay has elapsed, destroy this contract and
                             * remit any ether it owns to the beneficiary address.
                             * @dev Only the contract owner may call this.
                             */
                            function selfDestruct() external onlyOwner {
                                require(selfDestructInitiated, "Self Destruct not yet initiated");
                                require(initiationTime + SELFDESTRUCT_DELAY < now, "Self destruct delay not met");
                                address beneficiary = selfDestructBeneficiary;
                                emit SelfDestructed(beneficiary);
                                selfdestruct(beneficiary);
                            }
                        
                            event SelfDestructTerminated();
                            event SelfDestructed(address beneficiary);
                            event SelfDestructInitiated(uint selfDestructDelay);
                            event SelfDestructBeneficiaryUpdated(address newBeneficiary);
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/State
                        contract State is Owned {
                            // the address of the contract that can modify variables
                            // this can only be changed by the owner of this contract
                            address public associatedContract;
                        
                            constructor(address _owner, address _associatedContract) public Owned(_owner) {
                                associatedContract = _associatedContract;
                                emit AssociatedContractUpdated(_associatedContract);
                            }
                        
                            /* ========== SETTERS ========== */
                        
                            // Change the associated contract to a new address
                            function setAssociatedContract(address _associatedContract) external onlyOwner {
                                associatedContract = _associatedContract;
                                emit AssociatedContractUpdated(_associatedContract);
                            }
                        
                            /* ========== MODIFIERS ========== */
                        
                            modifier onlyAssociatedContract {
                                require(msg.sender == associatedContract, "Only the associated contract can perform this action");
                                _;
                            }
                        
                            /* ========== EVENTS ========== */
                        
                            event AssociatedContractUpdated(address associatedContract);
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/TokenState
                        contract TokenState is State {
                            /* ERC20 fields. */
                            mapping(address => uint) public balanceOf;
                            mapping(address => mapping(address => uint)) public allowance;
                        
                            /**
                             * @dev Constructor
                             * @param _owner The address which controls this contract.
                             * @param _associatedContract The ERC20 contract whose state this composes.
                             */
                            constructor(address _owner, address _associatedContract) public State(_owner, _associatedContract) {}
                        
                            /* ========== SETTERS ========== */
                        
                            /**
                             * @notice Set ERC20 allowance.
                             * @dev Only the associated contract may call this.
                             * @param tokenOwner The authorising party.
                             * @param spender The authorised party.
                             * @param value The total value the authorised party may spend on the
                             * authorising party's behalf.
                             */
                            function setAllowance(address tokenOwner, address spender, uint value) external onlyAssociatedContract {
                                allowance[tokenOwner][spender] = value;
                            }
                        
                            /**
                             * @notice Set the balance in a given account
                             * @dev Only the associated contract may call this.
                             * @param account The account whose value to set.
                             * @param value The new balance of the given account.
                             */
                            function setBalanceOf(address account, uint value) external onlyAssociatedContract {
                                balanceOf[account] = value;
                            }
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/Proxy
                        contract Proxy is Owned {
                            Proxyable public target;
                            bool public useDELEGATECALL;
                        
                            constructor(address _owner) public Owned(_owner) {}
                        
                            function setTarget(Proxyable _target) external onlyOwner {
                                target = _target;
                                emit TargetUpdated(_target);
                            }
                        
                            function setUseDELEGATECALL(bool value) external onlyOwner {
                                useDELEGATECALL = value;
                            }
                        
                            function _emit(bytes callData, uint numTopics, bytes32 topic1, bytes32 topic2, bytes32 topic3, bytes32 topic4)
                                external
                                onlyTarget
                            {
                                uint size = callData.length;
                                bytes memory _callData = callData;
                        
                                assembly {
                                    /* The first 32 bytes of callData contain its length (as specified by the abi).
                                     * Length is assumed to be a uint256 and therefore maximum of 32 bytes
                                     * in length. It is also leftpadded to be a multiple of 32 bytes.
                                     * This means moving call_data across 32 bytes guarantees we correctly access
                                     * the data itself. */
                                    switch numTopics
                                        case 0 {
                                            log0(add(_callData, 32), size)
                                        }
                                        case 1 {
                                            log1(add(_callData, 32), size, topic1)
                                        }
                                        case 2 {
                                            log2(add(_callData, 32), size, topic1, topic2)
                                        }
                                        case 3 {
                                            log3(add(_callData, 32), size, topic1, topic2, topic3)
                                        }
                                        case 4 {
                                            log4(add(_callData, 32), size, topic1, topic2, topic3, topic4)
                                        }
                                }
                            }
                        
                            function() external payable {
                                if (useDELEGATECALL) {
                                    assembly {
                                        /* Copy call data into free memory region. */
                                        let free_ptr := mload(0x40)
                                        calldatacopy(free_ptr, 0, calldatasize)
                        
                                        /* Forward all gas and call data to the target contract. */
                                        let result := delegatecall(gas, sload(target_slot), free_ptr, calldatasize, 0, 0)
                                        returndatacopy(free_ptr, 0, returndatasize)
                        
                                        /* Revert if the call failed, otherwise return the result. */
                                        if iszero(result) {
                                            revert(free_ptr, returndatasize)
                                        }
                                        return(free_ptr, returndatasize)
                                    }
                                } else {
                                    /* Here we are as above, but must send the messageSender explicitly
                                     * since we are using CALL rather than DELEGATECALL. */
                                    target.setMessageSender(msg.sender);
                                    assembly {
                                        let free_ptr := mload(0x40)
                                        calldatacopy(free_ptr, 0, calldatasize)
                        
                                        /* We must explicitly forward ether to the underlying contract as well. */
                                        let result := call(gas, sload(target_slot), callvalue, free_ptr, calldatasize, 0, 0)
                                        returndatacopy(free_ptr, 0, returndatasize)
                        
                                        if iszero(result) {
                                            revert(free_ptr, returndatasize)
                                        }
                                        return(free_ptr, returndatasize)
                                    }
                                }
                            }
                        
                            modifier onlyTarget {
                                require(Proxyable(msg.sender) == target, "Must be proxy target");
                                _;
                            }
                        
                            event TargetUpdated(Proxyable newTarget);
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/Proxyable
                        contract Proxyable is Owned {
                            // This contract should be treated like an abstract contract
                        
                            /* The proxy this contract exists behind. */
                            Proxy public proxy;
                            Proxy public integrationProxy;
                        
                            /* The caller of the proxy, passed through to this contract.
                             * Note that every function using this member must apply the onlyProxy or
                             * optionalProxy modifiers, otherwise their invocations can use stale values. */
                            address public messageSender;
                        
                            constructor(address _proxy, address _owner) public Owned(_owner) {
                                proxy = Proxy(_proxy);
                                emit ProxyUpdated(_proxy);
                            }
                        
                            function setProxy(address _proxy) external onlyOwner {
                                proxy = Proxy(_proxy);
                                emit ProxyUpdated(_proxy);
                            }
                        
                            function setIntegrationProxy(address _integrationProxy) external onlyOwner {
                                integrationProxy = Proxy(_integrationProxy);
                            }
                        
                            function setMessageSender(address sender) external onlyProxy {
                                messageSender = sender;
                            }
                        
                            modifier onlyProxy {
                                require(Proxy(msg.sender) == proxy || Proxy(msg.sender) == integrationProxy, "Only the proxy can call");
                                _;
                            }
                        
                            modifier optionalProxy {
                                if (Proxy(msg.sender) != proxy && Proxy(msg.sender) != integrationProxy && messageSender != msg.sender) {
                                    messageSender = msg.sender;
                                }
                                _;
                            }
                        
                            modifier optionalProxy_onlyOwner {
                                if (Proxy(msg.sender) != proxy && Proxy(msg.sender) != integrationProxy && messageSender != msg.sender) {
                                    messageSender = msg.sender;
                                }
                                require(messageSender == owner, "Owner only function");
                                _;
                            }
                        
                            event ProxyUpdated(address proxyAddress);
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/ExternStateToken
                        contract ExternStateToken is SelfDestructible, Proxyable {
                            using SafeMath for uint;
                            using SafeDecimalMath for uint;
                        
                            /* ========== STATE VARIABLES ========== */
                        
                            /* Stores balances and allowances. */
                            TokenState public tokenState;
                        
                            /* Other ERC20 fields. */
                            string public name;
                            string public symbol;
                            uint public totalSupply;
                            uint8 public decimals;
                        
                            /**
                             * @dev Constructor.
                             * @param _proxy The proxy associated with this contract.
                             * @param _name Token's ERC20 name.
                             * @param _symbol Token's ERC20 symbol.
                             * @param _totalSupply The total supply of the token.
                             * @param _tokenState The TokenState contract address.
                             * @param _owner The owner of this contract.
                             */
                            constructor(
                                address _proxy,
                                TokenState _tokenState,
                                string _name,
                                string _symbol,
                                uint _totalSupply,
                                uint8 _decimals,
                                address _owner
                            ) public SelfDestructible(_owner) Proxyable(_proxy, _owner) {
                                tokenState = _tokenState;
                        
                                name = _name;
                                symbol = _symbol;
                                totalSupply = _totalSupply;
                                decimals = _decimals;
                            }
                        
                            /* ========== VIEWS ========== */
                        
                            /**
                             * @notice Returns the ERC20 allowance of one party to spend on behalf of another.
                             * @param owner The party authorising spending of their funds.
                             * @param spender The party spending tokenOwner's funds.
                             */
                            function allowance(address owner, address spender) public view returns (uint) {
                                return tokenState.allowance(owner, spender);
                            }
                        
                            /**
                             * @notice Returns the ERC20 token balance of a given account.
                             */
                            function balanceOf(address account) public view returns (uint) {
                                return tokenState.balanceOf(account);
                            }
                        
                            /* ========== MUTATIVE FUNCTIONS ========== */
                        
                            /**
                             * @notice Set the address of the TokenState contract.
                             * @dev This can be used to "pause" transfer functionality, by pointing the tokenState at 0x000..
                             * as balances would be unreachable.
                             */
                            function setTokenState(TokenState _tokenState) external optionalProxy_onlyOwner {
                                tokenState = _tokenState;
                                emitTokenStateUpdated(_tokenState);
                            }
                        
                            function _internalTransfer(address from, address to, uint value) internal returns (bool) {
                                /* Disallow transfers to irretrievable-addresses. */
                                require(to != address(0) && to != address(this) && to != address(proxy), "Cannot transfer to this address");
                        
                                // Insufficient balance will be handled by the safe subtraction.
                                tokenState.setBalanceOf(from, tokenState.balanceOf(from).sub(value));
                                tokenState.setBalanceOf(to, tokenState.balanceOf(to).add(value));
                        
                                // Emit a standard ERC20 transfer event
                                emitTransfer(from, to, value);
                        
                                return true;
                            }
                        
                            /**
                             * @dev Perform an ERC20 token transfer. Designed to be called by transfer functions possessing
                             * the onlyProxy or optionalProxy modifiers.
                             */
                            function _transfer_byProxy(address from, address to, uint value) internal returns (bool) {
                                return _internalTransfer(from, to, value);
                            }
                        
                            /**
                             * @dev Perform an ERC20 token transferFrom. Designed to be called by transferFrom functions
                             * possessing the optionalProxy or optionalProxy modifiers.
                             */
                            function _transferFrom_byProxy(address sender, address from, address to, uint value) internal returns (bool) {
                                /* Insufficient allowance will be handled by the safe subtraction. */
                                tokenState.setAllowance(from, sender, tokenState.allowance(from, sender).sub(value));
                                return _internalTransfer(from, to, value);
                            }
                        
                            /**
                             * @notice Approves spender to transfer on the message sender's behalf.
                             */
                            function approve(address spender, uint value) public optionalProxy returns (bool) {
                                address sender = messageSender;
                        
                                tokenState.setAllowance(sender, spender, value);
                                emitApproval(sender, spender, value);
                                return true;
                            }
                        
                            /* ========== EVENTS ========== */
                        
                            event Transfer(address indexed from, address indexed to, uint value);
                            bytes32 constant TRANSFER_SIG = keccak256("Transfer(address,address,uint256)");
                        
                            function emitTransfer(address from, address to, uint value) internal {
                                proxy._emit(abi.encode(value), 3, TRANSFER_SIG, bytes32(from), bytes32(to), 0);
                            }
                        
                            event Approval(address indexed owner, address indexed spender, uint value);
                            bytes32 constant APPROVAL_SIG = keccak256("Approval(address,address,uint256)");
                        
                            function emitApproval(address owner, address spender, uint value) internal {
                                proxy._emit(abi.encode(value), 3, APPROVAL_SIG, bytes32(owner), bytes32(spender), 0);
                            }
                        
                            event TokenStateUpdated(address newTokenState);
                            bytes32 constant TOKENSTATEUPDATED_SIG = keccak256("TokenStateUpdated(address)");
                        
                            function emitTokenStateUpdated(address newTokenState) internal {
                                proxy._emit(abi.encode(newTokenState), 1, TOKENSTATEUPDATED_SIG, 0, 0, 0);
                            }
                        }
                        
                        
                        interface ISystemStatus {
                            function requireSystemActive() external view;
                        
                            function requireIssuanceActive() external view;
                        
                            function requireExchangeActive() external view;
                        
                            function requireSynthActive(bytes32 currencyKey) external view;
                        
                            function requireSynthsActive(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view;
                        }
                        
                        
                        /**
                         * @title FeePool Interface
                         * @notice Abstract contract to hold public getters
                         */
                        contract IFeePool {
                            address public FEE_ADDRESS;
                            uint public exchangeFeeRate;
                        
                            function amountReceivedFromExchange(uint value) external view returns (uint);
                        
                            function amountReceivedFromTransfer(uint value) external view returns (uint);
                        
                            function recordFeePaid(uint sUSDAmount) external;
                        
                            function appendAccountIssuanceRecord(address account, uint lockedAmount, uint debtEntryIndex) external;
                        
                            function setRewardsToDistribute(uint amount) external;
                        }
                        
                        
                        /**
                         * @title SynthetixState interface contract
                         * @notice Abstract contract to hold public getters
                         */
                        contract ISynthetixState {
                            // A struct for handing values associated with an individual user's debt position
                            struct IssuanceData {
                                // Percentage of the total debt owned at the time
                                // of issuance. This number is modified by the global debt
                                // delta array. You can figure out a user's exit price and
                                // collateralisation ratio using a combination of their initial
                                // debt and the slice of global debt delta which applies to them.
                                uint initialDebtOwnership;
                                // This lets us know when (in relative terms) the user entered
                                // the debt pool so we can calculate their exit price and
                                // collateralistion ratio
                                uint debtEntryIndex;
                            }
                        
                            uint[] public debtLedger;
                            uint public issuanceRatio;
                            mapping(address => IssuanceData) public issuanceData;
                        
                            function debtLedgerLength() external view returns (uint);
                        
                            function hasIssued(address account) external view returns (bool);
                        
                            function incrementTotalIssuerCount() external;
                        
                            function decrementTotalIssuerCount() external;
                        
                            function setCurrentIssuanceData(address account, uint initialDebtOwnership) external;
                        
                            function lastDebtLedgerEntry() external view returns (uint);
                        
                            function appendDebtLedgerValue(uint value) external;
                        
                            function clearIssuanceData(address account) external;
                        }
                        
                        
                        interface ISynth {
                            function burn(address account, uint amount) external;
                        
                            function issue(address account, uint amount) external;
                        
                            function transfer(address to, uint value) external returns (bool);
                        
                            function transferFrom(address from, address to, uint value) external returns (bool);
                        
                            function transferFromAndSettle(address from, address to, uint value) external returns (bool);
                        
                            function balanceOf(address owner) external view returns (uint);
                        }
                        
                        
                        /**
                         * @title SynthetixEscrow interface
                         */
                        interface ISynthetixEscrow {
                            function balanceOf(address account) public view returns (uint);
                        
                            function appendVestingEntry(address account, uint quantity) public;
                        }
                        
                        
                        /**
                         * @title ExchangeRates interface
                         */
                        interface IExchangeRates {
                            function effectiveValue(bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey)
                                external
                                view
                                returns (uint);
                        
                            function rateForCurrency(bytes32 currencyKey) external view returns (uint);
                        
                            function ratesForCurrencies(bytes32[] currencyKeys) external view returns (uint[] memory);
                        
                            function rateIsStale(bytes32 currencyKey) external view returns (bool);
                        
                            function rateIsFrozen(bytes32 currencyKey) external view returns (bool);
                        
                            function anyRateIsStale(bytes32[] currencyKeys) external view returns (bool);
                        
                            function getCurrentRoundId(bytes32 currencyKey) external view returns (uint);
                        
                            function effectiveValueAtRound(
                                bytes32 sourceCurrencyKey,
                                uint sourceAmount,
                                bytes32 destinationCurrencyKey,
                                uint roundIdForSrc,
                                uint roundIdForDest
                            ) external view returns (uint);
                        
                            function getLastRoundIdBeforeElapsedSecs(
                                bytes32 currencyKey,
                                uint startingRoundId,
                                uint startingTimestamp,
                                uint timediff
                            ) external view returns (uint);
                        
                            function ratesAndStaleForCurrencies(bytes32[] currencyKeys) external view returns (uint[], bool);
                        
                            function rateAndTimestampAtRound(bytes32 currencyKey, uint roundId) external view returns (uint rate, uint time);
                        }
                        
                        
                        /**
                         * @title Synthetix interface contract
                         * @notice Abstract contract to hold public getters
                         * @dev pseudo interface, actually declared as contract to hold the public getters
                         */
                        
                        
                        contract ISynthetix {
                            // ========== PUBLIC STATE VARIABLES ==========
                        
                            uint public totalSupply;
                        
                            mapping(bytes32 => Synth) public synths;
                        
                            mapping(address => bytes32) public synthsByAddress;
                        
                            // ========== PUBLIC FUNCTIONS ==========
                        
                            function balanceOf(address account) public view returns (uint);
                        
                            function transfer(address to, uint value) public returns (bool);
                        
                            function transferFrom(address from, address to, uint value) public returns (bool);
                        
                            function exchange(bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey)
                                external
                                returns (uint amountReceived);
                        
                            function issueSynths(uint amount) external;
                        
                            function issueMaxSynths() external;
                        
                            function burnSynths(uint amount) external;
                        
                            function burnSynthsToTarget() external;
                        
                            function settle(bytes32 currencyKey) external returns (uint reclaimed, uint refunded, uint numEntries);
                        
                            function collateralisationRatio(address issuer) public view returns (uint);
                        
                            function totalIssuedSynths(bytes32 currencyKey) public view returns (uint);
                        
                            function totalIssuedSynthsExcludeEtherCollateral(bytes32 currencyKey) public view returns (uint);
                        
                            function debtBalanceOf(address issuer, bytes32 currencyKey) public view returns (uint);
                        
                            function debtBalanceOfAndTotalDebt(address issuer, bytes32 currencyKey)
                                public
                                view
                                returns (uint debtBalance, uint totalSystemValue);
                        
                            function remainingIssuableSynths(address issuer)
                                public
                                view
                                returns (uint maxIssuable, uint alreadyIssued, uint totalSystemDebt);
                        
                            function maxIssuableSynths(address issuer) public view returns (uint maxIssuable);
                        
                            function isWaitingPeriod(bytes32 currencyKey) external view returns (bool);
                        
                            function emitSynthExchange(
                                address account,
                                bytes32 fromCurrencyKey,
                                uint fromAmount,
                                bytes32 toCurrencyKey,
                                uint toAmount,
                                address toAddress
                            ) external;
                        
                            function emitExchangeReclaim(address account, bytes32 currencyKey, uint amount) external;
                        
                            function emitExchangeRebate(address account, bytes32 currencyKey, uint amount) external;
                        }
                        
                        
                        interface IExchanger {
                            function maxSecsLeftInWaitingPeriod(address account, bytes32 currencyKey) external view returns (uint);
                        
                            function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view returns (uint);
                        
                            function settlementOwing(address account, bytes32 currencyKey)
                                external
                                view
                                returns (uint reclaimAmount, uint rebateAmount, uint numEntries);
                        
                            function settle(address from, bytes32 currencyKey) external returns (uint reclaimed, uint refunded, uint numEntries);
                        
                            function exchange(
                                address from,
                                bytes32 sourceCurrencyKey,
                                uint sourceAmount,
                                bytes32 destinationCurrencyKey,
                                address destinationAddress
                            ) external returns (uint amountReceived);
                        
                            function exchangeOnBehalf(
                                address exchangeForAddress,
                                address from,
                                bytes32 sourceCurrencyKey,
                                uint sourceAmount,
                                bytes32 destinationCurrencyKey
                            ) external returns (uint amountReceived);
                        
                            function calculateAmountAfterSettlement(address from, bytes32 currencyKey, uint amount, uint refunded)
                                external
                                view
                                returns (uint amountAfterSettlement);
                        }
                        
                        
                        interface IIssuer {
                            function issueSynths(address from, uint amount) external;
                        
                            function issueSynthsOnBehalf(address issueFor, address from, uint amount) external;
                        
                            function issueMaxSynths(address from) external;
                        
                            function issueMaxSynthsOnBehalf(address issueFor, address from) external;
                        
                            function burnSynths(address from, uint amount) external;
                        
                            function burnSynthsOnBehalf(address burnForAddress, address from, uint amount) external;
                        
                            function burnSynthsToTarget(address from) external;
                        
                            function burnSynthsToTargetOnBehalf(address burnForAddress, address from) external;
                        
                            function canBurnSynths(address account) external view returns (bool);
                        
                            function lastIssueEvent(address account) external view returns (uint);
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/AddressResolver
                        contract AddressResolver is Owned {
                            mapping(bytes32 => address) public repository;
                        
                            constructor(address _owner) public Owned(_owner) {}
                        
                            /* ========== MUTATIVE FUNCTIONS ========== */
                        
                            function importAddresses(bytes32[] names, address[] destinations) public onlyOwner {
                                require(names.length == destinations.length, "Input lengths must match");
                        
                                for (uint i = 0; i < names.length; i++) {
                                    repository[names[i]] = destinations[i];
                                }
                            }
                        
                            /* ========== VIEWS ========== */
                        
                            function getAddress(bytes32 name) public view returns (address) {
                                return repository[name];
                            }
                        
                            function requireAndGetAddress(bytes32 name, string reason) public view returns (address) {
                                address _foundAddress = repository[name];
                                require(_foundAddress != address(0), reason);
                                return _foundAddress;
                            }
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/MixinResolver
                        contract MixinResolver is Owned {
                            AddressResolver public resolver;
                        
                            mapping(bytes32 => address) private addressCache;
                        
                            bytes32[] public resolverAddressesRequired;
                        
                            uint public constant MAX_ADDRESSES_FROM_RESOLVER = 24;
                        
                            constructor(address _owner, address _resolver, bytes32[MAX_ADDRESSES_FROM_RESOLVER] _addressesToCache)
                                public
                                Owned(_owner)
                            {
                                for (uint i = 0; i < _addressesToCache.length; i++) {
                                    if (_addressesToCache[i] != bytes32(0)) {
                                        resolverAddressesRequired.push(_addressesToCache[i]);
                                    } else {
                                        // End early once an empty item is found - assumes there are no empty slots in
                                        // _addressesToCache
                                        break;
                                    }
                                }
                                resolver = AddressResolver(_resolver);
                                // Do not sync the cache as addresses may not be in the resolver yet
                            }
                        
                            /* ========== SETTERS ========== */
                            function setResolverAndSyncCache(AddressResolver _resolver) external onlyOwner {
                                resolver = _resolver;
                        
                                for (uint i = 0; i < resolverAddressesRequired.length; i++) {
                                    bytes32 name = resolverAddressesRequired[i];
                                    // Note: can only be invoked once the resolver has all the targets needed added
                                    addressCache[name] = resolver.requireAndGetAddress(name, "Resolver missing target");
                                }
                            }
                        
                            /* ========== VIEWS ========== */
                        
                            function requireAndGetAddress(bytes32 name, string reason) internal view returns (address) {
                                address _foundAddress = addressCache[name];
                                require(_foundAddress != address(0), reason);
                                return _foundAddress;
                            }
                        
                            // Note: this could be made external in a utility contract if addressCache was made public
                            // (used for deployment)
                            function isResolverCached(AddressResolver _resolver) external view returns (bool) {
                                if (resolver != _resolver) {
                                    return false;
                                }
                        
                                // otherwise, check everything
                                for (uint i = 0; i < resolverAddressesRequired.length; i++) {
                                    bytes32 name = resolverAddressesRequired[i];
                                    // false if our cache is invalid or if the resolver doesn't have the required address
                                    if (resolver.getAddress(name) != addressCache[name] || addressCache[name] == address(0)) {
                                        return false;
                                    }
                                }
                        
                                return true;
                            }
                        
                            // Note: can be made external into a utility contract (used for deployment)
                            function getResolverAddressesRequired() external view returns (bytes32[MAX_ADDRESSES_FROM_RESOLVER] addressesRequired) {
                                for (uint i = 0; i < resolverAddressesRequired.length; i++) {
                                    addressesRequired[i] = resolverAddressesRequired[i];
                                }
                            }
                        
                            /* ========== INTERNAL FUNCTIONS ========== */
                            function appendToAddressCache(bytes32 name) internal {
                                resolverAddressesRequired.push(name);
                                require(resolverAddressesRequired.length < MAX_ADDRESSES_FROM_RESOLVER, "Max resolver cache size met");
                                // Because this is designed to be called internally in constructors, we don't
                                // check the address exists already in the resolver
                                addressCache[name] = resolver.getAddress(name);
                            }
                        }
                        
                        
                        // https://docs.synthetix.io/contracts/Synth
                        contract Synth is ExternStateToken, MixinResolver {
                            /* ========== STATE VARIABLES ========== */
                        
                            // Currency key which identifies this Synth to the Synthetix system
                            bytes32 public currencyKey;
                        
                            uint8 public constant DECIMALS = 18;
                        
                            // Where fees are pooled in sUSD
                            address public constant FEE_ADDRESS = 0xfeEFEEfeefEeFeefEEFEEfEeFeefEEFeeFEEFEeF;
                        
                            /* ========== ADDRESS RESOLVER CONFIGURATION ========== */
                        
                            bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus";
                            bytes32 private constant CONTRACT_SYNTHETIX = "Synthetix";
                            bytes32 private constant CONTRACT_EXCHANGER = "Exchanger";
                            bytes32 private constant CONTRACT_ISSUER = "Issuer";
                            bytes32 private constant CONTRACT_FEEPOOL = "FeePool";
                        
                            bytes32[24] internal addressesToCache = [
                                CONTRACT_SYSTEMSTATUS,
                                CONTRACT_SYNTHETIX,
                                CONTRACT_EXCHANGER,
                                CONTRACT_ISSUER,
                                CONTRACT_FEEPOOL
                            ];
                        
                            /* ========== CONSTRUCTOR ========== */
                        
                            constructor(
                                address _proxy,
                                TokenState _tokenState,
                                string _tokenName,
                                string _tokenSymbol,
                                address _owner,
                                bytes32 _currencyKey,
                                uint _totalSupply,
                                address _resolver
                            )
                                public
                                ExternStateToken(_proxy, _tokenState, _tokenName, _tokenSymbol, _totalSupply, DECIMALS, _owner)
                                MixinResolver(_owner, _resolver, addressesToCache)
                            {
                                require(_proxy != address(0), "_proxy cannot be 0");
                                require(_owner != 0, "_owner cannot be 0");
                        
                                currencyKey = _currencyKey;
                            }
                        
                            /* ========== MUTATIVE FUNCTIONS ========== */
                        
                            function transfer(address to, uint value) public optionalProxy returns (bool) {
                                _ensureCanTransfer(messageSender, value);
                        
                                // transfers to FEE_ADDRESS will be exchanged into sUSD and recorded as fee
                                if (to == FEE_ADDRESS) {
                                    return _transferToFeeAddress(to, value);
                                }
                        
                                // transfers to 0x address will be burned
                                if (to == address(0)) {
                                    return _internalBurn(messageSender, value);
                                }
                        
                                return super._internalTransfer(messageSender, to, value);
                            }
                        
                            function transferAndSettle(address to, uint value) public optionalProxy returns (bool) {
                                systemStatus().requireSynthActive(currencyKey);
                        
                                (, , uint numEntriesSettled) = exchanger().settle(messageSender, currencyKey);
                        
                                // Save gas instead of calling transferableSynths
                                uint balanceAfter = value;
                        
                                if (numEntriesSettled > 0) {
                                    balanceAfter = tokenState.balanceOf(messageSender);
                                }
                        
                                // Reduce the value to transfer if balance is insufficient after reclaimed
                                value = value > balanceAfter ? balanceAfter : value;
                        
                                return super._internalTransfer(messageSender, to, value);
                            }
                        
                            function transferFrom(address from, address to, uint value) public optionalProxy returns (bool) {
                                _ensureCanTransfer(from, value);
                        
                                return _internalTransferFrom(from, to, value);
                            }
                        
                            function transferFromAndSettle(address from, address to, uint value) public optionalProxy returns (bool) {
                                systemStatus().requireSynthActive(currencyKey);
                        
                                (, , uint numEntriesSettled) = exchanger().settle(from, currencyKey);
                        
                                // Save gas instead of calling transferableSynths
                                uint balanceAfter = value;
                        
                                if (numEntriesSettled > 0) {
                                    balanceAfter = tokenState.balanceOf(from);
                                }
                        
                                // Reduce the value to transfer if balance is insufficient after reclaimed
                                value = value >= balanceAfter ? balanceAfter : value;
                        
                                return _internalTransferFrom(from, to, value);
                            }
                        
                            /**
                             * @notice _transferToFeeAddress function
                             * non-sUSD synths are exchanged into sUSD via synthInitiatedExchange
                             * notify feePool to record amount as fee paid to feePool */
                            function _transferToFeeAddress(address to, uint value) internal returns (bool) {
                                uint amountInUSD;
                        
                                // sUSD can be transferred to FEE_ADDRESS directly
                                if (currencyKey == "sUSD") {
                                    amountInUSD = value;
                                    super._internalTransfer(messageSender, to, value);
                                } else {
                                    // else exchange synth into sUSD and send to FEE_ADDRESS
                                    amountInUSD = exchanger().exchange(messageSender, currencyKey, value, "sUSD", FEE_ADDRESS);
                                }
                        
                                // Notify feePool to record sUSD to distribute as fees
                                feePool().recordFeePaid(amountInUSD);
                        
                                return true;
                            }
                        
                            // Allow synthetix to issue a certain number of synths from an account.
                            // forward call to _internalIssue
                            function issue(address account, uint amount) external onlyInternalContracts {
                                _internalIssue(account, amount);
                            }
                        
                            // Allow synthetix or another synth contract to burn a certain number of synths from an account.
                            // forward call to _internalBurn
                            function burn(address account, uint amount) external onlyInternalContracts {
                                _internalBurn(account, amount);
                            }
                        
                            function _internalIssue(address account, uint amount) internal {
                                tokenState.setBalanceOf(account, tokenState.balanceOf(account).add(amount));
                                totalSupply = totalSupply.add(amount);
                                emitTransfer(address(0), account, amount);
                                emitIssued(account, amount);
                            }
                        
                            function _internalBurn(address account, uint amount) internal returns (bool) {
                                tokenState.setBalanceOf(account, tokenState.balanceOf(account).sub(amount));
                                totalSupply = totalSupply.sub(amount);
                                emitTransfer(account, address(0), amount);
                                emitBurned(account, amount);
                        
                                return true;
                            }
                        
                            // Allow owner to set the total supply on import.
                            function setTotalSupply(uint amount) external optionalProxy_onlyOwner {
                                totalSupply = amount;
                            }
                        
                            /* ========== VIEWS ========== */
                            function systemStatus() internal view returns (ISystemStatus) {
                                return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS, "Missing SystemStatus address"));
                            }
                        
                            function synthetix() internal view returns (ISynthetix) {
                                return ISynthetix(requireAndGetAddress(CONTRACT_SYNTHETIX, "Missing Synthetix address"));
                            }
                        
                            function feePool() internal view returns (IFeePool) {
                                return IFeePool(requireAndGetAddress(CONTRACT_FEEPOOL, "Missing FeePool address"));
                            }
                        
                            function exchanger() internal view returns (IExchanger) {
                                return IExchanger(requireAndGetAddress(CONTRACT_EXCHANGER, "Missing Exchanger address"));
                            }
                        
                            function issuer() internal view returns (IIssuer) {
                                return IIssuer(requireAndGetAddress(CONTRACT_ISSUER, "Missing Issuer address"));
                            }
                        
                            function _ensureCanTransfer(address from, uint value) internal view {
                                require(exchanger().maxSecsLeftInWaitingPeriod(from, currencyKey) == 0, "Cannot transfer during waiting period");
                                require(transferableSynths(from) >= value, "Insufficient balance after any settlement owing");
                                systemStatus().requireSynthActive(currencyKey);
                            }
                        
                            function transferableSynths(address account) public view returns (uint) {
                                (uint reclaimAmount, , ) = exchanger().settlementOwing(account, currencyKey);
                        
                                // Note: ignoring rebate amount here because a settle() is required in order to
                                // allow the transfer to actually work
                        
                                uint balance = tokenState.balanceOf(account);
                        
                                if (reclaimAmount > balance) {
                                    return 0;
                                } else {
                                    return balance.sub(reclaimAmount);
                                }
                            }
                        
                            /* ========== INTERNAL FUNCTIONS ========== */
                        
                            function _internalTransferFrom(address from, address to, uint value) internal returns (bool) {
                                // Skip allowance update in case of infinite allowance
                                if (tokenState.allowance(from, messageSender) != uint(-1)) {
                                    // Reduce the allowance by the amount we're transferring.
                                    // The safeSub call will handle an insufficient allowance.
                                    tokenState.setAllowance(from, messageSender, tokenState.allowance(from, messageSender).sub(value));
                                }
                        
                                return super._internalTransfer(from, to, value);
                            }
                        
                            /* ========== MODIFIERS ========== */
                        
                            modifier onlyInternalContracts() {
                                bool isSynthetix = msg.sender == address(synthetix());
                                bool isFeePool = msg.sender == address(feePool());
                                bool isExchanger = msg.sender == address(exchanger());
                                bool isIssuer = msg.sender == address(issuer());
                        
                                require(
                                    isSynthetix || isFeePool || isExchanger || isIssuer,
                                    "Only Synthetix, FeePool, Exchanger or Issuer contracts allowed"
                                );
                                _;
                            }
                        
                            /* ========== EVENTS ========== */
                            event Issued(address indexed account, uint value);
                            bytes32 private constant ISSUED_SIG = keccak256("Issued(address,uint256)");
                        
                            function emitIssued(address account, uint value) internal {
                                proxy._emit(abi.encode(value), 2, ISSUED_SIG, bytes32(account), 0, 0);
                            }
                        
                            event Burned(address indexed account, uint value);
                            bytes32 private constant BURNED_SIG = keccak256("Burned(address,uint256)");
                        
                            function emitBurned(address account, uint value) internal {
                                proxy._emit(abi.encode(value), 2, BURNED_SIG, bytes32(account), 0, 0);
                            }
                        }
                        
                        
                            

                        File 6 of 7: TokenState
                        /*
                         * Nomin TokenState Contract
                         *
                         * Stores ERC20 balance and approval information for the
                         * nomin component of the Havven stablecoin system.
                         *
                         * version: nUSDa.1
                         * date: 29 Jun 2018
                         * url: https://github.com/Havven/havven/releases/tag/nUSDa.1
                         */
                         
                         
                        pragma solidity 0.4.24;
                         
                         
                        /*
                        -----------------------------------------------------------------
                        FILE INFORMATION
                        -----------------------------------------------------------------
                         
                        file:       Owned.sol
                        version:    1.1
                        author:     Anton Jurisevic
                                    Dominic Romanowski
                         
                        date:       2018-2-26
                         
                        -----------------------------------------------------------------
                        MODULE DESCRIPTION
                        -----------------------------------------------------------------
                         
                        An Owned contract, to be inherited by other contracts.
                        Requires its owner to be explicitly set in the constructor.
                        Provides an onlyOwner access modifier.
                         
                        To change owner, the current owner must nominate the next owner,
                        who then has to accept the nomination. The nomination can be
                        cancelled before it is accepted by the new owner by having the
                        previous owner change the nomination (setting it to 0).
                         
                        -----------------------------------------------------------------
                        */
                         
                         
                        /**
                         * @title A contract with an owner.
                         * @notice Contract ownership can be transferred by first nominating the new owner,
                         * who must then accept the ownership, which prevents accidental incorrect ownership transfers.
                         */
                        contract Owned {
                            address public owner;
                            address public nominatedOwner;
                         
                            /**
                             * @dev Owned Constructor
                             */
                            constructor(address _owner)
                                public
                            {
                                require(_owner != address(0));
                                owner = _owner;
                                emit OwnerChanged(address(0), _owner);
                            }
                         
                            /**
                             * @notice Nominate a new owner of this contract.
                             * @dev Only the current owner may nominate a new owner.
                             */
                            function nominateNewOwner(address _owner)
                                external
                                onlyOwner
                            {
                                nominatedOwner = _owner;
                                emit OwnerNominated(_owner);
                            }
                         
                            /**
                             * @notice Accept the nomination to be owner.
                             */
                            function acceptOwnership()
                                external
                            {
                                require(msg.sender == nominatedOwner);
                                emit OwnerChanged(owner, nominatedOwner);
                                owner = nominatedOwner;
                                nominatedOwner = address(0);
                            }
                         
                            modifier onlyOwner
                            {
                                require(msg.sender == owner);
                                _;
                            }
                         
                            event OwnerNominated(address newOwner);
                            event OwnerChanged(address oldOwner, address newOwner);
                        }
                         
                         
                        /*
                        -----------------------------------------------------------------
                        FILE INFORMATION
                        -----------------------------------------------------------------
                         
                        file:       State.sol
                        version:    1.1
                        author:     Dominic Romanowski
                                    Anton Jurisevic
                         
                        date:       2018-05-15
                         
                        -----------------------------------------------------------------
                        MODULE DESCRIPTION
                        -----------------------------------------------------------------
                         
                        This contract is used side by side with external state token
                        contracts, such as Havven and Nomin.
                        It provides an easy way to upgrade contract logic while
                        maintaining all user balances and allowances. This is designed
                        to make the changeover as easy as possible, since mappings
                        are not so cheap or straightforward to migrate.
                         
                        The first deployed contract would create this state contract,
                        using it as its store of balances.
                        When a new contract is deployed, it links to the existing
                        state contract, whose owner would then change its associated
                        contract to the new one.
                         
                        -----------------------------------------------------------------
                        */
                         
                         
                        contract State is Owned {
                            // the address of the contract that can modify variables
                            // this can only be changed by the owner of this contract
                            address public associatedContract;
                         
                         
                            constructor(address _owner, address _associatedContract)
                                Owned(_owner)
                                public
                            {
                                associatedContract = _associatedContract;
                                emit AssociatedContractUpdated(_associatedContract);
                            }
                         
                            /* ========== SETTERS ========== */
                         
                            // Change the associated contract to a new address
                            function setAssociatedContract(address _associatedContract)
                                external
                                onlyOwner
                            {
                                associatedContract = _associatedContract;
                                emit AssociatedContractUpdated(_associatedContract);
                            }
                         
                            /* ========== MODIFIERS ========== */
                         
                            modifier onlyAssociatedContract
                            {
                                require(msg.sender == associatedContract);
                                _;
                            }
                         
                            /* ========== EVENTS ========== */
                         
                            event AssociatedContractUpdated(address associatedContract);
                        }
                         
                         
                        /*
                        -----------------------------------------------------------------
                        FILE INFORMATION
                        -----------------------------------------------------------------
                         
                        file:       TokenState.sol
                        version:    1.1
                        author:     Dominic Romanowski
                                    Anton Jurisevic
                         
                        date:       2018-05-15
                         
                        -----------------------------------------------------------------
                        MODULE DESCRIPTION
                        -----------------------------------------------------------------
                         
                        A contract that holds the state of an ERC20 compliant token.
                         
                        This contract is used side by side with external state token
                        contracts, such as Havven and Nomin.
                        It provides an easy way to upgrade contract logic while
                        maintaining all user balances and allowances. This is designed
                        to make the changeover as easy as possible, since mappings
                        are not so cheap or straightforward to migrate.
                         
                        The first deployed contract would create this state contract,
                        using it as its store of balances.
                        When a new contract is deployed, it links to the existing
                        state contract, whose owner would then change its associated
                        contract to the new one.
                         
                        -----------------------------------------------------------------
                        */
                         
                         
                        /**
                         * @title ERC20 Token State
                         * @notice Stores balance information of an ERC20 token contract.
                         */
                        contract TokenState is State {
                         
                            /* ERC20 fields. */
                            mapping(address => uint) public balanceOf;
                            mapping(address => mapping(address => uint)) public allowance;
                         
                            /**
                             * @dev Constructor
                             * @param _owner The address which controls this contract.
                             * @param _associatedContract The ERC20 contract whose state this composes.
                             */
                            constructor(address _owner, address _associatedContract)
                                State(_owner, _associatedContract)
                                public
                            {}
                         
                            /* ========== SETTERS ========== */
                         
                            /**
                             * @notice Set ERC20 allowance.
                             * @dev Only the associated contract may call this.
                             * @param tokenOwner The authorising party.
                             * @param spender The authorised party.
                             * @param value The total value the authorised party may spend on the
                             * authorising party's behalf.
                             */
                            function setAllowance(address tokenOwner, address spender, uint value)
                                external
                                onlyAssociatedContract
                            {
                                allowance[tokenOwner][spender] = value;
                            }
                         
                            /**
                             * @notice Set the balance in a given account
                             * @dev Only the associated contract may call this.
                             * @param account The account whose value to set.
                             * @param value The new balance of the given account.
                             */
                            function setBalanceOf(address account, uint value)
                                external
                                onlyAssociatedContract
                            {
                                balanceOf[account] = value;
                            }
                        }

                        File 7 of 7: TokenPriceProvider
                        pragma solidity ^0.5.4;
                        
                        /**
                         * ERC20 contract interface.
                         */
                        contract ERC20 {
                            function totalSupply() public view returns (uint);
                            function decimals() public view returns (uint);
                            function balanceOf(address tokenOwner) public view returns (uint balance);
                            function allowance(address tokenOwner, address spender) public view returns (uint remaining);
                            function transfer(address to, uint tokens) public returns (bool success);
                            function approve(address spender, uint tokens) public returns (bool success);
                            function transferFrom(address from, address to, uint tokens) public returns (bool success);
                        }
                        
                        contract KyberNetwork {
                        
                            function getExpectedRate(
                                ERC20 src,
                                ERC20 dest,
                                uint srcQty
                            )
                                public
                                view
                                returns (uint expectedRate, uint slippageRate);
                        
                            function trade(
                                ERC20 src,
                                uint srcAmount,
                                ERC20 dest,
                                address payable destAddress,
                                uint maxDestAmount,
                                uint minConversionRate,
                                address walletId
                            )
                                public
                                payable
                                returns(uint);
                        }
                        
                        /**
                         * @title SafeMath
                         * @dev Math operations with safety checks that throw on error
                         */
                        library SafeMath {
                        
                            /**
                            * @dev Multiplies two numbers, reverts on overflow.
                            */
                            function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                                // benefit is lost if 'b' is also tested.
                                // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                                if (a == 0) {
                                    return 0;
                                }
                        
                                uint256 c = a * b;
                                require(c / a == b);
                        
                                return c;
                            }
                        
                            /**
                            * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
                            */
                            function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                require(b > 0); // Solidity only automatically asserts when dividing by 0
                                uint256 c = a / b;
                                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                        
                                return c;
                            }
                        
                            /**
                            * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                            */
                            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                require(b <= a);
                                uint256 c = a - b;
                        
                                return c;
                            }
                        
                            /**
                            * @dev Adds two numbers, reverts on overflow.
                            */
                            function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                uint256 c = a + b;
                                require(c >= a);
                        
                                return c;
                            }
                        
                            /**
                            * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
                            * reverts when dividing by zero.
                            */
                            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                require(b != 0);
                                return a % b;
                            }
                        
                            /**
                            * @dev Returns ceil(a / b).
                            */
                            function ceil(uint256 a, uint256 b) internal pure returns (uint256) {
                                uint256 c = a / b;
                                if(a % b == 0) {
                                    return c;
                                }
                                else {
                                    return c + 1;
                                }
                            }
                        }
                        
                        /**
                         * @title Owned
                         * @dev Basic contract to define an owner.
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract Owned {
                        
                            // The owner
                            address public owner;
                        
                            event OwnerChanged(address indexed _newOwner);
                        
                            /**
                             * @dev Throws if the sender is not the owner.
                             */
                            modifier onlyOwner {
                                require(msg.sender == owner, "Must be owner");
                                _;
                            }
                        
                            constructor() public {
                                owner = msg.sender;
                            }
                        
                            /**
                             * @dev Lets the owner transfer ownership of the contract to a new owner.
                             * @param _newOwner The new owner.
                             */
                            function changeOwner(address _newOwner) external onlyOwner {
                                require(_newOwner != address(0), "Address must not be null");
                                owner = _newOwner;
                                emit OwnerChanged(_newOwner);
                            }
                        }
                        
                        /**
                         * @title Managed
                         * @dev Basic contract that defines a set of managers. Only the owner can add/remove managers.
                         * @author Julien Niset - <julien@argent.im>
                         */
                        contract Managed is Owned {
                        
                            // The managers
                            mapping (address => bool) public managers;
                        
                            /**
                             * @dev Throws if the sender is not a manager.
                             */
                            modifier onlyManager {
                                require(managers[msg.sender] == true, "M: Must be manager");
                                _;
                            }
                        
                            event ManagerAdded(address indexed _manager);
                            event ManagerRevoked(address indexed _manager);
                        
                            /**
                            * @dev Adds a manager. 
                            * @param _manager The address of the manager.
                            */
                            function addManager(address _manager) external onlyOwner {
                                require(_manager != address(0), "M: Address must not be null");
                                if(managers[_manager] == false) {
                                    managers[_manager] = true;
                                    emit ManagerAdded(_manager);
                                }        
                            }
                        
                            /**
                            * @dev Revokes a manager.
                            * @param _manager The address of the manager.
                            */
                            function revokeManager(address _manager) external onlyOwner {
                                require(managers[_manager] == true, "M: Target must be an existing manager");
                                delete managers[_manager];
                                emit ManagerRevoked(_manager);
                            }
                        }
                        
                        contract TokenPriceProvider is Managed {
                        
                            // Mock token address for ETH
                            address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                        
                            using SafeMath for uint256;
                        
                            mapping(address => uint256) public cachedPrices;
                        
                            // Address of the KyberNetwork contract
                            KyberNetwork public kyberNetwork;
                        
                            constructor(KyberNetwork _kyberNetwork) public {
                                kyberNetwork = _kyberNetwork;
                            }
                        
                            function setPrice(ERC20 _token, uint256 _price) public onlyManager {
                                cachedPrices[address(_token)] = _price;
                            }
                        
                            function setPriceForTokenList(ERC20[] calldata _tokens, uint256[] calldata _prices) external onlyManager {
                                for(uint16 i = 0; i < _tokens.length; i++) {
                                    setPrice(_tokens[i], _prices[i]);
                                }
                            }
                        
                            /**
                             * @dev Converts the value of _amount tokens in ether.
                             * @param _amount the amount of tokens to convert (in 'token wei' twei)
                             * @param _token the ERC20 token contract
                             * @return the ether value (in wei) of _amount tokens with contract _token
                             */
                            function getEtherValue(uint256 _amount, address _token) external view returns (uint256) {
                                uint256 decimals = ERC20(_token).decimals();
                                uint256 price = cachedPrices[_token];
                                return price.mul(_amount).div(10**decimals);
                            }
                        
                            //
                            // The following is added to be backward-compatible with Argent's old backend
                            //
                        
                            function setKyberNetwork(KyberNetwork _kyberNetwork) external onlyManager {
                                kyberNetwork = _kyberNetwork;
                            }
                        
                            function syncPrice(ERC20 _token) external {
                                require(address(kyberNetwork) != address(0), "Kyber sync is disabled");
                                (uint256 expectedRate,) = kyberNetwork.getExpectedRate(_token, ERC20(ETH_TOKEN_ADDRESS), 10000);
                                cachedPrices[address(_token)] = expectedRate;
                            }
                        
                            function syncPriceForTokenList(ERC20[] calldata _tokens) external {
                                require(address(kyberNetwork) != address(0), "Kyber sync is disabled");
                                for(uint16 i = 0; i < _tokens.length; i++) {
                                    (uint256 expectedRate,) = kyberNetwork.getExpectedRate(_tokens[i], ERC20(ETH_TOKEN_ADDRESS), 10000);
                                    cachedPrices[address(_tokens[i])] = expectedRate;
                                }
                            }
                        }