ETH Price: $2,109.33 (+1.48%)
Gas: 0.04 Gwei

Transaction Decoder

Block:
11395334 at Dec-05-2020 10:28:59 PM +UTC
Transaction Fee:
0.00418788405 ETH $8.83
Gas Used:
167,181 Gas / 25.05 Gwei

Emitted Events:

315 0xa64915bbdfbe8e4801624ae0b60ff49ca5e13a50.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x000000000000000000000000645ba45dbe3c6942c812a46f9ee8115c89b524ec, 0x0000000000000000000000005c703f5131bb3268e0dc32913a3c0994eda215fe, 0x00000000000000000000000000000000000000000000000006f05b59d3b20000, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000000 )
316 TransferManager.Transfer( wallet=0xa64915bbdfbe8e4801624ae0b60ff49ca5e13a50, token=0xEeeeeEee...eeeeeEEeE, amount=500000000000000000, to=0x5c703f5131bb3268e0dc32913a3c0994eda215fe, data=0x )
317 Proxy.Received( value=1750000000000000, sender=0xa64915bbdfbe8e4801624ae0b60ff49ca5e13a50, data=0x )
318 0xa64915bbdfbe8e4801624ae0b60ff49ca5e13a50.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x000000000000000000000000645ba45dbe3c6942c812a46f9ee8115c89b524ec, 0x000000000000000000000000de57844f758a0a6a1910a4787ab2f7121c8978c3, 0x0000000000000000000000000000000000000000000000000006379da05b6000, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000000 )
319 RelayerManager.Refund( wallet=0xa64915bbdfbe8e4801624ae0b60ff49ca5e13a50, refundAddress=Proxy, refundToken=0xEeeeeEee...eeeeeEEeE, refundAmount=1750000000000000 )
320 RelayerManager.TransactionExecuted( wallet=0xa64915bbdfbe8e4801624ae0b60ff49ca5e13a50, success=True, returnData=0x, signedHash=3F181A51AF82313927A1CBE72D9506D21D534655D25053A3C23CC3A20D84D422 )

Execution Trace

RelayerManager.execute( _wallet=0xA64915BBdFBe8e4801624AE0b60Ff49cA5e13A50, _feature=0x5094a8f54B12AEc540bF7cCd0Dd7B62f4FecF7f2, _data=0x2DF546F4000000000000000000000000A64915BBDFBE8E4801624AE0B60FF49CA5E13A50000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE0000000000000000000000005C703F5131BB3268E0DC32913A3C0994EDA215FE00000000000000000000000000000000000000000000000006F05B59D3B2000000000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000000, _nonce=3877630885092278463673486955429273549962742206, _signatures=0xA9BA75573EE4C2433DB815935FC60D356168E420D4BB3FD99BE578B184E97C3F79E7A9FBBF645D76BB9C33A333630A71AE15AB94719BA0A1332845596B02B6B11B, _gasPrice=25000000000, _gasLimit=70000, _refundToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, _refundAddress=0xdE57844F758A0A6a1910a4787AB2F7121c8978c3 ) => ( True )
  • VersionManager.isFeatureAuthorised( _wallet=0xA64915BBdFBe8e4801624AE0b60Ff49cA5e13A50, _feature=0x5094a8f54B12AEc540bF7cCd0Dd7B62f4FecF7f2 ) => ( True )
  • TransferManager.getRequiredSignatures( 0xA64915BBdFBe8e4801624AE0b60Ff49cA5e13A50, 0x2DF546F4000000000000000000000000A64915BBDFBE8E4801624AE0B60FF49CA5E13A50000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE0000000000000000000000005C703F5131BB3268E0DC32913A3C0994EDA215FE00000000000000000000000000000000000000000000000006F05B59D3B2000000000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000000 ) => ( 1, 1 )
  • Null: 0x000...001.3f181a51( )
  • 0xa64915bbdfbe8e4801624ae0b60ff49ca5e13a50.STATICCALL( )
  • TransferManager.transferToken( _wallet=0xA64915BBdFBe8e4801624AE0b60Ff49cA5e13A50, _token=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, _to=0x5c703F5131bB3268E0dC32913a3c0994eda215Fe, _amount=500000000000000000, _data=0x )
    • VersionManager.isFeatureAuthorised( _wallet=0xA64915BBdFBe8e4801624AE0b60Ff49cA5e13A50, _feature=0x10A0847c2D170008dDCa7C3a688124f493630032 ) => ( True )
    • LockStorage.isLocked( _wallet=0xA64915BBdFBe8e4801624AE0b60Ff49cA5e13A50 ) => ( False )
    • 0x391f0e86da951c03b1183c60b195090671adea88.13f4a0ea( )
    • VersionManager.checkAuthorisedFeatureAndInvokeWallet( _wallet=0xA64915BBdFBe8e4801624AE0b60Ff49cA5e13A50, _to=0x5c703F5131bB3268E0dC32913a3c0994eda215Fe, _value=500000000000000000, _data=0x ) => ( _res=0x )
      • 0xa64915bbdfbe8e4801624ae0b60ff49ca5e13a50.8f6f0332( )
        • BaseWallet.invoke( _target=0x5c703F5131bB3268E0dC32913a3c0994eda215Fe, _value=500000000000000000, _data=0x ) => ( _result=0x )
          • ETH 0.5 0x5c703f5131bb3268e0dc32913a3c0994eda215fe.CALL( )
          • 0x045b32efa0d97a681cc415f1b37c972ad7299a55.13565b2c( )
          • VersionManager.invokeStorage( _wallet=0xA64915BBdFBe8e4801624AE0b60Ff49cA5e13A50, _storage=0x045B32efA0D97a681Cc415f1B37C972Ad7299a55, _data=0x5AE5BC52000000000000000000000000A64915BBDFBE8E4801624AE0B60FF49CA5E13A500000000000000000000000000000000000000000000000000006379DA05B6000000000000000000000000000000000000000000000000000000000005FCD5B2B )
            • 0x045b32efa0d97a681cc415f1b37c972ad7299a55.5ae5bc52( )
              • 0xa64915bbdfbe8e4801624ae0b60ff49ca5e13a50.d6eb1bbf( )
                • BaseWallet.authorised( 0x645BA45dBe3c6942c812A46f9EE8115C89B524EC ) => ( True )
                • VersionManager.checkAuthorisedFeatureAndInvokeWallet( _wallet=0xA64915BBdFBe8e4801624AE0b60Ff49cA5e13A50, _to=0xdE57844F758A0A6a1910a4787AB2F7121c8978c3, _value=1750000000000000, _data=0x ) => ( _res=0x )
                  • 0xa64915bbdfbe8e4801624ae0b60ff49ca5e13a50.8f6f0332( )
                    • BaseWallet.invoke( _target=0xdE57844F758A0A6a1910a4787AB2F7121c8978c3, _value=1750000000000000, _data=0x ) => ( _result=0x )
                      • ETH 0.00175 Proxy.CALL( )
                        execute[RelayerManager (ln:1022)]
                        File 1 of 6: RelayerManager
                        pragma experimental ABIEncoderV2;
                        // File: contracts/modules/common/Utils.sol
                        // Copyright (C) 2020  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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        /**
                         * @title Utils
                         * @notice Common utility methods used by modules.
                         */
                        library Utils {
                            /**
                            * @notice Helper method to recover 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
                                // solhint-disable-next-line 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);
                                address recoveredAddress = ecrecover(_signedHash, v, r, s);
                                require(recoveredAddress != address(0), "Utils: ecrecover returned 0");
                                return recoveredAddress;
                            }
                            /**
                            * @notice Helper method to parse data and extract the method signature.
                            */
                            function functionPrefix(bytes memory _data) internal pure returns (bytes4 prefix) {
                                require(_data.length >= 4, "RM: Invalid functionPrefix");
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    prefix := mload(add(_data, 0x20))
                                }
                            }
                            /**
                            * @notice 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;
                                }
                            }
                            function min(uint256 a, uint256 b) internal pure returns (uint256) {
                                if (a < b) {
                                    return a;
                                }
                                return b;
                            }
                        }
                        // File: @openzeppelin/contracts/math/SafeMath.sol
                        /**
                         * @dev Wrappers over Solidity's arithmetic operations with added overflow
                         * checks.
                         *
                         * Arithmetic operations in Solidity wrap on overflow. This can easily result
                         * in bugs, because programmers usually assume that an overflow raises an
                         * error, which is the standard behavior in high level programming languages.
                         * `SafeMath` restores this intuition by reverting the transaction when an
                         * operation overflows.
                         *
                         * Using this library instead of the unchecked operations eliminates an entire
                         * class of bugs, so it's recommended to use it always.
                         */
                        library SafeMath {
                            /**
                             * @dev Returns the addition of two unsigned integers, reverting on
                             * overflow.
                             *
                             * Counterpart to Solidity's `+` operator.
                             *
                             * Requirements:
                             * - Addition cannot overflow.
                             */
                            function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                uint256 c = a + b;
                                require(c >= a, "SafeMath: addition overflow");
                                return c;
                            }
                            /**
                             * @dev Returns the subtraction of two unsigned integers, reverting on
                             * overflow (when the result is negative).
                             *
                             * Counterpart to Solidity's `-` operator.
                             *
                             * Requirements:
                             * - Subtraction cannot overflow.
                             */
                            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                return sub(a, b, "SafeMath: subtraction overflow");
                            }
                            /**
                             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                             * overflow (when the result is negative).
                             *
                             * Counterpart to Solidity's `-` operator.
                             *
                             * Requirements:
                             * - Subtraction cannot overflow.
                             */
                            function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                require(b <= a, errorMessage);
                                uint256 c = a - b;
                                return c;
                            }
                            /**
                             * @dev Returns the multiplication of two unsigned integers, reverting on
                             * overflow.
                             *
                             * Counterpart to Solidity's `*` operator.
                             *
                             * Requirements:
                             * - Multiplication cannot 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-contracts/pull/522
                                if (a == 0) {
                                    return 0;
                                }
                                uint256 c = a * b;
                                require(c / a == b, "SafeMath: multiplication overflow");
                                return c;
                            }
                            /**
                             * @dev Returns the integer division of two unsigned integers. Reverts on
                             * division by zero. The result is rounded towards zero.
                             *
                             * Counterpart to Solidity's `/` operator. Note: this function uses a
                             * `revert` opcode (which leaves remaining gas untouched) while Solidity
                             * uses an invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                return div(a, b, "SafeMath: division by zero");
                            }
                            /**
                             * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                             * division by zero. The result is rounded towards zero.
                             *
                             * Counterpart to Solidity's `/` operator. Note: this function uses a
                             * `revert` opcode (which leaves remaining gas untouched) while Solidity
                             * uses an invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                // Solidity only automatically asserts when dividing by 0
                                require(b > 0, errorMessage);
                                uint256 c = a / b;
                                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                                return c;
                            }
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                             * Reverts when dividing by zero.
                             *
                             * Counterpart to Solidity's `%` operator. This function uses a `revert`
                             * opcode (which leaves remaining gas untouched) while Solidity uses an
                             * invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                return mod(a, b, "SafeMath: modulo by zero");
                            }
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                             * Reverts with custom message when dividing by zero.
                             *
                             * Counterpart to Solidity's `%` operator. This function uses a `revert`
                             * opcode (which leaves remaining gas untouched) while Solidity uses an
                             * invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                require(b != 0, errorMessage);
                                return a % b;
                            }
                        }
                        // File: contracts/wallet/IWallet.sol
                        // 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 <0.7.0;
                        /**
                         * @title IWallet
                         * @notice Interface for the BaseWallet
                         */
                        interface IWallet {
                            /**
                             * @notice Returns the wallet owner.
                             * @return The wallet owner address.
                             */
                            function owner() external view returns (address);
                            /**
                             * @notice Returns the number of authorised modules.
                             * @return The number of authorised modules.
                             */
                            function modules() external view returns (uint);
                            /**
                             * @notice Sets a new owner for the wallet.
                             * @param _newOwner The new owner.
                             */
                            function setOwner(address _newOwner) external;
                            /**
                             * @notice Checks if a module is authorised on the wallet.
                             * @param _module The module address to check.
                             * @return `true` if the module is authorised, otherwise `false`.
                             */
                            function authorised(address _module) external view returns (bool);
                            /**
                             * @notice Returns the module responsible for a static call redirection.
                             * @param _sig The signature of the static call.
                             * @return the module doing the redirection
                             */
                            function enabled(bytes4 _sig) external view returns (address);
                            /**
                             * @notice 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;
                            /**
                            * @notice 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;
                        }
                        // File: contracts/infrastructure/IModuleRegistry.sol
                        // Copyright (C) 2020  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 <0.7.0;
                        /**
                         * @title IModuleRegistry
                         * @notice Interface for the registry of authorised modules.
                         */
                        interface IModuleRegistry {
                            function registerModule(address _module, bytes32 _name) external;
                            function deregisterModule(address _module) external;
                            function registerUpgrader(address _upgrader, bytes32 _name) external;
                            function deregisterUpgrader(address _upgrader) external;
                            function recoverToken(address _token) external;
                            function moduleInfo(address _module) external view returns (bytes32);
                            function upgraderInfo(address _upgrader) external view returns (bytes32);
                            function isRegisteredModule(address _module) external view returns (bool);
                            function isRegisteredModule(address[] calldata _modules) external view returns (bool);
                            function isRegisteredUpgrader(address _upgrader) external view returns (bool);
                        }
                        // File: contracts/infrastructure/storage/ILockStorage.sol
                        // 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 <0.7.0;
                        interface ILockStorage {
                            function isLocked(address _wallet) external view returns (bool);
                            function getLock(address _wallet) external view returns (uint256);
                            function getLocker(address _wallet) external view returns (address);
                            function setLock(address _wallet, address _locker, uint256 _releaseAfter) external;
                        }
                        // File: contracts/modules/common/IFeature.sol
                        // 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 <0.7.0;
                        /**
                         * @title IFeature
                         * @notice Interface for a Feature.
                         * @author Julien Niset - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                         */
                        interface IFeature {
                            enum OwnerSignature {
                                Anyone,             // Anyone
                                Required,           // Owner required
                                Optional,           // Owner and/or guardians
                                Disallowed          // guardians only
                            }
                            /**
                            * @notice Utility method to recover any ERC20 token that was sent to the Feature by mistake.
                            * @param _token The token to recover.
                            */
                            function recoverToken(address _token) external;
                            /**
                             * @notice Inits a Feature for a wallet by e.g. setting some wallet specific parameters in storage.
                             * @param _wallet The wallet.
                             */
                            function init(address _wallet) external;
                            /**
                             * @notice Helper method to check if an address is an authorised feature of a target wallet.
                             * @param _wallet The target wallet.
                             * @param _feature The address.
                             */
                            function isFeatureAuthorisedInVersionManager(address _wallet, address _feature) external view returns (bool);
                            /**
                            * @notice 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 and the wallet owner signature requirement.
                            */
                            function getRequiredSignatures(address _wallet, bytes calldata _data) external view returns (uint256, OwnerSignature);
                            /**
                            * @notice Gets the list of static call signatures that this feature responds to on behalf of wallets
                            */
                            function getStaticCallSignatures() external view returns (bytes4[] memory);
                        }
                        // File: lib/other/ERC20.sol
                        pragma solidity >=0.5.4 <0.7.0;
                        /**
                         * ERC20 contract interface.
                         */
                        interface ERC20 {
                            function totalSupply() external view returns (uint);
                            function decimals() external view returns (uint);
                            function balanceOf(address tokenOwner) external view returns (uint balance);
                            function allowance(address tokenOwner, address spender) external view returns (uint remaining);
                            function transfer(address to, uint tokens) external returns (bool success);
                            function approve(address spender, uint tokens) external returns (bool success);
                            function transferFrom(address from, address to, uint tokens) external returns (bool success);
                        }
                        // File: contracts/infrastructure/storage/ILimitStorage.sol
                        // 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 ILimitStorage
                         * @notice LimitStorage interface
                         */
                        interface ILimitStorage {
                            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;
                            }
                            function setLimit(address _wallet, Limit memory _limit) external;
                            function getLimit(address _wallet) external view returns (Limit memory _limit);
                            function setDailySpent(address _wallet, DailySpent memory _dailySpent) external;
                            function getDailySpent(address _wallet) external view returns (DailySpent memory _dailySpent);
                            function setLimitAndDailySpent(address _wallet, Limit memory _limit, DailySpent memory _dailySpent) external;
                            function getLimitAndDailySpent(address _wallet) external view returns (Limit memory _limit, DailySpent memory _dailySpent);
                        }
                        // File: contracts/modules/common/IVersionManager.sol
                        // 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 <0.7.0;
                        /**
                         * @title IVersionManager
                         * @notice Interface for the VersionManager module.
                         * @author Olivier VDB - <olivier@argent.xyz>
                         */
                        interface IVersionManager {
                            /**
                             * @notice Returns true if the feature is authorised for the wallet
                             * @param _wallet The target wallet.
                             * @param _feature The feature.
                             */
                            function isFeatureAuthorised(address _wallet, address _feature) external view returns (bool);
                            /**
                             * @notice Lets a feature (caller) 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 checkAuthorisedFeatureAndInvokeWallet(
                                address _wallet,
                                address _to,
                                uint256 _value,
                                bytes calldata _data
                            ) external returns (bytes memory _res);
                            /* ******* Backward Compatibility with old Storages and BaseWallet *************** */
                            /**
                             * @notice Sets a new owner for the wallet.
                             * @param _newOwner The new owner.
                             */
                            function setOwner(address _wallet, address _newOwner) external;
                            /**
                             * @notice Lets a feature write data to a storage contract.
                             * @param _wallet The target wallet.
                             * @param _storage The storage contract.
                             * @param _data The data of the call
                             */
                            function invokeStorage(address _wallet, address _storage, bytes calldata _data) external;
                            /**
                             * @notice Upgrade a wallet to a new version.
                             * @param _wallet the wallet to upgrade
                             * @param _toVersion the new version
                             */
                            function upgradeWallet(address _wallet, uint256 _toVersion) external;
                        }
                        // File: contracts/modules/common/BaseFeature.sol
                        // 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.s
                        // 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 BaseFeature
                         * @notice Base Feature contract that contains methods common to all Feature contracts.
                         * @author Julien Niset - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                         */
                        contract BaseFeature is IFeature {
                            // Empty calldata
                            bytes constant internal EMPTY_BYTES = "";
                            // Mock token address for ETH
                            address constant internal ETH_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                            // The address of the Lock storage
                            ILockStorage internal lockStorage;
                            // The address of the Version Manager
                            IVersionManager internal versionManager;
                            event FeatureCreated(bytes32 name);
                            /**
                             * @notice Throws if the wallet is locked.
                             */
                            modifier onlyWhenUnlocked(address _wallet) {
                                require(!lockStorage.isLocked(_wallet), "BF: wallet locked");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not the VersionManager.
                             */
                            modifier onlyVersionManager() {
                                require(msg.sender == address(versionManager), "BF: caller must be VersionManager");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not the owner of the target wallet.
                             */
                            modifier onlyWalletOwner(address _wallet) {
                                require(isOwner(_wallet, msg.sender), "BF: must be wallet owner");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not an authorised feature of the target wallet.
                             */
                            modifier onlyWalletFeature(address _wallet) {
                                require(versionManager.isFeatureAuthorised(_wallet, msg.sender), "BF: must be a wallet feature");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not the owner of the target wallet or the feature itself.
                             */
                            modifier onlyWalletOwnerOrFeature(address _wallet) {
                                // Wrapping in an internal method reduces deployment cost by avoiding duplication of inlined code
                                verifyOwnerOrAuthorisedFeature(_wallet, msg.sender);
                                _;
                            }
                            constructor(
                                ILockStorage _lockStorage,
                                IVersionManager _versionManager,
                                bytes32 _name
                            ) public {
                                lockStorage = _lockStorage;
                                versionManager = _versionManager;
                                emit FeatureCreated(_name);
                            }
                            /**
                            * @inheritdoc IFeature
                            */
                            function recoverToken(address _token) external virtual override {
                                uint total = ERC20(_token).balanceOf(address(this));
                                _token.call(abi.encodeWithSelector(ERC20(_token).transfer.selector, address(versionManager), total));
                            }
                            /**
                             * @notice Inits the feature for a wallet by doing nothing.
                             * @dev !! Overriding methods need make sure `init()` can only be called by the VersionManager !!
                             * @param _wallet The wallet.
                             */
                            function init(address _wallet) external virtual override  {}
                            /**
                             * @inheritdoc IFeature
                             */
                            function getRequiredSignatures(address, bytes calldata) external virtual view override returns (uint256, OwnerSignature) {
                                revert("BF: disabled method");
                            }
                            /**
                             * @inheritdoc IFeature
                             */
                            function getStaticCallSignatures() external virtual override view returns (bytes4[] memory _sigs) {}
                            /**
                             * @inheritdoc IFeature
                             */
                            function isFeatureAuthorisedInVersionManager(address _wallet, address _feature) public override view returns (bool) {
                                return versionManager.isFeatureAuthorised(_wallet, _feature);
                            }
                            /**
                            * @notice Checks that the wallet address provided as the first parameter of _data matches _wallet
                            * @return false if the addresses are different.
                            */
                            function verifyData(address _wallet, bytes calldata _data) internal pure returns (bool) {
                                require(_data.length >= 36, "RM: Invalid dataWallet");
                                address dataWallet = abi.decode(_data[4:], (address));
                                return dataWallet == _wallet;
                            }
                             /**
                             * @notice 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(address _wallet, address _addr) internal view returns (bool) {
                                return IWallet(_wallet).owner() == _addr;
                            }
                            /**
                             * @notice Verify that the caller is an authorised feature or the wallet owner.
                             * @param _wallet The target wallet.
                             * @param _sender The caller.
                             */
                            function verifyOwnerOrAuthorisedFeature(address _wallet, address _sender) internal view {
                                require(isFeatureAuthorisedInVersionManager(_wallet, _sender) || isOwner(_wallet, _sender), "BF: must be owner or feature");
                            }
                            /**
                             * @notice 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) 
                            {
                                _res = versionManager.checkAuthorisedFeatureAndInvokeWallet(_wallet, _to, _value, _data);
                            }
                        }
                        // File: contracts/modules/common/GuardianUtils.sol
                        // 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 GuardianUtils
                         * @notice Bundles guardian read logic.
                         */
                        library GuardianUtils {
                            /**
                            * @notice Checks if an address is a guardian or an account authorised to sign on behalf of a smart-contract guardian
                            * given a list of guardians.
                            * @param _guardians the list of guardians
                            * @param _guardian the address to test
                            * @return true and the list of guardians minus the found guardian upon success, false and the original list of guardians if not found.
                            */
                            function isGuardianOrGuardianSigner(address[] memory _guardians, address _guardian) internal view returns (bool, address[] memory) {
                                if (_guardians.length == 0 || _guardian == address(0)) {
                                    return (false, _guardians);
                                }
                                bool isFound = false;
                                address[] memory updatedGuardians = new address[](_guardians.length - 1);
                                uint256 index = 0;
                                for (uint256 i = 0; i < _guardians.length; i++) {
                                    if (!isFound) {
                                        // check if _guardian is an account guardian
                                        if (_guardian == _guardians[i]) {
                                            isFound = true;
                                            continue;
                                        }
                                        // check if _guardian is the owner of a smart contract guardian
                                        if (isContract(_guardians[i]) && isGuardianOwner(_guardians[i], _guardian)) {
                                            isFound = true;
                                            continue;
                                        }
                                    }
                                    if (index < updatedGuardians.length) {
                                        updatedGuardians[index] = _guardians[i];
                                        index++;
                                    }
                                }
                                return isFound ? (true, updatedGuardians) : (false, _guardians);
                            }
                           /**
                            * @notice Checks if an address is a contract.
                            * @param _addr The address.
                            */
                            function isContract(address _addr) internal view returns (bool) {
                                uint32 size;
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    size := extcodesize(_addr)
                                }
                                return (size > 0);
                            }
                            /**
                            * @notice Checks if an address is the owner of a guardian contract.
                            * The method does not revert if the call to the owner() method consumes more then 5000 gas.
                            * @param _guardian The guardian contract
                            * @param _owner The owner to verify.
                            */
                            function isGuardianOwner(address _guardian, address _owner) internal view returns (bool) {
                                address owner = address(0);
                                bytes4 sig = bytes4(keccak256("owner()"));
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    let ptr := mload(0x40)
                                    mstore(ptr,sig)
                                    let result := staticcall(5000, _guardian, ptr, 0x20, ptr, 0x20)
                                    if eq(result, 1) {
                                        owner := mload(ptr)
                                    }
                                }
                                return owner == _owner;
                            }
                        }
                        // File: contracts/infrastructure/ITokenPriceRegistry.sol
                        // 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 ITokenPriceRegistry
                         * @notice TokenPriceRegistry interface
                         */
                        interface ITokenPriceRegistry {
                            function getTokenPrice(address _token) external view returns (uint184 _price);
                            function isTokenTradable(address _token) external view returns (bool _isTradable);
                        }
                        // File: contracts/modules/common/LimitUtils.sol
                        // 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 LimitUtils
                         * @notice Helper library to manage the daily limit and interact with a contract implementing the ILimitStorage interface.
                         * @author Julien Niset - <julien@argent.xyz>
                         */
                        library LimitUtils {
                            // large limit when the limit can be considered disabled
                            uint128 constant internal LIMIT_DISABLED = uint128(-1);
                            using SafeMath for uint256;
                            // *************** Internal Functions ********************* //
                            /**
                             * @notice Changes the daily limit (expressed in ETH).
                             * Decreasing the limit is immediate while increasing the limit is pending for the security period.
                             * @param _lStorage The storage contract.
                             * @param _versionManager The version manager.
                             * @param _wallet The target wallet.
                             * @param _targetLimit The target limit.
                             * @param _securityPeriod The security period.
                             */
                            function changeLimit(
                                ILimitStorage _lStorage,
                                IVersionManager _versionManager,
                                address _wallet,
                                uint256 _targetLimit,
                                uint256 _securityPeriod
                            )
                                internal
                                returns (ILimitStorage.Limit memory)
                            {
                                ILimitStorage.Limit memory limit = _lStorage.getLimit(_wallet);
                                uint256 currentLimit = currentLimit(limit);
                                ILimitStorage.Limit memory newLimit;
                                if (_targetLimit <= currentLimit) {
                                    uint128 targetLimit = safe128(_targetLimit);
                                    newLimit = ILimitStorage.Limit(targetLimit, targetLimit, safe64(block.timestamp));
                                } else {
                                    newLimit = ILimitStorage.Limit(safe128(currentLimit), safe128(_targetLimit), safe64(block.timestamp.add(_securityPeriod)));
                                }
                                setLimit(_versionManager, _lStorage, _wallet, newLimit);
                                return newLimit;
                            }
                             /**
                             * @notice Disable the daily limit.
                             * The change is pending for the security period.
                             * @param _lStorage The storage contract.
                             * @param _versionManager The version manager.
                             * @param _wallet The target wallet.
                             * @param _securityPeriod The security period.
                             */
                            function disableLimit(
                                ILimitStorage _lStorage,
                                IVersionManager _versionManager,
                                address _wallet,
                                uint256 _securityPeriod
                            )
                                internal
                            {
                                changeLimit(_lStorage, _versionManager, _wallet, LIMIT_DISABLED, _securityPeriod);
                            }
                            /**
                            * @notice Returns whether the daily limit is disabled for a wallet.
                            * @param _wallet The target wallet.
                            * @return _limitDisabled true if the daily limit is disabled, false otherwise.
                            */
                            function isLimitDisabled(ILimitStorage _lStorage, address _wallet) internal view returns (bool) {
                                ILimitStorage.Limit memory limit = _lStorage.getLimit(_wallet);
                                uint256 currentLimit = currentLimit(limit);
                                return (currentLimit == LIMIT_DISABLED);
                            }
                            /**
                            * @notice Checks if a transfer is within the limit. If yes the daily spent is updated.
                            * @param _lStorage The storage contract.
                            * @param _versionManager The Version Manager.
                            * @param _wallet The target wallet.
                            * @param _amount The amount for the transfer
                            * @return true if the transfer is withing the daily limit.
                            */
                            function checkAndUpdateDailySpent(
                                ILimitStorage _lStorage,
                                IVersionManager _versionManager,
                                address _wallet,
                                uint256 _amount
                            )
                                internal
                                returns (bool)
                            {
                                (ILimitStorage.Limit memory limit, ILimitStorage.DailySpent memory dailySpent) = _lStorage.getLimitAndDailySpent(_wallet);
                                uint256 currentLimit = currentLimit(limit);
                                if (_amount == 0 || currentLimit == LIMIT_DISABLED) {
                                    return true;
                                }
                                ILimitStorage.DailySpent memory newDailySpent;
                                if (dailySpent.periodEnd <= block.timestamp && _amount <= currentLimit) {
                                    newDailySpent = ILimitStorage.DailySpent(safe128(_amount), safe64(block.timestamp + 24 hours));
                                    setDailySpent(_versionManager, _lStorage, _wallet, newDailySpent);
                                    return true;
                                } else if (dailySpent.periodEnd > block.timestamp && _amount.add(dailySpent.alreadySpent) <= currentLimit) {
                                    newDailySpent = ILimitStorage.DailySpent(safe128(_amount.add(dailySpent.alreadySpent)), safe64(dailySpent.periodEnd));
                                    setDailySpent(_versionManager, _lStorage, _wallet, newDailySpent);
                                    return true;
                                }
                                return false;
                            }
                            /**
                            * @notice Helper method to Reset the daily consumption.
                            * @param _versionManager The Version Manager.
                            * @param _wallet The target wallet.
                            */
                            function resetDailySpent(IVersionManager _versionManager, ILimitStorage limitStorage, address _wallet) internal {
                                setDailySpent(_versionManager, limitStorage, _wallet, ILimitStorage.DailySpent(uint128(0), uint64(0)));
                            }
                            /**
                            * @notice Helper method to get the ether value equivalent of a token amount.
                            * @notice For low value amounts of tokens we accept this to return zero as these are small enough to disregard.
                            * Note that the price stored for tokens = price for 1 token (in ETH wei) * 10^(18-token decimals).
                            * @param _amount The token amount.
                            * @param _token The address of the token.
                            * @return The ether value for _amount of _token.
                            */
                            function getEtherValue(ITokenPriceRegistry _priceRegistry, uint256 _amount, address _token) internal view returns (uint256) {
                                uint256 price = _priceRegistry.getTokenPrice(_token);
                                uint256 etherValue = price.mul(_amount).div(10**18);
                                return etherValue;
                            }
                            /**
                            * @notice Helper method to get the current limit from a Limit struct.
                            * @param _limit The limit struct
                            */
                            function currentLimit(ILimitStorage.Limit memory _limit) internal view returns (uint256) {
                                if (_limit.changeAfter > 0 && _limit.changeAfter < block.timestamp) {
                                    return _limit.pending;
                                }
                                return _limit.current;
                            }
                            function safe128(uint256 _num) internal pure returns (uint128) {
                                require(_num < 2**128, "LU: more then 128 bits");
                                return uint128(_num);
                            }
                            function safe64(uint256 _num) internal pure returns (uint64) {
                                require(_num < 2**64, "LU: more then 64 bits");
                                return uint64(_num);
                            }
                            // *************** Storage invocations in VersionManager ********************* //
                            function setLimit(
                                IVersionManager _versionManager,
                                ILimitStorage _lStorage,
                                address _wallet, 
                                ILimitStorage.Limit memory _limit
                            ) internal {
                                _versionManager.invokeStorage(
                                    _wallet,
                                    address(_lStorage),
                                    abi.encodeWithSelector(_lStorage.setLimit.selector, _wallet, _limit)
                                );
                            }
                            function setDailySpent(
                                IVersionManager _versionManager,
                                ILimitStorage _lStorage,
                                address _wallet, 
                                ILimitStorage.DailySpent memory _dailySpent
                            ) private {
                                _versionManager.invokeStorage(
                                    _wallet,
                                    address(_lStorage),
                                    abi.encodeWithSelector(_lStorage.setDailySpent.selector, _wallet, _dailySpent)
                                );
                            }
                        }
                        // File: contracts/infrastructure/storage/IGuardianStorage.sol
                        // 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 <0.7.0;
                        interface IGuardianStorage {
                            /**
                             * @notice Lets an authorised module add a guardian to a wallet.
                             * @param _wallet The target wallet.
                             * @param _guardian The guardian to add.
                             */
                            function addGuardian(address _wallet, address _guardian) external;
                            /**
                             * @notice Lets an authorised module revoke a guardian from a wallet.
                             * @param _wallet The target wallet.
                             * @param _guardian The guardian to revoke.
                             */
                            function revokeGuardian(address _wallet, address _guardian) external;
                            /**
                             * @notice 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(address _wallet, address _guardian) external view returns (bool);
                            function isLocked(address _wallet) external view returns (bool);
                            function getLock(address _wallet) external view returns (uint256);
                            function getLocker(address _wallet) external view returns (address);
                            function setLock(address _wallet, uint256 _releaseAfter) external;
                            function getGuardians(address _wallet) external view returns (address[] memory);
                            function guardianCount(address _wallet) external view returns (uint256);
                        }
                        // File: modules/RelayerManager.sol
                        // 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 RelayerManager
                         * @notice Feature to execute transactions signed by ETH-less accounts and sent by a relayer.
                         * @author Julien Niset <julien@argent.xyz>, Olivier VDB <olivier@argent.xyz>
                         */
                        contract RelayerManager is BaseFeature {
                            bytes32 constant NAME = "RelayerManager";
                            uint256 constant internal BLOCKBOUND = 10000;
                            using SafeMath for uint256;
                            mapping (address => RelayerConfig) public relayer;
                            // The storage of the limit
                            ILimitStorage public limitStorage;
                            // The Token price storage
                            ITokenPriceRegistry public tokenPriceRegistry;
                            // The Guardian storage
                            IGuardianStorage public guardianStorage;
                            struct RelayerConfig {
                                uint256 nonce;
                                mapping (bytes32 => bool) executedTx;
                            }
                            // Used to avoid stack too deep error
                            struct StackExtension {
                                uint256 requiredSignatures;
                                OwnerSignature ownerSignatureRequirement;
                                bytes32 signHash;
                                bool success;
                                bytes returnData;
                            }
                            event TransactionExecuted(address indexed wallet, bool indexed success, bytes returnData, bytes32 signedHash);
                            event Refund(address indexed wallet, address indexed refundAddress, address refundToken, uint256 refundAmount);
                            /* ***************** External methods ************************* */
                            constructor(
                                ILockStorage _lockStorage,
                                IGuardianStorage _guardianStorage,
                                ILimitStorage _limitStorage,
                                ITokenPriceRegistry _tokenPriceRegistry,
                                IVersionManager _versionManager
                            )
                                BaseFeature(_lockStorage, _versionManager, NAME)
                                public
                            {
                                limitStorage = _limitStorage;
                                tokenPriceRegistry = _tokenPriceRegistry;
                                guardianStorage = _guardianStorage;
                            }
                            /**
                            * @notice Executes a relayed transaction.
                            * @param _wallet The target wallet.
                            * @param _feature The target feature.
                            * @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.
                            * @param _refundToken The token to use for the gas refund.
                            * @param _refundAddress The address refunded to prevent front-running.
                            */
                            function execute(
                                address _wallet,
                                address _feature,
                                bytes calldata _data,
                                uint256 _nonce,
                                bytes calldata _signatures,
                                uint256 _gasPrice,
                                uint256 _gasLimit,
                                address _refundToken,
                                address _refundAddress
                            )
                                external
                                returns (bool)
                            {
                                uint startGas = gasleft();
                                require(startGas >= _gasLimit, "RM: not enough gas provided");
                                require(verifyData(_wallet, _data), "RM: Target of _data != _wallet");
                                require(isFeatureAuthorisedInVersionManager(_wallet, _feature), "RM: feature not authorised");
                                StackExtension memory stack;
                                (stack.requiredSignatures, stack.ownerSignatureRequirement) = IFeature(_feature).getRequiredSignatures(_wallet, _data);
                                require(stack.requiredSignatures > 0 || stack.ownerSignatureRequirement == OwnerSignature.Anyone, "RM: Wrong signature requirement");
                                require(stack.requiredSignatures * 65 == _signatures.length, "RM: Wrong number of signatures");
                                stack.signHash = getSignHash(
                                    address(this),
                                    _feature,
                                    0,
                                    _data,
                                    _nonce,
                                    _gasPrice,
                                    _gasLimit,
                                    _refundToken,
                                    _refundAddress);
                                require(checkAndUpdateUniqueness(
                                    _wallet,
                                    _nonce,
                                    stack.signHash,
                                    stack.requiredSignatures,
                                    stack.ownerSignatureRequirement), "RM: Duplicate request");
                                require(validateSignatures(_wallet, stack.signHash, _signatures, stack.ownerSignatureRequirement), "RM: Invalid signatures");
                                (stack.success, stack.returnData) = _feature.call(_data);
                                // only refund when approved by owner and positive gas price
                                if (_gasPrice > 0 && stack.ownerSignatureRequirement == OwnerSignature.Required) {
                                    refund(
                                        _wallet,
                                        startGas,
                                        _gasPrice,
                                        _gasLimit,
                                        _refundToken,
                                        _refundAddress,
                                        stack.requiredSignatures);
                                }
                                emit TransactionExecuted(_wallet, stack.success, stack.returnData, stack.signHash);
                                return stack.success;
                            }
                            /**
                            * @notice Gets the current nonce for a wallet.
                            * @param _wallet The target wallet.
                            */
                            function getNonce(address _wallet) external view returns (uint256 nonce) {
                                return relayer[_wallet].nonce;
                            }
                            /**
                            * @notice Checks if a transaction identified by its sign hash has already been executed.
                            * @param _wallet The target wallet.
                            * @param _signHash The sign hash of the transaction.
                            */
                            function isExecutedTx(address _wallet, bytes32 _signHash) external view returns (bool executed) {
                                return relayer[_wallet].executedTx[_signHash];
                            }
                            /* ***************** Internal & Private methods ************************* */
                            /**
                            * @notice Generates the signed hash of a relayed transaction according to ERC 1077.
                            * @param _from The starting address for the relayed transaction (should be the relayer module)
                            * @param _to The destination address for the relayed transaction (should be the target module)
                            * @param _value The value for the relayed transaction.
                            * @param _data The data for the relayed transaction which includes the wallet address.
                            * @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.
                            * @param _refundToken The token to use for the gas refund.
                            * @param _refundAddress The address refunded to prevent front-running.
                            */
                            function getSignHash(
                                address _from,
                                address _to,
                                uint256 _value,
                                bytes memory _data,
                                uint256 _nonce,
                                uint256 _gasPrice,
                                uint256 _gasLimit,
                                address _refundToken,
                                address _refundAddress
                            )
                                internal
                                pure
                                returns (bytes32)
                            {
                                return keccak256(
                                    abi.encodePacked(
                                        "\x19Ethereum Signed Message:\n32",
                                        keccak256(abi.encodePacked(
                                            byte(0x19),
                                            byte(0),
                                            _from,
                                            _to,
                                            _value,
                                            _data,
                                            getChainId(),
                                            _nonce,
                                            _gasPrice,
                                            _gasLimit,
                                            _refundToken,
                                            _refundAddress))
                                ));
                            }
                            /**
                            * @notice Checks if the relayed transaction is unique. If yes the state is updated.
                            * For actions requiring 1 signature by the owner we use the incremental nonce.
                            * For all other actions we check/store the signHash in a mapping.
                            * @param _wallet The target wallet.
                            * @param _nonce The nonce.
                            * @param _signHash The signed hash of the transaction.
                            * @param requiredSignatures The number of signatures required.
                            * @param ownerSignatureRequirement The wallet owner signature requirement.
                            * @return true if the transaction is unique.
                            */
                            function checkAndUpdateUniqueness(
                                address _wallet,
                                uint256 _nonce,
                                bytes32 _signHash,
                                uint256 requiredSignatures,
                                OwnerSignature ownerSignatureRequirement
                            )
                                internal
                                returns (bool)
                            {
                                if (requiredSignatures == 1 && ownerSignatureRequirement == OwnerSignature.Required) {
                                    // use the incremental nonce
                                    if (_nonce <= relayer[_wallet].nonce) {
                                        return false;
                                    }
                                    uint256 nonceBlock = (_nonce & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) >> 128;
                                    if (nonceBlock > block.number + BLOCKBOUND) {
                                        return false;
                                    }
                                    relayer[_wallet].nonce = _nonce;
                                    return true;
                                } else {
                                    // use the txHash map
                                    if (relayer[_wallet].executedTx[_signHash] == true) {
                                        return false;
                                    }
                                    relayer[_wallet].executedTx[_signHash] = true;
                                    return true;
                                }
                            }
                            /**
                            * @notice 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 _signHash The signed hash representing the relayed transaction.
                            * @param _signatures The signatures as a concatenated byte array.
                            * @param _option An enum indicating whether the owner is required, optional or disallowed.
                            * @return A boolean indicating whether the signatures are valid.
                            */
                            function validateSignatures(
                                address _wallet,
                                bytes32 _signHash,
                                bytes memory _signatures,
                                OwnerSignature _option
                            )
                                internal
                                view
                                returns (bool)
                            {
                                if (_signatures.length == 0) {
                                    return true;
                                }
                                address lastSigner = address(0);
                                address[] memory guardians;
                                if (_option != OwnerSignature.Required || _signatures.length > 65) {
                                    guardians = guardianStorage.getGuardians(_wallet); // guardians are only read if they may be needed
                                }
                                bool isGuardian;
                                for (uint256 i = 0; i < _signatures.length / 65; i++) {
                                    address signer = Utils.recoverSigner(_signHash, _signatures, i);
                                    if (i == 0) {
                                        if (_option == OwnerSignature.Required) {
                                            // First signer must be owner
                                            if (isOwner(_wallet, signer)) {
                                                continue;
                                            }
                                            return false;
                                        } else if (_option == OwnerSignature.Optional) {
                                            // First signer can be owner
                                            if (isOwner(_wallet, signer)) {
                                                continue;
                                            }
                                        }
                                    }
                                    if (signer <= lastSigner) {
                                        return false; // Signers must be different
                                    }
                                    lastSigner = signer;
                                    (isGuardian, guardians) = GuardianUtils.isGuardianOrGuardianSigner(guardians, signer);
                                    if (!isGuardian) {
                                        return false;
                                    }
                                }
                                return true;
                            }
                            /**
                            * @notice Refunds the gas used to the Relayer.
                            * @param _wallet The target wallet.
                            * @param _startGas The gas provided at the start of the execution.
                            * @param _gasPrice The gas price for the refund.
                            * @param _gasLimit The gas limit for the refund.
                            * @param _refundToken The token to use for the gas refund.
                            * @param _refundAddress The address refunded to prevent front-running.
                            * @param _requiredSignatures The number of signatures required.
                            */
                            function refund(
                                address _wallet,
                                uint _startGas,
                                uint _gasPrice,
                                uint _gasLimit,
                                address _refundToken,
                                address _refundAddress,
                                uint256 _requiredSignatures
                            )
                                internal
                            {
                                address refundAddress = _refundAddress == address(0) ? msg.sender : _refundAddress;
                                uint256 refundAmount;
                                // skip daily limit when approved by guardians (and signed by owner)
                                if (_requiredSignatures > 1) {
                                    uint256 gasConsumed = _startGas.sub(gasleft()).add(30000);
                                    refundAmount = Utils.min(gasConsumed, _gasLimit).mul(_gasPrice);
                                } else {
                                    uint256 gasConsumed = _startGas.sub(gasleft()).add(40000);
                                    refundAmount = Utils.min(gasConsumed, _gasLimit).mul(_gasPrice);
                                    uint256 ethAmount = (_refundToken == ETH_TOKEN) ? refundAmount : LimitUtils.getEtherValue(tokenPriceRegistry, refundAmount, _refundToken);
                                    require(LimitUtils.checkAndUpdateDailySpent(limitStorage, versionManager, _wallet, ethAmount), "RM: refund is above daily limit");
                                }
                                // refund in ETH or ERC20
                                if (_refundToken == ETH_TOKEN) {
                                    invokeWallet(_wallet, refundAddress, refundAmount, EMPTY_BYTES);
                                } else {
                                    bytes memory methodData = abi.encodeWithSignature("transfer(address,uint256)", refundAddress, refundAmount);
                        		    bytes memory transferSuccessBytes = invokeWallet(_wallet, _refundToken, 0, methodData);
                                    // Check token refund is successful, when `transfer` returns a success bool result
                                    if (transferSuccessBytes.length > 0) {
                                        require(abi.decode(transferSuccessBytes, (bool)), "RM: Refund transfer failed");
                                    }
                                }
                                emit Refund(_wallet, refundAddress, _refundToken, refundAmount);
                            }
                           /**
                            * @notice Returns the current chainId
                            * @return chainId the chainId
                            */
                            function getChainId() private pure returns (uint256 chainId) {
                                // solhint-disable-next-line no-inline-assembly
                                assembly { chainId := chainid() }
                            }
                        }

                        File 2 of 6: TransferManager
                        // Copyright (C) 2020  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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity >=0.5.4 <0.7.0;
                        /**
                         * @title IModuleRegistry
                         * @notice Interface for the registry of authorised modules.
                         */
                        interface IModuleRegistry {
                            function registerModule(address _module, bytes32 _name) external;
                            function deregisterModule(address _module) external;
                            function registerUpgrader(address _upgrader, bytes32 _name) external;
                            function deregisterUpgrader(address _upgrader) external;
                            function recoverToken(address _token) external;
                            function moduleInfo(address _module) external view returns (bytes32);
                            function upgraderInfo(address _upgrader) external view returns (bytes32);
                            function isRegisteredModule(address _module) external view returns (bool);
                            function isRegisteredModule(address[] calldata _modules) external view returns (bool);
                            function isRegisteredUpgrader(address _upgrader) external view returns (bool);
                        }// 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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity ^0.6.12;
                        /**
                         * @title ITokenPriceRegistry
                         * @notice TokenPriceRegistry interface
                         */
                        interface ITokenPriceRegistry {
                            function getTokenPrice(address _token) external view returns (uint184 _price);
                            function isTokenTradable(address _token) external view returns (bool _isTradable);
                        }// 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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity ^0.6.12;
                        pragma experimental ABIEncoderV2;
                        /**
                         * @title ILimitStorage
                         * @notice LimitStorage interface
                         */
                        interface ILimitStorage {
                            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;
                            }
                            function setLimit(address _wallet, Limit memory _limit) external;
                            function getLimit(address _wallet) external view returns (Limit memory _limit);
                            function setDailySpent(address _wallet, DailySpent memory _dailySpent) external;
                            function getDailySpent(address _wallet) external view returns (DailySpent memory _dailySpent);
                            function setLimitAndDailySpent(address _wallet, Limit memory _limit, DailySpent memory _dailySpent) external;
                            function getLimitAndDailySpent(address _wallet) external view returns (Limit memory _limit, DailySpent memory _dailySpent);
                        }// 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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity >=0.5.4 <0.7.0;
                        interface ILockStorage {
                            function isLocked(address _wallet) external view returns (bool);
                            function getLock(address _wallet) external view returns (uint256);
                            function getLocker(address _wallet) external view returns (address);
                            function setLock(address _wallet, address _locker, uint256 _releaseAfter) external;
                        }// Copyright (C) 2020  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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity >=0.5.4 <0.7.0;
                        /**
                         * @title ITransferStorage
                         * @notice TransferStorage interface
                         */
                        interface ITransferStorage {
                            function setWhitelist(address _wallet, address _target, uint256 _value) external;
                            function getWhitelist(address _wallet, address _target) external view returns (uint256);
                        }// 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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity ^0.6.12;
                        pragma experimental ABIEncoderV2;
                        import "./common/Utils.sol";
                        import "./common/BaseTransfer.sol";
                        import "./common/LimitUtils.sol";
                        import "../infrastructure/storage/ILimitStorage.sol";
                        import "../infrastructure/storage/ITransferStorage.sol";
                        import "../infrastructure/ITokenPriceRegistry.sol";
                        import "../../lib/other/ERC20.sol";
                        /**
                         * @title TransferManager
                         * @notice Feature to transfer and approve tokens (ETH or ERC20) or data (contract call) based on a security context (daily limit, whitelist, etc).
                         * @author Julien Niset - <julien@argent.xyz>
                         */
                        contract TransferManager is BaseTransfer {
                            bytes32 constant NAME = "TransferManager";
                            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 default limit
                            uint128 public defaultLimit;
                            // The Token storage
                            ITransferStorage public transferStorage;
                            // The previous limit manager needed to migrate the limits
                            TransferManager public oldTransferManager;
                            // The limit storage
                            ILimitStorage public limitStorage;
                            // The token price storage
                            ITokenPriceRegistry public tokenPriceRegistry;
                            // *************** 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);
                            event DailyLimitMigrated(address indexed wallet, uint256 currentDailyLimit, uint256 pendingDailyLimit, uint256 changeDailyLimitAfter);
                            event DailyLimitDisabled(address indexed wallet, uint256 securityPeriod);
                            // *************** Constructor ********************** //
                            constructor(
                                ILockStorage _lockStorage,
                                ITransferStorage _transferStorage,
                                ILimitStorage _limitStorage,
                                ITokenPriceRegistry _tokenPriceRegistry,
                                IVersionManager _versionManager,
                                uint256 _securityPeriod,
                                uint256 _securityWindow,
                                uint256 _defaultLimit,
                                address _wethToken,
                                TransferManager _oldTransferManager
                            )
                                BaseFeature(_lockStorage, _versionManager, NAME)
                                BaseTransfer(_wethToken)
                                public
                            {
                                transferStorage = _transferStorage;
                                limitStorage = _limitStorage;
                                tokenPriceRegistry = _tokenPriceRegistry;
                                securityPeriod = _securityPeriod;
                                securityWindow = _securityWindow;
                                defaultLimit = LimitUtils.safe128(_defaultLimit);
                                oldTransferManager = _oldTransferManager;
                            }
                            /**
                             * @inheritdoc IFeature
                             */
                            function getRequiredSignatures(address, bytes calldata) external view override returns (uint256, OwnerSignature) {
                                return (1, OwnerSignature.Required);
                            }
                            /**
                             * @inheritdoc IFeature
                             */
                            function getStaticCallSignatures() external virtual override view returns (bytes4[] memory _sigs) {
                                _sigs = new bytes4[](1);
                                _sigs[0] = ERC1271_ISVALIDSIGNATURE_BYTES32;
                            }
                            /**
                             * @notice Inits the feature for a wallet by setting up the isValidSignature (EIP 1271)
                             * static call redirection from the wallet to the feature and copying all the parameters
                             * of the daily limit from the previous implementation of the LimitManager module.
                             * @param _wallet The target wallet.
                             */
                            function init(address _wallet) external override(BaseFeature) onlyVersionManager {
                                if (address(oldTransferManager) == address(0)) {
                                    setLimit(_wallet, ILimitStorage.Limit(defaultLimit, 0, 0));
                                } else {
                                    uint256 current = oldTransferManager.getCurrentLimit(_wallet);
                                    (uint256 pending, uint64 changeAfter) = oldTransferManager.getPendingLimit(_wallet);
                                    if (current == 0 && changeAfter == 0) {
                                        // new wallet: we setup the default limit
                                        setLimit(_wallet, ILimitStorage.Limit(defaultLimit, 0, 0));
                                    } else {
                                        // migrate limit and daily spent (if we are in a rolling period)
                                        (uint256 unspent, uint64 periodEnd) = oldTransferManager.getDailyUnspent(_wallet);
                                        if (periodEnd < block.timestamp) {
                                            setLimit(_wallet, ILimitStorage.Limit(LimitUtils.safe128(current), LimitUtils.safe128(pending), changeAfter));
                                        } else {
                                            setLimitAndDailySpent(
                                                _wallet,
                                                ILimitStorage.Limit(LimitUtils.safe128(current), LimitUtils.safe128(pending), changeAfter),
                                                ILimitStorage.DailySpent(LimitUtils.safe128(current.sub(unspent)), periodEnd)
                                            );
                                        }
                                        emit DailyLimitMigrated(_wallet, current, pending, changeAfter);
                                    }
                                }
                            }
                            // *************** External/Public Functions ********************* //
                            /**
                            * @notice 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(
                                address _wallet,
                                address _token,
                                address _to,
                                uint256 _amount,
                                bytes calldata _data
                            )
                                external
                                onlyWalletOwnerOrFeature(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                if (isWhitelisted(_wallet, _to)) {
                                    // transfer to whitelist
                                    doTransfer(_wallet, _token, _to, _amount, _data);
                                } else {
                                    uint256 etherAmount = (_token == ETH_TOKEN) ? _amount : LimitUtils.getEtherValue(tokenPriceRegistry, _amount, _token);
                                    if (LimitUtils.checkAndUpdateDailySpent(limitStorage, versionManager, _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(_wallet, id, executeAfter, _token, _to, _amount, _data);
                                    }
                                }
                            }
                            /**
                            * @notice 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(
                                address _wallet,
                                address _token,
                                address _spender,
                                uint256 _amount
                            )
                                external
                                onlyWalletOwnerOrFeature(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                if (isWhitelisted(_wallet, _spender)) {
                                    // approve to whitelist
                                    doApproveToken(_wallet, _token, _spender, _amount);
                                } else {
                                    // get current alowance
                                    uint256 currentAllowance = ERC20(_token).allowance(_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 = LimitUtils.getEtherValue(tokenPriceRegistry, delta, _token);
                                        require(LimitUtils.checkAndUpdateDailySpent(limitStorage, versionManager, _wallet, deltaInEth), "TM: Approve above daily limit");
                                        // approve if under the limit
                                        doApproveToken(_wallet, _token, _spender, _amount);
                                    }
                                }
                            }
                            /**
                            * @notice 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(
                                address _wallet,
                                address _contract,
                                uint256 _value,
                                bytes calldata _data
                            )
                                external
                                onlyWalletOwnerOrFeature(_wallet)
                                onlyWhenUnlocked(_wallet)
                                onlyAuthorisedContractCall(_wallet, _contract)
                            {
                                checkAndUpdateDailySpentIfNeeded(_wallet, ETH_TOKEN, _value, _contract);
                                doCallContract(_wallet, _contract, _value, _data);
                            }
                            /**
                            * @notice 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 _proxy The address to approve, which may be different from the contract being called.
                            * @param _amount The amount of ERC20 tokens to approve.
                            * @param _contract The address of the contract.
                            * @param _data The encoded method data
                            */
                            function approveTokenAndCallContract(
                                address _wallet,
                                address _token,
                                address _proxy,
                                uint256 _amount,
                                address _contract,
                                bytes calldata _data
                            )
                                external
                                onlyWalletOwnerOrFeature(_wallet)
                                onlyWhenUnlocked(_wallet)
                                onlyAuthorisedContractCall(_wallet, _contract)
                            {
                                checkAndUpdateDailySpentIfNeeded(_wallet, _token, _amount, _contract);
                                doApproveTokenAndCallContract(_wallet, _token, _proxy, _amount, _contract, _data);
                            }
                            /**
                            * @notice Lets the owner wrap ETH into WETH, approve the WETH and call a contract.
                            * We assume that the contract will pull the tokens and does not require ETH.
                            * @param _wallet The target wallet.
                            * @param _proxy The address to approve, which may be different from the contract being called.
                            * @param _amount The amount of ETH to wrap and approve.
                            * @param _contract The address of the contract.
                            * @param _data The encoded method data
                            */
                            function approveWethAndCallContract(
                                address _wallet,
                                address _proxy,
                                uint256 _amount,
                                address _contract,
                                bytes calldata _data
                            )
                                external
                                onlyWalletOwnerOrFeature(_wallet)
                                onlyWhenUnlocked(_wallet)
                                onlyAuthorisedContractCall(_wallet, _contract)
                            {
                                checkAndUpdateDailySpentIfNeeded(_wallet, wethToken, _amount, _contract);
                                doApproveWethAndCallContract(_wallet, _proxy, _amount, _contract, _data);
                            }
                            /**
                             * @notice Adds an address to the whitelist of a wallet.
                             * @param _wallet The target wallet.
                             * @param _target The address to add.
                             */
                            function addToWhitelist(
                                address _wallet,
                                address _target
                            )
                                external
                                onlyWalletOwnerOrFeature(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                require(!isWhitelisted(_wallet, _target), "TT: target already whitelisted");
                                uint256 whitelistAfter = block.timestamp.add(securityPeriod);
                                setWhitelist(_wallet, _target, whitelistAfter);
                                emit AddedToWhitelist(_wallet, _target, uint64(whitelistAfter));
                            }
                            /**
                             * @notice Removes an address from the whitelist of a wallet.
                             * @param _wallet The target wallet.
                             * @param _target The address to remove.
                             */
                            function removeFromWhitelist(
                                address _wallet,
                                address _target
                            )
                                external
                                onlyWalletOwnerOrFeature(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                setWhitelist(_wallet, _target, 0);
                                emit RemovedFromWhitelist(_wallet, _target);
                            }
                            /**
                            * @notice 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(
                                address _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[_wallet].pendingActions[id];
                                require(executeAfter > 0, "TT: unknown pending transfer");
                                uint executeBefore = executeAfter.add(securityWindow);
                                require(executeAfter <= block.timestamp && block.timestamp <= executeBefore, "TT: transfer outside of the execution window");
                                delete configs[_wallet].pendingActions[id];
                                doTransfer(_wallet, _token, _to, _amount, _data);
                                emit PendingTransferExecuted(_wallet, id);
                            }
                            function cancelPendingTransfer(
                                address _wallet,
                                bytes32 _id
                            )
                                external
                                onlyWalletOwnerOrFeature(_wallet)
                                onlyWhenUnlocked(_wallet)
                            {
                                require(configs[_wallet].pendingActions[_id] > 0, "TT: unknown pending action");
                                delete configs[_wallet].pendingActions[_id];
                                emit PendingTransferCanceled(_wallet, _id);
                            }
                            /**
                             * @notice 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(address _wallet, uint256 _newLimit) external onlyWalletOwnerOrFeature(_wallet) onlyWhenUnlocked(_wallet) {
                                ILimitStorage.Limit memory limit = LimitUtils.changeLimit(limitStorage, versionManager, _wallet, _newLimit, securityPeriod);
                                emit LimitChanged(_wallet, _newLimit, limit.changeAfter);
                            }
                            /**
                             * @notice 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(address _wallet) external onlyWalletOwnerOrFeature(_wallet) onlyWhenUnlocked(_wallet) {
                                LimitUtils.disableLimit(limitStorage, versionManager, _wallet, securityPeriod);
                                emit DailyLimitDisabled(_wallet, securityPeriod);
                            }
                            /**
                            * @notice Gets the current daily limit for a wallet.
                            * @param _wallet The target wallet.
                            * @return _currentLimit The current limit expressed in ETH.
                            */
                            function getCurrentLimit(address _wallet) external view returns (uint256 _currentLimit) {
                                ILimitStorage.Limit memory limit = limitStorage.getLimit(_wallet);
                                return LimitUtils.currentLimit(limit);
                            }
                            /**
                            * @notice Returns whether the daily limit is disabled for a wallet.
                            * @param _wallet The target wallet.
                            * @return _limitDisabled true if the daily limit is disabled, false otherwise.
                            */
                            function isLimitDisabled(address _wallet) public view returns (bool _limitDisabled) {
                                return LimitUtils.isLimitDisabled(limitStorage, _wallet);
                            }
                            /**
                            * @notice Gets a pending limit for a wallet if any.
                            * @param _wallet The target wallet.
                            * @return _pendingLimit The pending limit (in ETH).
                            * @return _changeAfter The time at which the pending limit will become effective.
                            */
                            function getPendingLimit(address _wallet) external view returns (uint256 _pendingLimit, uint64 _changeAfter) {
                                ILimitStorage.Limit memory limit = limitStorage.getLimit(_wallet);
                                return ((block.timestamp < limit.changeAfter)? (limit.pending, uint64(limit.changeAfter)) : (0,0));
                            }
                            /**
                            * @notice Gets the amount of tokens that has not yet been spent during the current period.
                            * @param _wallet The target wallet.
                            * @return _unspent The amount of tokens (in ETH) that has not been spent yet.
                            * @return _periodEnd The end of the daily period.
                            */
                            function getDailyUnspent(address _wallet) external view returns (uint256 _unspent, uint64 _periodEnd) {
                                (
                                    ILimitStorage.Limit memory limit,
                                    ILimitStorage.DailySpent memory dailySpent
                                ) = limitStorage.getLimitAndDailySpent(_wallet);
                                uint256 currentLimit = LimitUtils.currentLimit(limit);
                                if (block.timestamp > dailySpent.periodEnd) {
                                    return (currentLimit, uint64(block.timestamp.add(24 hours)));
                                } else if (dailySpent.alreadySpent < currentLimit) {
                                    return (currentLimit.sub(dailySpent.alreadySpent), dailySpent.periodEnd);
                                } else {
                                    return (0, dailySpent.periodEnd);
                                }
                            }
                            /**
                            * @notice Checks if an address is whitelisted for a wallet.
                            * @param _wallet The target wallet.
                            * @param _target The address.
                            * @return _isWhitelisted true if the address is whitelisted.
                            */
                            function isWhitelisted(address _wallet, address _target) public view returns (bool _isWhitelisted) {
                                uint whitelistAfter = transferStorage.getWhitelist(_wallet, _target);
                                
                                return whitelistAfter > 0 && whitelistAfter < block.timestamp;
                            }
                            /**
                            * @notice Gets the info of a pending transfer for a wallet.
                            * @param _wallet The target wallet.
                            * @param _id The pending transfer ID.
                            * @return _executeAfter The epoch time at which the pending transfer can be executed.
                            */
                            function getPendingTransfer(address _wallet, bytes32 _id) external view returns (uint64 _executeAfter) {
                                _executeAfter = uint64(configs[address(_wallet)].pendingActions[_id]);
                            }
                            /**
                            * @notice 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 = Utils.recoverSigner(_msgHash, _signature, 0);
                                require(isOwner(msg.sender, signer), "TM: Invalid signer");
                                return ERC1271_ISVALIDSIGNATURE_BYTES32;
                            }
                            // *************** Internal Functions ********************* //
                            /**
                             * @notice 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 id The identifier for the new pending action.
                             * @return executeAfter The time when the action can be executed
                             */
                            function addPendingAction(
                                ActionType _action,
                                address _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[_wallet].pendingActions[id] == 0, "TM: duplicate pending action");
                                executeAfter = block.timestamp.add(securityPeriod);
                                configs[_wallet].pendingActions[id] = executeAfter;
                            }
                            /**
                            * @notice Make sure a contract call is not trying to call a supported ERC20.
                            * @param _wallet The target wallet.
                            * @param _contract The address of the contract.
                             */
                            function coveredByDailyLimit(address _wallet, address _contract) internal view returns (bool) {
                                return (tokenPriceRegistry.getTokenPrice(_contract) > 0 && !isLimitDisabled(_wallet));
                            }
                            /**
                            * @notice Verify and update the daily spent if the spender is not whitelisted.
                            * Reverts if the daily spent is insufficient or if the contract to call is
                            * protected by the daily limit (i.e. is a token contract).
                            * @param _wallet The target wallet.
                            * @param _token The token that the spender will spend.
                            * @param _amount The amount of ERC20 or ETH that the spender will spend.
                            * @param _contract The address of the contract called by the wallet for the spend to occur.
                            */
                            function checkAndUpdateDailySpentIfNeeded(
                                address _wallet,
                                address _token,
                                uint256 _amount,
                                address _contract
                            )
                                internal
                            {
                                if (!isWhitelisted(_wallet, _contract)) {
                                    // Make sure we don't call a supported ERC20 that's not whitelisted
                                    require(!coveredByDailyLimit(_wallet, _contract), "TM: Forbidden contract");
                                    // 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;
                                    if (_token == ETH_TOKEN || _token == wethToken) {
                                        valueInEth = _amount;
                                    } else {
                                        valueInEth = LimitUtils.getEtherValue(tokenPriceRegistry, _amount, _token);
                                    }
                                    require(LimitUtils.checkAndUpdateDailySpent(limitStorage, versionManager, _wallet, valueInEth), "TM: Approve above daily limit");
                                }
                            }
                            // *************** Internal Functions ********************* //
                            function setWhitelist(address _wallet, address _target, uint256 _whitelistAfter) internal {
                                versionManager.invokeStorage(
                                    _wallet,
                                    address(transferStorage),
                                    abi.encodeWithSelector(transferStorage.setWhitelist.selector, _wallet, _target, _whitelistAfter)
                                );
                            }
                            function setLimit(address _wallet, ILimitStorage.Limit memory _limit) internal {
                                versionManager.invokeStorage(
                                    _wallet,
                                    address(limitStorage),
                                    abi.encodeWithSelector(limitStorage.setLimit.selector, _wallet, _limit)
                                );
                            }
                            function setLimitAndDailySpent(
                                address _wallet,
                                ILimitStorage.Limit memory _limit,
                                ILimitStorage.DailySpent memory _dailySpent
                            ) internal {
                                versionManager.invokeStorage(
                                    _wallet,
                                    address(limitStorage),
                                    abi.encodeWithSelector(limitStorage.setLimitAndDailySpent.selector, _wallet, _limit, _dailySpent)
                                );
                            }
                        }
                        // 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.s
                        // You should have received a copy of the GNU General Public License
                        // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity ^0.6.12;
                        import "@openzeppelin/contracts/math/SafeMath.sol";
                        import "../../wallet/IWallet.sol";
                        import "../../infrastructure/IModuleRegistry.sol";
                        import "../../infrastructure/storage/ILockStorage.sol";
                        import "./IFeature.sol";
                        import "../../../lib/other/ERC20.sol";
                        import "./IVersionManager.sol";
                        /**
                         * @title BaseFeature
                         * @notice Base Feature contract that contains methods common to all Feature contracts.
                         * @author Julien Niset - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                         */
                        contract BaseFeature is IFeature {
                            // Empty calldata
                            bytes constant internal EMPTY_BYTES = "";
                            // Mock token address for ETH
                            address constant internal ETH_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                            // The address of the Lock storage
                            ILockStorage internal lockStorage;
                            // The address of the Version Manager
                            IVersionManager internal versionManager;
                            event FeatureCreated(bytes32 name);
                            /**
                             * @notice Throws if the wallet is locked.
                             */
                            modifier onlyWhenUnlocked(address _wallet) {
                                require(!lockStorage.isLocked(_wallet), "BF: wallet locked");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not the VersionManager.
                             */
                            modifier onlyVersionManager() {
                                require(msg.sender == address(versionManager), "BF: caller must be VersionManager");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not the owner of the target wallet.
                             */
                            modifier onlyWalletOwner(address _wallet) {
                                require(isOwner(_wallet, msg.sender), "BF: must be wallet owner");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not an authorised feature of the target wallet.
                             */
                            modifier onlyWalletFeature(address _wallet) {
                                require(versionManager.isFeatureAuthorised(_wallet, msg.sender), "BF: must be a wallet feature");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not the owner of the target wallet or the feature itself.
                             */
                            modifier onlyWalletOwnerOrFeature(address _wallet) {
                                // Wrapping in an internal method reduces deployment cost by avoiding duplication of inlined code
                                verifyOwnerOrAuthorisedFeature(_wallet, msg.sender);
                                _;
                            }
                            constructor(
                                ILockStorage _lockStorage,
                                IVersionManager _versionManager,
                                bytes32 _name
                            ) public {
                                lockStorage = _lockStorage;
                                versionManager = _versionManager;
                                emit FeatureCreated(_name);
                            }
                            /**
                            * @inheritdoc IFeature
                            */
                            function recoverToken(address _token) external virtual override {
                                uint total = ERC20(_token).balanceOf(address(this));
                                _token.call(abi.encodeWithSelector(ERC20(_token).transfer.selector, address(versionManager), total));
                            }
                            /**
                             * @notice Inits the feature for a wallet by doing nothing.
                             * @dev !! Overriding methods need make sure `init()` can only be called by the VersionManager !!
                             * @param _wallet The wallet.
                             */
                            function init(address _wallet) external virtual override  {}
                            /**
                             * @inheritdoc IFeature
                             */
                            function getRequiredSignatures(address, bytes calldata) external virtual view override returns (uint256, OwnerSignature) {
                                revert("BF: disabled method");
                            }
                            /**
                             * @inheritdoc IFeature
                             */
                            function getStaticCallSignatures() external virtual override view returns (bytes4[] memory _sigs) {}
                            /**
                             * @inheritdoc IFeature
                             */
                            function isFeatureAuthorisedInVersionManager(address _wallet, address _feature) public override view returns (bool) {
                                return versionManager.isFeatureAuthorised(_wallet, _feature);
                            }
                            /**
                            * @notice Checks that the wallet address provided as the first parameter of _data matches _wallet
                            * @return false if the addresses are different.
                            */
                            function verifyData(address _wallet, bytes calldata _data) internal pure returns (bool) {
                                require(_data.length >= 36, "RM: Invalid dataWallet");
                                address dataWallet = abi.decode(_data[4:], (address));
                                return dataWallet == _wallet;
                            }
                            
                             /**
                             * @notice 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(address _wallet, address _addr) internal view returns (bool) {
                                return IWallet(_wallet).owner() == _addr;
                            }
                            /**
                             * @notice Verify that the caller is an authorised feature or the wallet owner.
                             * @param _wallet The target wallet.
                             * @param _sender The caller.
                             */
                            function verifyOwnerOrAuthorisedFeature(address _wallet, address _sender) internal view {
                                require(isFeatureAuthorisedInVersionManager(_wallet, _sender) || isOwner(_wallet, _sender), "BF: must be owner or feature");
                            }
                            /**
                             * @notice 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) 
                            {
                                _res = versionManager.checkAuthorisedFeatureAndInvokeWallet(_wallet, _to, _value, _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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity ^0.6.12;
                        import "./BaseFeature.sol";
                        import "./LimitUtils.sol";
                        /**
                         * @title BaseTransfer
                         * @notice Contains common methods to transfer tokens or call third-party contracts.
                         * @author Olivier VDB - <olivier@argent.xyz>
                         */
                        abstract contract BaseTransfer is BaseFeature {
                            // The address of the WETH token
                            address public wethToken;
                            // *************** 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
                            );
                            event LimitChanged(address indexed wallet, uint indexed newLimit, uint64 indexed startAfter);
                            // *************** Constructor ********************** //
                            constructor(address _wethToken) public {
                                wethToken = _wethToken;
                            }
                                    
                            // *************** Internal Functions ********************* //
                            /**
                            * @notice Make sure a contract call is not trying to call a module, a feature, or the wallet itself.
                            * @param _wallet The target wallet.
                            * @param _contract The address of the contract.
                             */
                            modifier onlyAuthorisedContractCall(address _wallet, address _contract) {
                                require(
                                    _contract != _wallet && // not calling the wallet
                                    !IWallet(_wallet).authorised(_contract) && // not calling an authorised module
                                    !versionManager.isFeatureAuthorised(_wallet, _contract), // not calling an authorised feature
                                    "BT: Forbidden contract"
                                );
                                _;
                            }
                            /**
                            * @notice 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(address _wallet, address _token, address _to, uint256 _value, bytes memory _data) internal {
                                if (_token == ETH_TOKEN) {
                                    invokeWallet(_wallet, _to, _value, EMPTY_BYTES);
                                } else {
                                    bytes memory methodData = abi.encodeWithSignature("transfer(address,uint256)", _to, _value);
                                    bytes memory transferSuccessBytes = invokeWallet(_wallet, _token, 0, methodData);
                                    // Check transfer is successful, when `transfer` returns a success bool result
                                    if (transferSuccessBytes.length > 0) {
                                        require(abi.decode(transferSuccessBytes, (bool)), "RM: Transfer failed");
                                    }
                                }
                                emit Transfer(_wallet, _token, _value, _to, _data);
                            }
                            /**
                            * @notice 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(address _wallet, address _token, address _spender, uint256 _value) internal {
                                bytes memory methodData = abi.encodeWithSignature("approve(address,uint256)", _spender, _value);
                                invokeWallet(_wallet, _token, 0, methodData);
                                emit Approved(_wallet, _token, _value, _spender);
                            }
                            /**
                            * @notice 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(address _wallet, address _contract, uint256 _value, bytes memory _data) internal {
                                invokeWallet(_wallet, _contract, _value, _data);
                                emit CalledContract(_wallet, _contract, _value, _data);
                            }
                            /**
                            * @notice 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 _proxy The address to approve.
                            * @param _amount The amount of tokens to transfer.
                            * @param _contract The contract address.
                            * @param _data The method data.
                            */
                            function doApproveTokenAndCallContract(
                                address _wallet,
                                address _token,
                                address _proxy,
                                uint256 _amount,
                                address _contract,
                                bytes memory _data
                            )
                                internal
                            {
                                // Ensure there is sufficient balance of token before we approve
                                uint256 balance = ERC20(_token).balanceOf(_wallet);
                                require(balance >= _amount, "BT: insufficient balance");
                                uint256 existingAllowance = ERC20(_token).allowance(_wallet, _proxy);
                                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 _proxy uses the exact approved _amount.
                                bytes memory methodData = abi.encodeWithSignature("approve(address,uint256)", _proxy, totalAllowance);
                                invokeWallet(_wallet, _token, 0, methodData);
                                invokeWallet(_wallet, _contract, 0, _data);
                                // Calculate the approved amount that was spent after the call
                                uint256 unusedAllowance = ERC20(_token).allowance(_wallet, _proxy);
                                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)", _proxy, existingAllowance);
                                    invokeWallet(_wallet, _token, 0, methodData);
                                }
                                emit ApprovedAndCalledContract(
                                    _wallet,
                                    _contract,
                                    _proxy,
                                    _token,
                                    _amount,
                                    usedAllowance,
                                    _data);
                            }
                            /**
                            * @notice Helper method to wrap ETH into WETH, approve a certain amount of WETH and call an external contract.
                            * The address that spends the WETH and the address that is called with _data can be different.
                            * @param _wallet The target wallet.
                            * @param _proxy The address to approves.
                            * @param _amount The amount of tokens to transfer.
                            * @param _contract The contract address.
                            * @param _data The method data.
                            */
                            function doApproveWethAndCallContract(
                                address _wallet,
                                address _proxy,
                                uint256 _amount,
                                address _contract,
                                bytes memory _data
                            )
                                internal
                            {
                                uint256 wethBalance = ERC20(wethToken).balanceOf(_wallet);
                                if (wethBalance < _amount) {
                                    // Wrap ETH into WETH
                                    invokeWallet(_wallet, wethToken, _amount - wethBalance, abi.encodeWithSignature("deposit()"));
                                }
                                doApproveTokenAndCallContract(_wallet, wethToken, _proxy, _amount, _contract, _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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity >=0.5.4 <0.7.0;
                        /**
                         * @title IFeature
                         * @notice Interface for a Feature.
                         * @author Julien Niset - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                         */
                        interface IFeature {
                            enum OwnerSignature {
                                Anyone,             // Anyone
                                Required,           // Owner required
                                Optional,           // Owner and/or guardians
                                Disallowed          // guardians only
                            }
                            /**
                            * @notice Utility method to recover any ERC20 token that was sent to the Feature by mistake.
                            * @param _token The token to recover.
                            */
                            function recoverToken(address _token) external;
                            /**
                             * @notice Inits a Feature for a wallet by e.g. setting some wallet specific parameters in storage.
                             * @param _wallet The wallet.
                             */
                            function init(address _wallet) external;
                            /**
                             * @notice Helper method to check if an address is an authorised feature of a target wallet.
                             * @param _wallet The target wallet.
                             * @param _feature The address.
                             */
                            function isFeatureAuthorisedInVersionManager(address _wallet, address _feature) external view returns (bool);
                            /**
                            * @notice 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 and the wallet owner signature requirement.
                            */
                            function getRequiredSignatures(address _wallet, bytes calldata _data) external view returns (uint256, OwnerSignature);
                            /**
                            * @notice Gets the list of static call signatures that this feature responds to on behalf of wallets
                            */
                            function getStaticCallSignatures() external view returns (bytes4[] memory);
                        }// 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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity >=0.5.4 <0.7.0;
                        pragma experimental ABIEncoderV2;
                        import "../../infrastructure/storage/ILimitStorage.sol";
                        /**
                         * @title IVersionManager
                         * @notice Interface for the VersionManager module.
                         * @author Olivier VDB - <olivier@argent.xyz>
                         */
                        interface IVersionManager {
                            /**
                             * @notice Returns true if the feature is authorised for the wallet
                             * @param _wallet The target wallet.
                             * @param _feature The feature.
                             */
                            function isFeatureAuthorised(address _wallet, address _feature) external view returns (bool);
                            /**
                             * @notice Lets a feature (caller) 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 checkAuthorisedFeatureAndInvokeWallet(
                                address _wallet,
                                address _to,
                                uint256 _value,
                                bytes calldata _data
                            ) external returns (bytes memory _res);
                            /* ******* Backward Compatibility with old Storages and BaseWallet *************** */
                            /**
                             * @notice Sets a new owner for the wallet.
                             * @param _newOwner The new owner.
                             */
                            function setOwner(address _wallet, address _newOwner) external;
                            /**
                             * @notice Lets a feature write data to a storage contract.
                             * @param _wallet The target wallet.
                             * @param _storage The storage contract.
                             * @param _data The data of the call
                             */
                            function invokeStorage(address _wallet, address _storage, bytes calldata _data) external;
                            /**
                             * @notice Upgrade a wallet to a new version.
                             * @param _wallet the wallet to upgrade
                             * @param _toVersion the new version
                             */
                            function upgradeWallet(address _wallet, uint256 _toVersion) 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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity ^0.6.12;
                        pragma experimental ABIEncoderV2;
                        import "@openzeppelin/contracts/math/SafeMath.sol";
                        import "../../infrastructure/storage/ILimitStorage.sol";
                        import "../../infrastructure/ITokenPriceRegistry.sol";
                        import "./IVersionManager.sol";
                        /**
                         * @title LimitUtils
                         * @notice Helper library to manage the daily limit and interact with a contract implementing the ILimitStorage interface.
                         * @author Julien Niset - <julien@argent.xyz>
                         */
                        library LimitUtils {
                            // large limit when the limit can be considered disabled
                            uint128 constant internal LIMIT_DISABLED = uint128(-1);
                            using SafeMath for uint256;
                            // *************** Internal Functions ********************* //
                            /**
                             * @notice Changes the daily limit (expressed in ETH).
                             * Decreasing the limit is immediate while increasing the limit is pending for the security period.
                             * @param _lStorage The storage contract.
                             * @param _versionManager The version manager.
                             * @param _wallet The target wallet.
                             * @param _targetLimit The target limit.
                             * @param _securityPeriod The security period.
                             */
                            function changeLimit(
                                ILimitStorage _lStorage,
                                IVersionManager _versionManager,
                                address _wallet,
                                uint256 _targetLimit,
                                uint256 _securityPeriod
                            )
                                internal
                                returns (ILimitStorage.Limit memory)
                            {
                                ILimitStorage.Limit memory limit = _lStorage.getLimit(_wallet);
                                uint256 currentLimit = currentLimit(limit);
                                ILimitStorage.Limit memory newLimit;
                                if (_targetLimit <= currentLimit) {
                                    uint128 targetLimit = safe128(_targetLimit);
                                    newLimit = ILimitStorage.Limit(targetLimit, targetLimit, safe64(block.timestamp));
                                } else {
                                    newLimit = ILimitStorage.Limit(safe128(currentLimit), safe128(_targetLimit), safe64(block.timestamp.add(_securityPeriod)));
                                }
                                setLimit(_versionManager, _lStorage, _wallet, newLimit);
                                return newLimit;
                            }
                             /**
                             * @notice Disable the daily limit.
                             * The change is pending for the security period.
                             * @param _lStorage The storage contract.
                             * @param _versionManager The version manager.
                             * @param _wallet The target wallet.
                             * @param _securityPeriod The security period.
                             */
                            function disableLimit(
                                ILimitStorage _lStorage,
                                IVersionManager _versionManager,
                                address _wallet,
                                uint256 _securityPeriod
                            )
                                internal
                            {
                                changeLimit(_lStorage, _versionManager, _wallet, LIMIT_DISABLED, _securityPeriod);
                            }
                            /**
                            * @notice Returns whether the daily limit is disabled for a wallet.
                            * @param _wallet The target wallet.
                            * @return _limitDisabled true if the daily limit is disabled, false otherwise.
                            */
                            function isLimitDisabled(ILimitStorage _lStorage, address _wallet) internal view returns (bool) {
                                ILimitStorage.Limit memory limit = _lStorage.getLimit(_wallet);
                                uint256 currentLimit = currentLimit(limit);
                                return (currentLimit == LIMIT_DISABLED);
                            }
                            /**
                            * @notice Checks if a transfer is within the limit. If yes the daily spent is updated.
                            * @param _lStorage The storage contract.
                            * @param _versionManager The Version Manager.
                            * @param _wallet The target wallet.
                            * @param _amount The amount for the transfer
                            * @return true if the transfer is withing the daily limit.
                            */
                            function checkAndUpdateDailySpent(
                                ILimitStorage _lStorage,
                                IVersionManager _versionManager,
                                address _wallet,
                                uint256 _amount
                            )
                                internal
                                returns (bool)
                            {
                                (ILimitStorage.Limit memory limit, ILimitStorage.DailySpent memory dailySpent) = _lStorage.getLimitAndDailySpent(_wallet);
                                uint256 currentLimit = currentLimit(limit);
                                if (_amount == 0 || currentLimit == LIMIT_DISABLED) {
                                    return true;
                                }
                                ILimitStorage.DailySpent memory newDailySpent;
                                if (dailySpent.periodEnd <= block.timestamp && _amount <= currentLimit) {
                                    newDailySpent = ILimitStorage.DailySpent(safe128(_amount), safe64(block.timestamp + 24 hours));
                                    setDailySpent(_versionManager, _lStorage, _wallet, newDailySpent);
                                    return true;
                                } else if (dailySpent.periodEnd > block.timestamp && _amount.add(dailySpent.alreadySpent) <= currentLimit) {
                                    newDailySpent = ILimitStorage.DailySpent(safe128(_amount.add(dailySpent.alreadySpent)), safe64(dailySpent.periodEnd));
                                    setDailySpent(_versionManager, _lStorage, _wallet, newDailySpent);
                                    return true;
                                }
                                return false;
                            }
                            /**
                            * @notice Helper method to Reset the daily consumption.
                            * @param _versionManager The Version Manager.
                            * @param _wallet The target wallet.
                            */
                            function resetDailySpent(IVersionManager _versionManager, ILimitStorage limitStorage, address _wallet) internal {
                                setDailySpent(_versionManager, limitStorage, _wallet, ILimitStorage.DailySpent(uint128(0), uint64(0)));
                            }
                            /**
                            * @notice Helper method to get the ether value equivalent of a token amount.
                            * @notice For low value amounts of tokens we accept this to return zero as these are small enough to disregard.
                            * Note that the price stored for tokens = price for 1 token (in ETH wei) * 10^(18-token decimals).
                            * @param _amount The token amount.
                            * @param _token The address of the token.
                            * @return The ether value for _amount of _token.
                            */
                            function getEtherValue(ITokenPriceRegistry _priceRegistry, uint256 _amount, address _token) internal view returns (uint256) {
                                uint256 price = _priceRegistry.getTokenPrice(_token);
                                uint256 etherValue = price.mul(_amount).div(10**18);
                                return etherValue;
                            }
                            /**
                            * @notice Helper method to get the current limit from a Limit struct.
                            * @param _limit The limit struct
                            */
                            function currentLimit(ILimitStorage.Limit memory _limit) internal view returns (uint256) {
                                if (_limit.changeAfter > 0 && _limit.changeAfter < block.timestamp) {
                                    return _limit.pending;
                                }
                                return _limit.current;
                            }
                            function safe128(uint256 _num) internal pure returns (uint128) {
                                require(_num < 2**128, "LU: more then 128 bits");
                                return uint128(_num);
                            }
                            function safe64(uint256 _num) internal pure returns (uint64) {
                                require(_num < 2**64, "LU: more then 64 bits");
                                return uint64(_num);
                            }
                            // *************** Storage invocations in VersionManager ********************* //
                            function setLimit(
                                IVersionManager _versionManager,
                                ILimitStorage _lStorage,
                                address _wallet, 
                                ILimitStorage.Limit memory _limit
                            ) internal {
                                _versionManager.invokeStorage(
                                    _wallet,
                                    address(_lStorage),
                                    abi.encodeWithSelector(_lStorage.setLimit.selector, _wallet, _limit)
                                );
                            }
                            function setDailySpent(
                                IVersionManager _versionManager,
                                ILimitStorage _lStorage,
                                address _wallet, 
                                ILimitStorage.DailySpent memory _dailySpent
                            ) private {
                                _versionManager.invokeStorage(
                                    _wallet,
                                    address(_lStorage),
                                    abi.encodeWithSelector(_lStorage.setDailySpent.selector, _wallet, _dailySpent)
                                );
                            }
                        }// Copyright (C) 2020  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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity ^0.6.12;
                        /**
                         * @title Utils
                         * @notice Common utility methods used by modules.
                         */
                        library Utils {
                            /**
                            * @notice Helper method to recover 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
                                // solhint-disable-next-line 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);
                                address recoveredAddress = ecrecover(_signedHash, v, r, s);
                                require(recoveredAddress != address(0), "Utils: ecrecover returned 0");
                                return recoveredAddress;
                            }
                            /**
                            * @notice Helper method to parse data and extract the method signature.
                            */
                            function functionPrefix(bytes memory _data) internal pure returns (bytes4 prefix) {
                                require(_data.length >= 4, "RM: Invalid functionPrefix");
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    prefix := mload(add(_data, 0x20))
                                }
                            }
                            /**
                            * @notice 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;
                                }
                            }
                            function min(uint256 a, uint256 b) internal pure returns (uint256) {
                                if (a < b) {
                                    return a;
                                }
                                return b;
                            }
                        }
                        // 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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        pragma solidity >=0.5.4 <0.7.0;
                        /**
                         * @title IWallet
                         * @notice Interface for the BaseWallet
                         */
                        interface IWallet {
                            /**
                             * @notice Returns the wallet owner.
                             * @return The wallet owner address.
                             */
                            function owner() external view returns (address);
                            /**
                             * @notice Returns the number of authorised modules.
                             * @return The number of authorised modules.
                             */
                            function modules() external view returns (uint);
                            /**
                             * @notice Sets a new owner for the wallet.
                             * @param _newOwner The new owner.
                             */
                            function setOwner(address _newOwner) external;
                            /**
                             * @notice Checks if a module is authorised on the wallet.
                             * @param _module The module address to check.
                             * @return `true` if the module is authorised, otherwise `false`.
                             */
                            function authorised(address _module) external view returns (bool);
                            /**
                             * @notice Returns the module responsible for a static call redirection.
                             * @param _sig The signature of the static call.
                             * @return the module doing the redirection
                             */
                            function enabled(bytes4 _sig) external view returns (address);
                            /**
                             * @notice 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;
                            /**
                            * @notice 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;
                        }pragma solidity >=0.5.4 <0.7.0;
                        /**
                         * ERC20 contract interface.
                         */
                        interface ERC20 {
                            function totalSupply() external view returns (uint);
                            function decimals() external view returns (uint);
                            function balanceOf(address tokenOwner) external view returns (uint balance);
                            function allowance(address tokenOwner, address spender) external view returns (uint remaining);
                            function transfer(address to, uint tokens) external returns (bool success);
                            function approve(address spender, uint tokens) external returns (bool success);
                            function transferFrom(address from, address to, uint tokens) external returns (bool success);
                        }pragma solidity ^0.6.0;
                        /**
                         * @dev Wrappers over Solidity's arithmetic operations with added overflow
                         * checks.
                         *
                         * Arithmetic operations in Solidity wrap on overflow. This can easily result
                         * in bugs, because programmers usually assume that an overflow raises an
                         * error, which is the standard behavior in high level programming languages.
                         * `SafeMath` restores this intuition by reverting the transaction when an
                         * operation overflows.
                         *
                         * Using this library instead of the unchecked operations eliminates an entire
                         * class of bugs, so it's recommended to use it always.
                         */
                        library SafeMath {
                            /**
                             * @dev Returns the addition of two unsigned integers, reverting on
                             * overflow.
                             *
                             * Counterpart to Solidity's `+` operator.
                             *
                             * Requirements:
                             * - Addition cannot overflow.
                             */
                            function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                uint256 c = a + b;
                                require(c >= a, "SafeMath: addition overflow");
                                return c;
                            }
                            /**
                             * @dev Returns the subtraction of two unsigned integers, reverting on
                             * overflow (when the result is negative).
                             *
                             * Counterpart to Solidity's `-` operator.
                             *
                             * Requirements:
                             * - Subtraction cannot overflow.
                             */
                            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                return sub(a, b, "SafeMath: subtraction overflow");
                            }
                            /**
                             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                             * overflow (when the result is negative).
                             *
                             * Counterpart to Solidity's `-` operator.
                             *
                             * Requirements:
                             * - Subtraction cannot overflow.
                             */
                            function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                require(b <= a, errorMessage);
                                uint256 c = a - b;
                                return c;
                            }
                            /**
                             * @dev Returns the multiplication of two unsigned integers, reverting on
                             * overflow.
                             *
                             * Counterpart to Solidity's `*` operator.
                             *
                             * Requirements:
                             * - Multiplication cannot 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-contracts/pull/522
                                if (a == 0) {
                                    return 0;
                                }
                                uint256 c = a * b;
                                require(c / a == b, "SafeMath: multiplication overflow");
                                return c;
                            }
                            /**
                             * @dev Returns the integer division of two unsigned integers. Reverts on
                             * division by zero. The result is rounded towards zero.
                             *
                             * Counterpart to Solidity's `/` operator. Note: this function uses a
                             * `revert` opcode (which leaves remaining gas untouched) while Solidity
                             * uses an invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                return div(a, b, "SafeMath: division by zero");
                            }
                            /**
                             * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                             * division by zero. The result is rounded towards zero.
                             *
                             * Counterpart to Solidity's `/` operator. Note: this function uses a
                             * `revert` opcode (which leaves remaining gas untouched) while Solidity
                             * uses an invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                // Solidity only automatically asserts when dividing by 0
                                require(b > 0, errorMessage);
                                uint256 c = a / b;
                                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                                return c;
                            }
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                             * Reverts when dividing by zero.
                             *
                             * Counterpart to Solidity's `%` operator. This function uses a `revert`
                             * opcode (which leaves remaining gas untouched) while Solidity uses an
                             * invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                return mod(a, b, "SafeMath: modulo by zero");
                            }
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                             * Reverts with custom message when dividing by zero.
                             *
                             * Counterpart to Solidity's `%` operator. This function uses a `revert`
                             * opcode (which leaves remaining gas untouched) while Solidity uses an
                             * invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                require(b != 0, errorMessage);
                                return a % b;
                            }
                        }
                        

                        File 3 of 6: 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 6: VersionManager
                        pragma experimental ABIEncoderV2;
                        // File: contracts/modules/common/Utils.sol
                        // Copyright (C) 2020  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/>.
                        // SPDX-License-Identifier: GPL-3.0-only
                        /**
                         * @title Utils
                         * @notice Common utility methods used by modules.
                         */
                        library Utils {
                            /**
                            * @notice Helper method to recover 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
                                // solhint-disable-next-line 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);
                                address recoveredAddress = ecrecover(_signedHash, v, r, s);
                                require(recoveredAddress != address(0), "Utils: ecrecover returned 0");
                                return recoveredAddress;
                            }
                            /**
                            * @notice Helper method to parse data and extract the method signature.
                            */
                            function functionPrefix(bytes memory _data) internal pure returns (bytes4 prefix) {
                                require(_data.length >= 4, "RM: Invalid functionPrefix");
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    prefix := mload(add(_data, 0x20))
                                }
                            }
                            /**
                            * @notice 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;
                                }
                            }
                            function min(uint256 a, uint256 b) internal pure returns (uint256) {
                                if (a < b) {
                                    return a;
                                }
                                return b;
                            }
                        }
                        // File: contracts/infrastructure/base/Owned.sol
                        // 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 <0.7.0;
                        /**
                         * @title Owned
                         * @notice Basic contract to define an owner.
                         * @author Julien Niset - <julien@argent.xyz>
                         */
                        contract Owned {
                            // The owner
                            address public owner;
                            event OwnerChanged(address indexed _newOwner);
                            /**
                             * @notice Throws if the sender is not the owner.
                             */
                            modifier onlyOwner {
                                require(msg.sender == owner, "Must be owner");
                                _;
                            }
                            constructor() public {
                                owner = msg.sender;
                            }
                            /**
                             * @notice 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);
                            }
                        }
                        // File: contracts/infrastructure/storage/ITransferStorage.sol
                        // Copyright (C) 2020  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 <0.7.0;
                        /**
                         * @title ITransferStorage
                         * @notice TransferStorage interface
                         */
                        interface ITransferStorage {
                            function setWhitelist(address _wallet, address _target, uint256 _value) external;
                            function getWhitelist(address _wallet, address _target) external view returns (uint256);
                        }
                        // File: contracts/infrastructure/storage/IGuardianStorage.sol
                        // 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 <0.7.0;
                        interface IGuardianStorage {
                            /**
                             * @notice Lets an authorised module add a guardian to a wallet.
                             * @param _wallet The target wallet.
                             * @param _guardian The guardian to add.
                             */
                            function addGuardian(address _wallet, address _guardian) external;
                            /**
                             * @notice Lets an authorised module revoke a guardian from a wallet.
                             * @param _wallet The target wallet.
                             * @param _guardian The guardian to revoke.
                             */
                            function revokeGuardian(address _wallet, address _guardian) external;
                            /**
                             * @notice 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(address _wallet, address _guardian) external view returns (bool);
                            function isLocked(address _wallet) external view returns (bool);
                            function getLock(address _wallet) external view returns (uint256);
                            function getLocker(address _wallet) external view returns (address);
                            function setLock(address _wallet, uint256 _releaseAfter) external;
                            function getGuardians(address _wallet) external view returns (address[] memory);
                            function guardianCount(address _wallet) external view returns (uint256);
                        }
                        // File: contracts/modules/common/IModule.sol
                        // 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 <0.7.0;
                        /**
                         * @title IModule
                         * @notice 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 IModule {
                            /**
                             * @notice Inits a module for a wallet by e.g. setting some wallet specific parameters in storage.
                             * @param _wallet The wallet.
                             */
                            function init(address _wallet) external;
                            /**	
                             * @notice Adds a module to a wallet. Cannot execute when wallet is locked (or under recovery)	
                             * @param _wallet The target wallet.	
                             * @param _module The modules to authorise.	
                             */	
                            function addModule(address _wallet, address _module) external;
                        }
                        // File: @openzeppelin/contracts/math/SafeMath.sol
                        /**
                         * @dev Wrappers over Solidity's arithmetic operations with added overflow
                         * checks.
                         *
                         * Arithmetic operations in Solidity wrap on overflow. This can easily result
                         * in bugs, because programmers usually assume that an overflow raises an
                         * error, which is the standard behavior in high level programming languages.
                         * `SafeMath` restores this intuition by reverting the transaction when an
                         * operation overflows.
                         *
                         * Using this library instead of the unchecked operations eliminates an entire
                         * class of bugs, so it's recommended to use it always.
                         */
                        library SafeMath {
                            /**
                             * @dev Returns the addition of two unsigned integers, reverting on
                             * overflow.
                             *
                             * Counterpart to Solidity's `+` operator.
                             *
                             * Requirements:
                             * - Addition cannot overflow.
                             */
                            function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                uint256 c = a + b;
                                require(c >= a, "SafeMath: addition overflow");
                                return c;
                            }
                            /**
                             * @dev Returns the subtraction of two unsigned integers, reverting on
                             * overflow (when the result is negative).
                             *
                             * Counterpart to Solidity's `-` operator.
                             *
                             * Requirements:
                             * - Subtraction cannot overflow.
                             */
                            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                return sub(a, b, "SafeMath: subtraction overflow");
                            }
                            /**
                             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                             * overflow (when the result is negative).
                             *
                             * Counterpart to Solidity's `-` operator.
                             *
                             * Requirements:
                             * - Subtraction cannot overflow.
                             */
                            function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                require(b <= a, errorMessage);
                                uint256 c = a - b;
                                return c;
                            }
                            /**
                             * @dev Returns the multiplication of two unsigned integers, reverting on
                             * overflow.
                             *
                             * Counterpart to Solidity's `*` operator.
                             *
                             * Requirements:
                             * - Multiplication cannot 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-contracts/pull/522
                                if (a == 0) {
                                    return 0;
                                }
                                uint256 c = a * b;
                                require(c / a == b, "SafeMath: multiplication overflow");
                                return c;
                            }
                            /**
                             * @dev Returns the integer division of two unsigned integers. Reverts on
                             * division by zero. The result is rounded towards zero.
                             *
                             * Counterpart to Solidity's `/` operator. Note: this function uses a
                             * `revert` opcode (which leaves remaining gas untouched) while Solidity
                             * uses an invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                return div(a, b, "SafeMath: division by zero");
                            }
                            /**
                             * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                             * division by zero. The result is rounded towards zero.
                             *
                             * Counterpart to Solidity's `/` operator. Note: this function uses a
                             * `revert` opcode (which leaves remaining gas untouched) while Solidity
                             * uses an invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                // Solidity only automatically asserts when dividing by 0
                                require(b > 0, errorMessage);
                                uint256 c = a / b;
                                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                                return c;
                            }
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                             * Reverts when dividing by zero.
                             *
                             * Counterpart to Solidity's `%` operator. This function uses a `revert`
                             * opcode (which leaves remaining gas untouched) while Solidity uses an
                             * invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                return mod(a, b, "SafeMath: modulo by zero");
                            }
                            /**
                             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                             * Reverts with custom message when dividing by zero.
                             *
                             * Counterpart to Solidity's `%` operator. This function uses a `revert`
                             * opcode (which leaves remaining gas untouched) while Solidity uses an
                             * invalid opcode to revert (consuming all remaining gas).
                             *
                             * Requirements:
                             * - The divisor cannot be zero.
                             */
                            function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                require(b != 0, errorMessage);
                                return a % b;
                            }
                        }
                        // File: contracts/wallet/IWallet.sol
                        // 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 <0.7.0;
                        /**
                         * @title IWallet
                         * @notice Interface for the BaseWallet
                         */
                        interface IWallet {
                            /**
                             * @notice Returns the wallet owner.
                             * @return The wallet owner address.
                             */
                            function owner() external view returns (address);
                            /**
                             * @notice Returns the number of authorised modules.
                             * @return The number of authorised modules.
                             */
                            function modules() external view returns (uint);
                            /**
                             * @notice Sets a new owner for the wallet.
                             * @param _newOwner The new owner.
                             */
                            function setOwner(address _newOwner) external;
                            /**
                             * @notice Checks if a module is authorised on the wallet.
                             * @param _module The module address to check.
                             * @return `true` if the module is authorised, otherwise `false`.
                             */
                            function authorised(address _module) external view returns (bool);
                            /**
                             * @notice Returns the module responsible for a static call redirection.
                             * @param _sig The signature of the static call.
                             * @return the module doing the redirection
                             */
                            function enabled(bytes4 _sig) external view returns (address);
                            /**
                             * @notice 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;
                            /**
                            * @notice 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;
                        }
                        // File: contracts/infrastructure/IModuleRegistry.sol
                        // Copyright (C) 2020  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 <0.7.0;
                        /**
                         * @title IModuleRegistry
                         * @notice Interface for the registry of authorised modules.
                         */
                        interface IModuleRegistry {
                            function registerModule(address _module, bytes32 _name) external;
                            function deregisterModule(address _module) external;
                            function registerUpgrader(address _upgrader, bytes32 _name) external;
                            function deregisterUpgrader(address _upgrader) external;
                            function recoverToken(address _token) external;
                            function moduleInfo(address _module) external view returns (bytes32);
                            function upgraderInfo(address _upgrader) external view returns (bytes32);
                            function isRegisteredModule(address _module) external view returns (bool);
                            function isRegisteredModule(address[] calldata _modules) external view returns (bool);
                            function isRegisteredUpgrader(address _upgrader) external view returns (bool);
                        }
                        // File: contracts/infrastructure/storage/ILockStorage.sol
                        // 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 <0.7.0;
                        interface ILockStorage {
                            function isLocked(address _wallet) external view returns (bool);
                            function getLock(address _wallet) external view returns (uint256);
                            function getLocker(address _wallet) external view returns (address);
                            function setLock(address _wallet, address _locker, uint256 _releaseAfter) external;
                        }
                        // File: contracts/modules/common/IFeature.sol
                        // 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 <0.7.0;
                        /**
                         * @title IFeature
                         * @notice Interface for a Feature.
                         * @author Julien Niset - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                         */
                        interface IFeature {
                            enum OwnerSignature {
                                Anyone,             // Anyone
                                Required,           // Owner required
                                Optional,           // Owner and/or guardians
                                Disallowed          // guardians only
                            }
                            /**
                            * @notice Utility method to recover any ERC20 token that was sent to the Feature by mistake.
                            * @param _token The token to recover.
                            */
                            function recoverToken(address _token) external;
                            /**
                             * @notice Inits a Feature for a wallet by e.g. setting some wallet specific parameters in storage.
                             * @param _wallet The wallet.
                             */
                            function init(address _wallet) external;
                            /**
                             * @notice Helper method to check if an address is an authorised feature of a target wallet.
                             * @param _wallet The target wallet.
                             * @param _feature The address.
                             */
                            function isFeatureAuthorisedInVersionManager(address _wallet, address _feature) external view returns (bool);
                            /**
                            * @notice 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 and the wallet owner signature requirement.
                            */
                            function getRequiredSignatures(address _wallet, bytes calldata _data) external view returns (uint256, OwnerSignature);
                            /**
                            * @notice Gets the list of static call signatures that this feature responds to on behalf of wallets
                            */
                            function getStaticCallSignatures() external view returns (bytes4[] memory);
                        }
                        // File: lib/other/ERC20.sol
                        pragma solidity >=0.5.4 <0.7.0;
                        /**
                         * ERC20 contract interface.
                         */
                        interface ERC20 {
                            function totalSupply() external view returns (uint);
                            function decimals() external view returns (uint);
                            function balanceOf(address tokenOwner) external view returns (uint balance);
                            function allowance(address tokenOwner, address spender) external view returns (uint remaining);
                            function transfer(address to, uint tokens) external returns (bool success);
                            function approve(address spender, uint tokens) external returns (bool success);
                            function transferFrom(address from, address to, uint tokens) external returns (bool success);
                        }
                        // File: contracts/infrastructure/storage/ILimitStorage.sol
                        // 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 ILimitStorage
                         * @notice LimitStorage interface
                         */
                        interface ILimitStorage {
                            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;
                            }
                            function setLimit(address _wallet, Limit memory _limit) external;
                            function getLimit(address _wallet) external view returns (Limit memory _limit);
                            function setDailySpent(address _wallet, DailySpent memory _dailySpent) external;
                            function getDailySpent(address _wallet) external view returns (DailySpent memory _dailySpent);
                            function setLimitAndDailySpent(address _wallet, Limit memory _limit, DailySpent memory _dailySpent) external;
                            function getLimitAndDailySpent(address _wallet) external view returns (Limit memory _limit, DailySpent memory _dailySpent);
                        }
                        // File: contracts/modules/common/IVersionManager.sol
                        // 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 <0.7.0;
                        /**
                         * @title IVersionManager
                         * @notice Interface for the VersionManager module.
                         * @author Olivier VDB - <olivier@argent.xyz>
                         */
                        interface IVersionManager {
                            /**
                             * @notice Returns true if the feature is authorised for the wallet
                             * @param _wallet The target wallet.
                             * @param _feature The feature.
                             */
                            function isFeatureAuthorised(address _wallet, address _feature) external view returns (bool);
                            /**
                             * @notice Lets a feature (caller) 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 checkAuthorisedFeatureAndInvokeWallet(
                                address _wallet,
                                address _to,
                                uint256 _value,
                                bytes calldata _data
                            ) external returns (bytes memory _res);
                            /* ******* Backward Compatibility with old Storages and BaseWallet *************** */
                            /**
                             * @notice Sets a new owner for the wallet.
                             * @param _newOwner The new owner.
                             */
                            function setOwner(address _wallet, address _newOwner) external;
                            /**
                             * @notice Lets a feature write data to a storage contract.
                             * @param _wallet The target wallet.
                             * @param _storage The storage contract.
                             * @param _data The data of the call
                             */
                            function invokeStorage(address _wallet, address _storage, bytes calldata _data) external;
                            /**
                             * @notice Upgrade a wallet to a new version.
                             * @param _wallet the wallet to upgrade
                             * @param _toVersion the new version
                             */
                            function upgradeWallet(address _wallet, uint256 _toVersion) external;
                        }
                        // File: contracts/modules/common/BaseFeature.sol
                        // 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.s
                        // 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 BaseFeature
                         * @notice Base Feature contract that contains methods common to all Feature contracts.
                         * @author Julien Niset - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                         */
                        contract BaseFeature is IFeature {
                            // Empty calldata
                            bytes constant internal EMPTY_BYTES = "";
                            // Mock token address for ETH
                            address constant internal ETH_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                            // The address of the Lock storage
                            ILockStorage internal lockStorage;
                            // The address of the Version Manager
                            IVersionManager internal versionManager;
                            event FeatureCreated(bytes32 name);
                            /**
                             * @notice Throws if the wallet is locked.
                             */
                            modifier onlyWhenUnlocked(address _wallet) {
                                require(!lockStorage.isLocked(_wallet), "BF: wallet locked");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not the VersionManager.
                             */
                            modifier onlyVersionManager() {
                                require(msg.sender == address(versionManager), "BF: caller must be VersionManager");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not the owner of the target wallet.
                             */
                            modifier onlyWalletOwner(address _wallet) {
                                require(isOwner(_wallet, msg.sender), "BF: must be wallet owner");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not an authorised feature of the target wallet.
                             */
                            modifier onlyWalletFeature(address _wallet) {
                                require(versionManager.isFeatureAuthorised(_wallet, msg.sender), "BF: must be a wallet feature");
                                _;
                            }
                            /**
                             * @notice Throws if the sender is not the owner of the target wallet or the feature itself.
                             */
                            modifier onlyWalletOwnerOrFeature(address _wallet) {
                                // Wrapping in an internal method reduces deployment cost by avoiding duplication of inlined code
                                verifyOwnerOrAuthorisedFeature(_wallet, msg.sender);
                                _;
                            }
                            constructor(
                                ILockStorage _lockStorage,
                                IVersionManager _versionManager,
                                bytes32 _name
                            ) public {
                                lockStorage = _lockStorage;
                                versionManager = _versionManager;
                                emit FeatureCreated(_name);
                            }
                            /**
                            * @inheritdoc IFeature
                            */
                            function recoverToken(address _token) external virtual override {
                                uint total = ERC20(_token).balanceOf(address(this));
                                _token.call(abi.encodeWithSelector(ERC20(_token).transfer.selector, address(versionManager), total));
                            }
                            /**
                             * @notice Inits the feature for a wallet by doing nothing.
                             * @dev !! Overriding methods need make sure `init()` can only be called by the VersionManager !!
                             * @param _wallet The wallet.
                             */
                            function init(address _wallet) external virtual override  {}
                            /**
                             * @inheritdoc IFeature
                             */
                            function getRequiredSignatures(address, bytes calldata) external virtual view override returns (uint256, OwnerSignature) {
                                revert("BF: disabled method");
                            }
                            /**
                             * @inheritdoc IFeature
                             */
                            function getStaticCallSignatures() external virtual override view returns (bytes4[] memory _sigs) {}
                            /**
                             * @inheritdoc IFeature
                             */
                            function isFeatureAuthorisedInVersionManager(address _wallet, address _feature) public override view returns (bool) {
                                return versionManager.isFeatureAuthorised(_wallet, _feature);
                            }
                            /**
                            * @notice Checks that the wallet address provided as the first parameter of _data matches _wallet
                            * @return false if the addresses are different.
                            */
                            function verifyData(address _wallet, bytes calldata _data) internal pure returns (bool) {
                                require(_data.length >= 36, "RM: Invalid dataWallet");
                                address dataWallet = abi.decode(_data[4:], (address));
                                return dataWallet == _wallet;
                            }
                             /**
                             * @notice 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(address _wallet, address _addr) internal view returns (bool) {
                                return IWallet(_wallet).owner() == _addr;
                            }
                            /**
                             * @notice Verify that the caller is an authorised feature or the wallet owner.
                             * @param _wallet The target wallet.
                             * @param _sender The caller.
                             */
                            function verifyOwnerOrAuthorisedFeature(address _wallet, address _sender) internal view {
                                require(isFeatureAuthorisedInVersionManager(_wallet, _sender) || isOwner(_wallet, _sender), "BF: must be owner or feature");
                            }
                            /**
                             * @notice 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) 
                            {
                                _res = versionManager.checkAuthorisedFeatureAndInvokeWallet(_wallet, _to, _value, _data);
                            }
                        }
                        // File: modules/VersionManager.sol
                        // 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 VersionManager
                         * @notice Intermediate contract between features and wallets. VersionManager checks that a calling feature is
                         * authorised for the wallet and if so, forwards the call to it. Note that VersionManager is meant to be the only
                         * module authorised on a wallet and because some of its methods need to be called by the RelayerManager feature,
                         * the VersionManager is both a module AND a feature.
                         * @author Olivier VDB <olivier@argent.xyz>
                         */
                        contract VersionManager is IVersionManager, IModule, BaseFeature, Owned {
                            bytes32 constant NAME = "VersionManager";
                            bytes4 constant internal ADD_MODULE_PREFIX = bytes4(keccak256("addModule(address,address)"));
                            bytes4 constant internal UPGRADE_WALLET_PREFIX = bytes4(keccak256("upgradeWallet(address,uint256)"));
                            // Last bundle version
                            uint256 public lastVersion;
                            // Minimum allowed version
                            uint256 public minVersion = 1;
                            // Current bundle version for a wallet
                            mapping(address => uint256) public walletVersions; // [wallet] => [version]
                            // Features per version
                            mapping(address => mapping(uint256 => bool)) public isFeatureInVersion; // [feature][version] => bool
                            // Features requiring initialization for a wallet
                            mapping(uint256 => address[]) public featuresToInit; // [version] => [features]
                            // Supported static call signatures
                            mapping(uint256 => bytes4[]) public staticCallSignatures; // [version] => [sigs]
                            // Features executing static calls
                            mapping(uint256 => mapping(bytes4 => address)) public staticCallExecutors; // [version][sig] => [feature]
                            // Authorised Storages
                            mapping(address => bool) public isStorage; // [storage] => bool
                            event VersionAdded(uint256 _version, address[] _features);
                            event WalletUpgraded(address indexed _wallet, uint256 _version);
                            // The Module Registry
                            IModuleRegistry private registry;
                            /* ***************** Constructor ************************* */
                            constructor(
                                IModuleRegistry _registry,
                                ILockStorage _lockStorage,
                                IGuardianStorage _guardianStorage,
                                ITransferStorage _transferStorage,
                                ILimitStorage _limitStorage
                            )
                                BaseFeature(_lockStorage, IVersionManager(address(this)), NAME)
                                public
                            {
                                registry = _registry;
                                // Add initial storages
                                if(address(_lockStorage) != address(0)) { 
                                    addStorage(address(_lockStorage));
                                }
                                if(address(_guardianStorage) != address(0)) { 
                                    addStorage(address(_guardianStorage));
                                }
                                if(address(_transferStorage) != address(0)) {
                                    addStorage(address(_transferStorage));
                                }
                                if(address(_limitStorage) != address(0)) {
                                    addStorage(address(_limitStorage));
                                }
                            }
                            /* ***************** onlyOwner ************************* */
                            /**
                             * @inheritdoc IFeature
                             */
                            function recoverToken(address _token) external override onlyOwner {
                                uint total = ERC20(_token).balanceOf(address(this));
                                _token.call(abi.encodeWithSelector(ERC20(_token).transfer.selector, msg.sender, total));
                            }
                            /**
                             * @notice Lets the owner change the minimum allowed version
                             * @param _minVersion the minimum allowed version
                             */
                            function setMinVersion(uint256 _minVersion) external onlyOwner {
                                require(_minVersion > 0 && _minVersion <= lastVersion, "VM: invalid _minVersion");
                                minVersion = _minVersion;
                            }
                            /**
                             * @notice Lets the owner add a new version, i.e. a new bundle of features.
                             * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                             * WARNING: if a feature was added to a version and later on removed from a subsequent version,
                             * the feature may no longer be used in any future version without first being redeployed.
                             * Otherwise, the feature could be initialized more than once.
                             * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                             * @param _features the list of features included in the new version
                             * @param _featuresToInit the subset of features that need to be initialized for a wallet
                             */
                            function addVersion(address[] calldata _features, address[] calldata _featuresToInit) external onlyOwner {
                                uint256 newVersion = ++lastVersion;
                                for(uint256 i = 0; i < _features.length; i++) {
                                    isFeatureInVersion[_features[i]][newVersion] = true;
                                    // Store static call information to optimise its use by wallets
                                    bytes4[] memory sigs = IFeature(_features[i]).getStaticCallSignatures();
                                    for(uint256 j = 0; j < sigs.length; j++) {
                                        staticCallSignatures[newVersion].push(sigs[j]);
                                        staticCallExecutors[newVersion][sigs[j]] = _features[i];
                                    }
                                }
                                // Sanity check
                                for(uint256 i = 0; i < _featuresToInit.length; i++) {
                                    require(isFeatureInVersion[_featuresToInit[i]][newVersion], "VM: invalid _featuresToInit");
                                }
                                featuresToInit[newVersion] = _featuresToInit;
                                emit VersionAdded(newVersion, _features);
                            }
                            /**
                             * @notice Lets the owner add a storage contract
                             * @param _storage the storage contract to add
                             */
                            function addStorage(address _storage) public onlyOwner {
                                require(!isStorage[_storage], "VM: storage already added");
                                isStorage[_storage] = true;
                            }
                            /* ***************** View Methods ************************* */
                            /**
                             * @inheritdoc IVersionManager
                             */
                            function isFeatureAuthorised(address _wallet, address _feature) external view override returns (bool) {
                                // Note that the VersionManager is the only feature that isn't stored in isFeatureInVersion
                                return _isFeatureAuthorisedForWallet(_wallet, _feature) || _feature == address(this);
                            }
                            /**
                             * @inheritdoc IFeature
                             */
                            function getRequiredSignatures(address /* _wallet */, bytes calldata _data) external view override returns (uint256, OwnerSignature) {
                                bytes4 methodId = Utils.functionPrefix(_data);
                                // This require ensures that the RelayerManager cannot be used to call a featureOnly VersionManager method
                                // that calls a Storage or the BaseWallet for backward-compatibility reason
                                require(methodId == UPGRADE_WALLET_PREFIX || methodId == ADD_MODULE_PREFIX, "VM: unknown method");     
                                return (1, OwnerSignature.Required);
                            }
                            /* ***************** Static Call Delegation ************************* */
                            /**
                             * @notice This method is used by the VersionManager's fallback (via an internal call) to determine whether
                             * the current transaction is a staticcall or not. The method succeeds if the current transaction is a static call, 
                             * and reverts otherwise. 
                             * @dev The use of an if/else allows to encapsulate the whole logic in a single function.
                             */
                            function verifyStaticCall() public {
                                if(msg.sender != address(this)) { // first entry in the method (via an internal call)
                                    (bool success,) = address(this).call{gas: 3000}(abi.encodeWithSelector(VersionManager(0).verifyStaticCall.selector));
                                    require(!success, "VM: not in a staticcall");
                                } else { // second entry in the method (via an external call)
                                    // solhint-disable-next-line no-inline-assembly
                                    assembly { log0(0, 0) }
                                }
                            }
                            /**
                             * @notice This method delegates the static call to a target feature
                             */
                            fallback() external {
                                uint256 version = walletVersions[msg.sender];
                                address feature = staticCallExecutors[version][msg.sig];
                                require(feature != address(0), "VM: static call not supported for wallet version");
                                verifyStaticCall();
                                // solhint-disable-next-line no-inline-assembly
                                assembly {
                                    calldatacopy(0, 0, calldatasize())
                                    let result := delegatecall(gas(), feature, 0, calldatasize(), 0, 0)
                                    returndatacopy(0, 0, returndatasize())
                                    switch result
                                    case 0 {revert(0, returndatasize())}
                                    default {return (0, returndatasize())}
                                }
                            }
                            /* ***************** Wallet Upgrade ************************* */
                            /**
                             * @inheritdoc IFeature
                             */
                            function init(address _wallet) public override(IModule, BaseFeature) {}
                            /**
                             * @inheritdoc IVersionManager
                             */
                            function upgradeWallet(address _wallet, uint256 _toVersion) external override onlyWhenUnlocked(_wallet) {
                                require(
                                    // Upgrade triggered by the RelayerManager (from version v>=1 to version v'>v)
                                    _isFeatureAuthorisedForWallet(_wallet, msg.sender) ||
                                    // Upgrade triggered by WalletFactory or UpgraderToVersionManager (from version v=0 to version v'>0)
                                    IWallet(_wallet).authorised(msg.sender) ||
                                    // Upgrade triggered directly by the owner (from version v>=1 to version v'>v)
                                    isOwner(_wallet, msg.sender), 
                                    "VM: sender may not upgrade wallet"
                                );
                                uint256 fromVersion = walletVersions[_wallet];
                                uint256 minVersion_ = minVersion;
                                uint256 toVersion;
                                if(_toVersion < minVersion_ && fromVersion == 0 && IWallet(_wallet).modules() == 2) {
                                    // When the caller is the WalletFactory, we automatically change toVersion to minVersion if needed.
                                    // Note that when fromVersion == 0, the caller could be the WalletFactory or the UpgraderToVersionManager. 
                                    // The WalletFactory will be the only possible caller when the wallet has only 2 authorised modules 
                                    // (that number would be >= 3 for a call from the UpgraderToVersionManager)
                                    toVersion = minVersion_;
                                } else {
                                    toVersion = _toVersion;
                                }
                                require(toVersion >= minVersion_ && toVersion <= lastVersion, "VM: invalid _toVersion");
                                require(fromVersion < toVersion, "VM: already on new version");
                                walletVersions[_wallet] = toVersion;
                                // Setup static call redirection
                                bytes4[] storage sigs = staticCallSignatures[toVersion];
                                for(uint256 i = 0; i < sigs.length; i++) {
                                    bytes4 sig = sigs[i];
                                    if(IWallet(_wallet).enabled(sig) != address(this)) {
                                        IWallet(_wallet).enableStaticCall(address(this), sig);
                                    }
                                }
                                // Init features
                                address[] storage featuresToInitInToVersion = featuresToInit[toVersion];
                                for(uint256 i = 0; i < featuresToInitInToVersion.length; i++) {
                                    address feature = featuresToInitInToVersion[i];
                                    // We only initialize a feature that was not already initialized in the previous version
                                    if(fromVersion == 0 || !isFeatureInVersion[feature][fromVersion]) {
                                        IFeature(feature).init(_wallet);
                                    }
                                }
                                emit WalletUpgraded(_wallet, toVersion);
                            }
                            /**
                             * @inheritdoc IModule
                             */
                            function addModule(address _wallet, address _module) external override onlyWalletOwnerOrFeature(_wallet) onlyWhenUnlocked(_wallet) {
                                require(registry.isRegisteredModule(_module), "VM: module is not registered");
                                IWallet(_wallet).authoriseModule(_module, true);
                            }
                            /* ******* Backward Compatibility with old Storages and BaseWallet *************** */
                            /**
                             * @inheritdoc IVersionManager
                             */
                            function checkAuthorisedFeatureAndInvokeWallet(
                                address _wallet, 
                                address _to, 
                                uint256 _value, 
                                bytes memory _data
                            ) 
                                external 
                                override
                                returns (bytes memory _res) 
                            {
                                require(_isFeatureAuthorisedForWallet(_wallet, msg.sender), "VM: sender may not invoke wallet");
                                bool success;
                                (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) {
                                    // solhint-disable-next-line no-inline-assembly
                                    assembly {
                                        returndatacopy(0, 0, returndatasize())
                                        revert(0, returndatasize())
                                    }
                                } else if (!success) {
                                    revert("VM: wallet invoke reverted");
                                }
                            }
                            /**
                             * @inheritdoc IVersionManager
                             */
                            function invokeStorage(address _wallet, address _storage, bytes calldata _data) external override {
                                require(_isFeatureAuthorisedForWallet(_wallet, msg.sender), "VM: sender may not invoke storage");
                                require(verifyData(_wallet, _data), "VM: target of _data != _wallet");
                                require(isStorage[_storage], "VM: invalid storage invoked");
                                (bool success,) = _storage.call(_data);
                                require(success, "VM: _storage failed");
                            }
                            /**
                             * @inheritdoc IVersionManager
                             */
                            function setOwner(address _wallet, address _newOwner) external override {
                                require(_isFeatureAuthorisedForWallet(_wallet, msg.sender), "VM: sender should be authorized feature");
                                IWallet(_wallet).setOwner(_newOwner);
                            }
                            /* ***************** Internal Methods ************************* */
                            function _isFeatureAuthorisedForWallet(address _wallet, address _feature) private view returns (bool) {
                                return isFeatureInVersion[_feature][walletVersions[_wallet]];
                            }
                        }

                        File 5 of 6: 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 6 of 6: LockStorage
                        {"BaseWallet.sol":{"content":"// Copyright (C) 2018  Argent Labs Ltd. \u003chttps://argent.xyz\u003e\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\n// SPDX-License-Identifier: GPL-3.0-only\npragma solidity ^0.6.12;\n\nimport \"./IModule.sol\";\nimport \"./IWallet.sol\";\n\n/**\n * @title BaseWallet\n * @notice Simple modular wallet that authorises modules to call its invoke() method.\n * @author Julien Niset - \u003cjulien@argent.xyz\u003e\n */\ncontract BaseWallet is IWallet {\n\n    // The implementation of the proxy\n    address public implementation;\n    // The owner\n    address public override owner;\n    // The authorised modules\n    mapping (address =\u003e bool) public override authorised;\n    // The enabled static calls\n    mapping (bytes4 =\u003e address) public override enabled;\n    // The number of modules\n    uint public override modules;\n\n    event AuthorisedModule(address indexed module, bool value);\n    event EnabledStaticCall(address indexed module, bytes4 indexed method);\n    event Invoked(address indexed module, address indexed target, uint indexed value, bytes data);\n    event Received(uint indexed value, address indexed sender, bytes data);\n    event OwnerChanged(address owner);\n\n    /**\n     * @notice Throws if the sender is not an authorised module.\n     */\n    modifier moduleOnly {\n        require(authorised[msg.sender], \"BW: msg.sender not an authorized module\");\n        _;\n    }\n\n    /**\n     * @notice Inits the wallet by setting the owner and authorising a list of modules.\n     * @param _owner The owner.\n     * @param _modules The modules to authorise.\n     */\n    function init(address _owner, address[] calldata _modules) external {\n        require(owner == address(0) \u0026\u0026 modules == 0, \"BW: wallet already initialised\");\n        require(_modules.length \u003e 0, \"BW: construction requires at least 1 module\");\n        owner = _owner;\n        modules = _modules.length;\n        for (uint256 i = 0; i \u003c _modules.length; i++) {\n            require(authorised[_modules[i]] == false, \"BW: module is already added\");\n            authorised[_modules[i]] = true;\n            IModule(_modules[i]).init(address(this));\n            emit AuthorisedModule(_modules[i], true);\n        }\n        if (address(this).balance \u003e 0) {\n            emit Received(address(this).balance, address(0), \"\");\n        }\n    }\n\n    /**\n     * @inheritdoc IWallet\n     */\n    function authoriseModule(address _module, bool _value) external override moduleOnly {\n        if (authorised[_module] != _value) {\n            emit AuthorisedModule(_module, _value);\n            if (_value == true) {\n                modules += 1;\n                authorised[_module] = true;\n                IModule(_module).init(address(this));\n            } else {\n                modules -= 1;\n                require(modules \u003e 0, \"BW: wallet must have at least one module\");\n                delete authorised[_module];\n            }\n        }\n    }\n\n    /**\n    * @inheritdoc IWallet\n    */\n    function enableStaticCall(address _module, bytes4 _method) external override moduleOnly {\n        require(authorised[_module], \"BW: must be an authorised module for static call\");\n        enabled[_method] = _module;\n        emit EnabledStaticCall(_module, _method);\n    }\n\n    /**\n     * @inheritdoc IWallet\n     */\n    function setOwner(address _newOwner) external override moduleOnly {\n        require(_newOwner != address(0), \"BW: address cannot be null\");\n        owner = _newOwner;\n        emit OwnerChanged(_newOwner);\n    }\n\n    /**\n     * @notice Performs a generic transaction.\n     * @param _target The address for the transaction.\n     * @param _value The value of the transaction.\n     * @param _data The data of the transaction.\n     */\n    function invoke(address _target, uint _value, bytes calldata _data) external moduleOnly returns (bytes memory _result) {\n        bool success;\n        (success, _result) = _target.call{value: _value}(_data);\n        if (!success) {\n            // solhint-disable-next-line no-inline-assembly\n            assembly {\n                returndatacopy(0, 0, returndatasize())\n                revert(0, returndatasize())\n            }\n        }\n        emit Invoked(msg.sender, _target, _value, _data);\n    }\n\n    /**\n     * @notice This method delegates the static call to a target contract if the data corresponds\n     * to an enabled module, or logs the call otherwise.\n     */\n    fallback() external payable {\n        address module = enabled[msg.sig];\n        if (module == address(0)) {\n            emit Received(msg.value, msg.sender, msg.data);\n        } else {\n            require(authorised[module], \"BW: must be an authorised module for static call\");\n\n            // solhint-disable-next-line no-inline-assembly\n            assembly {\n                calldatacopy(0, 0, calldatasize())\n                let result := staticcall(gas(), module, 0, calldatasize(), 0, 0)\n                returndatacopy(0, 0, returndatasize())\n                switch result\n                case 0 {revert(0, returndatasize())}\n                default {return (0, returndatasize())}\n            }\n        }\n    }\n\n    receive() external payable {\n    }\n}"},"ILockStorage.sol":{"content":"// Copyright (C) 2018  Argent Labs Ltd. \u003chttps://argent.xyz\u003e\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\n// SPDX-License-Identifier: GPL-3.0-only\npragma solidity \u003e=0.5.4 \u003c0.7.0;\n\ninterface ILockStorage {\n    function isLocked(address _wallet) external view returns (bool);\n\n    function getLock(address _wallet) external view returns (uint256);\n\n    function getLocker(address _wallet) external view returns (address);\n\n    function setLock(address _wallet, address _locker, uint256 _releaseAfter) external;\n}"},"IModule.sol":{"content":"// Copyright (C) 2018  Argent Labs Ltd. \u003chttps://argent.xyz\u003e\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\n// SPDX-License-Identifier: GPL-3.0-only\npragma solidity \u003e=0.5.4 \u003c0.7.0;\n\n/**\n * @title IModule\n * @notice Interface for a module.\n * A module MUST implement the addModule() method to ensure that a wallet with at least one module\n * can never end up in a \"frozen\" state.\n * @author Julien Niset - \u003cjulien@argent.xyz\u003e\n */\ninterface IModule {\n    /**\n     * @notice Inits a module for a wallet by e.g. setting some wallet specific parameters in storage.\n     * @param _wallet The wallet.\n     */\n    function init(address _wallet) external;\n\n    /**\t\n     * @notice Adds a module to a wallet. Cannot execute when wallet is locked (or under recovery)\t\n     * @param _wallet The target wallet.\t\n     * @param _module The modules to authorise.\t\n     */\t\n    function addModule(address _wallet, address _module) external;\n}"},"IWallet.sol":{"content":"// Copyright (C) 2018  Argent Labs Ltd. \u003chttps://argent.xyz\u003e\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\n// SPDX-License-Identifier: GPL-3.0-only\npragma solidity \u003e=0.5.4 \u003c0.7.0;\n\n/**\n * @title IWallet\n * @notice Interface for the BaseWallet\n */\ninterface IWallet {\n    /**\n     * @notice Returns the wallet owner.\n     * @return The wallet owner address.\n     */\n    function owner() external view returns (address);\n\n    /**\n     * @notice Returns the number of authorised modules.\n     * @return The number of authorised modules.\n     */\n    function modules() external view returns (uint);\n\n    /**\n     * @notice Sets a new owner for the wallet.\n     * @param _newOwner The new owner.\n     */\n    function setOwner(address _newOwner) external;\n\n    /**\n     * @notice Checks if a module is authorised on the wallet.\n     * @param _module The module address to check.\n     * @return `true` if the module is authorised, otherwise `false`.\n     */\n    function authorised(address _module) external view returns (bool);\n\n    /**\n     * @notice Returns the module responsible for a static call redirection.\n     * @param _sig The signature of the static call.\n     * @return the module doing the redirection\n     */\n    function enabled(bytes4 _sig) external view returns (address);\n\n    /**\n     * @notice Enables/Disables a module.\n     * @param _module The target module.\n     * @param _value Set to `true` to authorise the module.\n     */\n    function authoriseModule(address _module, bool _value) external;\n\n    /**\n    * @notice Enables a static method by specifying the target module to which the call must be delegated.\n    * @param _module The target module.\n    * @param _method The static method signature.\n    */\n    function enableStaticCall(address _module, bytes4 _method) external;\n}"},"LockStorage.sol":{"content":"// Copyright (C) 2018  Argent Labs Ltd. \u003chttps://argent.xyz\u003e\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\npragma solidity ^0.6.12;\nimport \"./BaseWallet.sol\";\nimport \"./Storage.sol\";\nimport \"./ILockStorage.sol\";\n\n/**\n * @title LockStorage\n * @dev Contract storing the state of wallets related to guardians and lock.\n * The contract only defines basic setters and getters with no logic. Only modules authorised\n * for a wallet can modify its state.\n * @author Julien Niset - \u003cjulien@argent.xyz\u003e\n * @author Olivier Van Den Biggelaar - \u003colivier@argent.xyz\u003e\n */\ncontract LockStorage is ILockStorage, Storage {\n\n    struct LockStorageConfig {\n        // the lock\u0027s release timestamp\n        uint256 lock;\n        // the module that set the last lock\n        address locker;\n    }\n    \n    // wallet specific storage\n    mapping (address =\u003e LockStorageConfig) internal configs;\n\n    // *************** External Functions ********************* //\n\n    /**\n     * @dev Lets an authorised module set the lock for a wallet.\n     * @param _wallet The target wallet.\n     * @param _locker The feature doing the lock.\n     * @param _releaseAfter The epoch time at which the lock should automatically release.\n     */\n    function setLock(address _wallet, address _locker, uint256 _releaseAfter) external override onlyModule(_wallet) {\n        configs[_wallet].lock = _releaseAfter;\n        if (_releaseAfter != 0 \u0026\u0026 _locker != configs[_wallet].locker) {\n            configs[_wallet].locker = _locker;\n        }\n    }\n\n    /**\n     * @dev Checks if the lock is set for a wallet.\n     * @param _wallet The target wallet.\n     * @return true if the lock is set for the wallet.\n     */\n    function isLocked(address _wallet) external view override returns (bool) {\n        return configs[_wallet].lock \u003e now;\n    }\n\n    /**\n     * @dev Gets the time at which the lock of a wallet will release.\n     * @param _wallet The target wallet.\n     * @return the time at which the lock of a wallet will release, or zero if there is no lock set.\n     */\n    function getLock(address _wallet) external view override returns (uint256) {\n        return configs[_wallet].lock;\n    }\n\n    /**\n     * @dev Gets the address of the last module that modified the lock for a wallet.\n     * @param _wallet The target wallet.\n     * @return the address of the last module that modified the lock for a wallet.\n     */\n    function getLocker(address _wallet) external view override returns (address) {\n        return configs[_wallet].locker;\n    }\n}"},"Storage.sol":{"content":"// Copyright (C) 2018  Argent Labs Ltd. \u003chttps://argent.xyz\u003e\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see \u003chttp://www.gnu.org/licenses/\u003e.\n\n// SPDX-License-Identifier: GPL-3.0-only\npragma solidity \u003e=0.5.4 \u003c0.7.0;\n\nimport \"./IWallet.sol\";\n\n/**\n * @title Storage\n * @notice Base contract for the storage of a wallet.\n * @author Julien Niset - \u003cjulien@argent.xyz\u003e\n */\ncontract Storage {\n\n    /**\n     * @notice Throws if the caller is not an authorised module.\n     */\n    modifier onlyModule(address _wallet) {\n        require(IWallet(_wallet).authorised(msg.sender), \"TS: must be an authorized module to call this method\");\n        _;\n    }\n}"}}