ETH Price: $1,874.45 (+0.43%)
Gas: 0.56 Gwei

Transaction Decoder

Block:
12031617 at Mar-13-2021 05:30:56 PM +UTC
Transaction Fee:
0.0334163254 ETH $62.64
Gas Used:
202,646 Gas / 164.9 Gwei

Emitted Events:

104 Proxy.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x000000000000000000000000645ba45dbe3c6942c812a46f9ee8115c89b524ec, 0x0000000000000000000000004132b0d5153cc305a23334e47b318142e2d74402, 0x000000000000000000000000000000000000000000000000013fbe85edc90000, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000000 )
105 TransferManager.Transfer( wallet=Proxy, token=0xEeeeeEee...eeeeeEEeE, amount=90000000000000000, to=0x4132b0d5153cc305a23334e47b318142e2d74402, data=0x )
106 Proxy.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x000000000000000000000000645ba45dbe3c6942c812a46f9ee8115c89b524ec, 0x000000000000000000000000482579f93dc13e6b434e38b5a0447ca543d88a46, 0x000000000000000000000000000000000000000000000000003578b010f91000, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000000 )
107 RelayerManager.Refund( wallet=Proxy, refundAddress=0x482579f93dc13e6b434e38b5a0447ca543d88a46, refundToken=0xEeeeeEee...eeeeeEEeE, refundAmount=15050871360000000 )
108 RelayerManager.TransactionExecuted( wallet=Proxy, success=True, returnData=0x, signedHash=BA36390A0CB3327F89D3DB1DA4B01FFB6E3EB45614BBB4044AB29C95213AE8E1 )

Account State Difference:

  Address   Before After State Difference Code
0x045B32ef...Ad7299a55
0x10A0847c...493630032
(Argent: Relayer Manager)
0x4132B0d5...2E2d74402 0.074850228406717883 Eth0.164850228406717883 Eth0.09
0x482579F9...543D88A46 9.518614509302182551 Eth9.533665380662182551 Eth0.01505087136
(Spark Pool)
1.120581246532887401 Eth1.153997571932887401 Eth0.0334163254
0x77522581...6931c056f 0.1116 Eth0.00654912864 Eth0.10505087136
0xF27696C8...f59342fA6
(Argent: Relayer 3)
3.644682226409002438 Eth
Nonce: 181950
3.611265901009002438 Eth
Nonce: 181951
0.0334163254

Execution Trace

RelayerManager.execute( _wallet=0x775225818bA851948733A611922A8916931c056f, _feature=0x5094a8f54B12AEc540bF7cCd0Dd7B62f4FecF7f2, _data=0x2DF546F4000000000000000000000000775225818BA851948733A611922A8916931C056F000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE0000000000000000000000004132B0D5153CC305A23334E47B318142E2D74402000000000000000000000000000000000000000000000000013FBE85EDC9000000000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000000, _nonce=4094135541045725561052059049407736074510070947, _signatures=0xD50497B93E28AEA87510784F0FBFD89D319E5FD601ECEC1630DB82BA4FB94B184B0B5B43DCF3F42D5E74CFF610D70F80D79FDE519E9F4855F5CA2B94EB4C51CB1C, _gasPrice=141120000000, _gasLimit=106653, _refundToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, _refundAddress=0x482579F93dC13e6B434E38b5a0447ca543D88A46 ) => ( True )
  • VersionManager.isFeatureAuthorised( _wallet=0x775225818bA851948733A611922A8916931c056f, _feature=0x5094a8f54B12AEc540bF7cCd0Dd7B62f4FecF7f2 ) => ( True )
  • TransferManager.getRequiredSignatures( 0x775225818bA851948733A611922A8916931c056f, 0x2DF546F4000000000000000000000000775225818BA851948733A611922A8916931C056F000000000000000000000000EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE0000000000000000000000004132B0D5153CC305A23334E47B318142E2D74402000000000000000000000000000000000000000000000000013FBE85EDC9000000000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000000 ) => ( 1, 1 )
  • Null: 0x000...001.ba36390a( )
  • Proxy.STATICCALL( )
    • BaseWallet.DELEGATECALL( )
    • TransferManager.transferToken( _wallet=0x775225818bA851948733A611922A8916931c056f, _token=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, _to=0x4132B0d5153cc305a23334E47B318142E2d74402, _amount=90000000000000000, _data=0x )
      • VersionManager.isFeatureAuthorised( _wallet=0x775225818bA851948733A611922A8916931c056f, _feature=0x10A0847c2D170008dDCa7C3a688124f493630032 ) => ( True )
      • LockStorage.isLocked( _wallet=0x775225818bA851948733A611922A8916931c056f ) => ( False )
      • 0x391f0e86da951c03b1183c60b195090671adea88.13f4a0ea( )
      • 0x045b32efa0d97a681cc415f1b37c972ad7299a55.13565b2c( )
      • VersionManager.invokeStorage( _wallet=0x775225818bA851948733A611922A8916931c056f, _storage=0x045B32efA0D97a681Cc415f1B37C972Ad7299a55, _data=0x5AE5BC52000000000000000000000000775225818BA851948733A611922A8916931C056F000000000000000000000000000000000000000000000000013FBE85EDC9000000000000000000000000000000000000000000000000000000000000604E4850 )
        • 0x045b32efa0d97a681cc415f1b37c972ad7299a55.5ae5bc52( )
          • Proxy.d6eb1bbf( )
            • BaseWallet.authorised( 0x645BA45dBe3c6942c812A46f9EE8115C89B524EC ) => ( True )
            • VersionManager.checkAuthorisedFeatureAndInvokeWallet( _wallet=0x775225818bA851948733A611922A8916931c056f, _to=0x4132B0d5153cc305a23334E47B318142E2d74402, _value=90000000000000000, _data=0x ) => ( _res=0x )
              • Proxy.8f6f0332( )
                • BaseWallet.invoke( _target=0x4132B0d5153cc305a23334E47B318142E2d74402, _value=90000000000000000, _data=0x ) => ( _result=0x )
                  • ETH 0.09 0x4132b0d5153cc305a23334e47b318142e2d74402.CALL( )
                  • 0x045b32efa0d97a681cc415f1b37c972ad7299a55.13565b2c( )
                  • VersionManager.invokeStorage( _wallet=0x775225818bA851948733A611922A8916931c056f, _storage=0x045B32efA0D97a681Cc415f1B37C972Ad7299a55, _data=0x5AE5BC52000000000000000000000000775225818BA851948733A611922A8916931C056F00000000000000000000000000000000000000000000000001753735FEC2100000000000000000000000000000000000000000000000000000000000604E4850 )
                    • 0x045b32efa0d97a681cc415f1b37c972ad7299a55.5ae5bc52( )
                      • Proxy.d6eb1bbf( )
                        • BaseWallet.authorised( 0x645BA45dBe3c6942c812A46f9EE8115C89B524EC ) => ( True )
                        • VersionManager.checkAuthorisedFeatureAndInvokeWallet( _wallet=0x775225818bA851948733A611922A8916931c056f, _to=0x482579F93dC13e6B434E38b5a0447ca543D88A46, _value=15050871360000000, _data=0x ) => ( _res=0x )
                          • Proxy.8f6f0332( )
                            • BaseWallet.invoke( _target=0x482579F93dC13e6B434E38b5a0447ca543D88A46, _value=15050871360000000, _data=0x ) => ( _result=0x )
                              • ETH 0.01505087136 0x482579f93dc13e6b434e38b5a0447ca543d88a46.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 - <[email protected]>, Olivier VDB - <[email protected]>
                                 */
                                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 - <[email protected]>
                                 */
                                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 - <[email protected]>, Olivier VDB - <[email protected]>
                                 */
                                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 - <[email protected]>
                                 */
                                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 <[email protected]>, Olivier VDB <[email protected]>
                                 */
                                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: Proxy
                                pragma solidity ^0.6.12;
                                
                                // 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
                                
                                /**
                                 * @title Proxy
                                 * @notice Basic proxy that delegates all calls to a fixed implementing contract.
                                 * The implementing contract cannot be upgraded.
                                 * @author Julien Niset - <[email protected]>
                                 */
                                contract Proxy {
                                
                                    address implementation;
                                
                                    event Received(uint indexed value, address indexed sender, bytes data);
                                
                                    constructor(address _implementation) public {
                                        implementation = _implementation;
                                    }
                                
                                    fallback() external payable {
                                        // solhint-disable-next-line 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())}
                                        }
                                    }
                                
                                    receive() external payable {
                                        emit Received(msg.value, msg.sender, msg.data);
                                    }
                                }

                                File 3 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 - <[email protected]>
                                 */
                                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 - <[email protected]>, Olivier VDB - <[email protected]>
                                 */
                                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 - <[email protected]>
                                 */
                                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 - <[email protected]>, Olivier VDB - <[email protected]>
                                 */
                                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 - <[email protected]>
                                 */
                                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 - <[email protected]>
                                 */
                                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 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 - <[email protected]>
                                 */
                                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 - <[email protected]>
                                 */
                                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 - <[email protected]>, Olivier VDB - <[email protected]>
                                 */
                                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 - <[email protected]>
                                 */
                                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 - <[email protected]>, Olivier VDB - <[email protected]>
                                 */
                                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 <[email protected]>
                                 */
                                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
                                pragma solidity ^0.6.12;
                                // 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
                                
                                
                                /**
                                 * @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 - <[email protected]>
                                 */
                                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;
                                }
                                
                                
                                /**
                                 * @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;
                                }
                                
                                
                                
                                /**
                                 * @title BaseWallet
                                 * @notice Simple modular wallet that authorises modules to call its invoke() method.
                                 * @author Julien Niset - <[email protected]>
                                 */
                                contract BaseWallet is IWallet {
                                
                                    // The implementation of the proxy
                                    address public implementation;
                                    // The owner
                                    address public override owner;
                                    // The authorised modules
                                    mapping (address => bool) public override authorised;
                                    // The enabled static calls
                                    mapping (bytes4 => address) public override enabled;
                                    // The number of modules
                                    uint public override 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);
                                
                                    /**
                                     * @notice Throws if the sender is not an authorised module.
                                     */
                                    modifier moduleOnly {
                                        require(authorised[msg.sender], "BW: msg.sender not an authorized module");
                                        _;
                                    }
                                
                                    /**
                                     * @notice 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;
                                            IModule(_modules[i]).init(address(this));
                                            emit AuthorisedModule(_modules[i], true);
                                        }
                                        if (address(this).balance > 0) {
                                            emit Received(address(this).balance, address(0), "");
                                        }
                                    }
                                
                                    /**
                                     * @inheritdoc IWallet
                                     */
                                    function authoriseModule(address _module, bool _value) external override moduleOnly {
                                        if (authorised[_module] != _value) {
                                            emit AuthorisedModule(_module, _value);
                                            if (_value == true) {
                                                modules += 1;
                                                authorised[_module] = true;
                                                IModule(_module).init(address(this));
                                            } else {
                                                modules -= 1;
                                                require(modules > 0, "BW: wallet must have at least one module");
                                                delete authorised[_module];
                                            }
                                        }
                                    }
                                
                                    /**
                                    * @inheritdoc IWallet
                                    */
                                    function enableStaticCall(address _module, bytes4 _method) external override moduleOnly {
                                        require(authorised[_module], "BW: must be an authorised module for static call");
                                        enabled[_method] = _module;
                                        emit EnabledStaticCall(_module, _method);
                                    }
                                
                                    /**
                                     * @inheritdoc IWallet
                                     */
                                    function setOwner(address _newOwner) external override moduleOnly {
                                        require(_newOwner != address(0), "BW: address cannot be null");
                                        owner = _newOwner;
                                        emit OwnerChanged(_newOwner);
                                    }
                                
                                    /**
                                     * @notice 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;
                                        (success, _result) = _target.call{value: _value}(_data);
                                        if (!success) {
                                            // solhint-disable-next-line no-inline-assembly
                                            assembly {
                                                returndatacopy(0, 0, returndatasize())
                                                revert(0, returndatasize())
                                            }
                                        }
                                        emit Invoked(msg.sender, _target, _value, _data);
                                    }
                                
                                    /**
                                     * @notice This method delegates the static call to a target contract if the data corresponds
                                     * to an enabled module, or logs the call otherwise.
                                     */
                                    fallback() external payable {
                                        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");
                                
                                            // solhint-disable-next-line 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())}
                                            }
                                        }
                                    }
                                
                                    receive() external payable {
                                    }
                                }

                                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 - \[email protected]\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 - \[email protected]\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 - \[email protected]\u003e\n * @author Olivier Van Den Biggelaar - \[email protected]\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 - \[email protected]\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}"}}