ETH Price: $2,157.64 (+5.48%)

Transaction Decoder

Block:
12013705 at Mar-10-2021 11:06:19 PM +UTC
Transaction Fee:
0.0115299 ETH $24.88
Gas Used:
153,732 Gas / 75 Gwei

Emitted Events:

138 TRYfinance.Approval( owner=0x378eaa68ee038d04f23ced85c9e03fcadf819c49, spender=0x7a250d56...659F2488D, value=23765656678000000000 )
139 0x378eaa68ee038d04f23ced85c9e03fcadf819c49.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x000000000000000000000000645ba45dbe3c6942c812a46f9ee8115c89b524ec, 0x000000000000000000000000c12ecee46ed65d970ee5c899fcc7ae133aff9b03, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000044, 095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c6, 59f2488d00000000000000000000000000000000000000000000000149d092d5, 32147c0000000000000000000000000000000000000000000000000000000000 )
140 TransferManager.Approved( wallet=0x378eaa68ee038d04f23ced85c9e03fcadf819c49, token=TRYfinance, amount=23765656678000000000, spender=0x7a250d56...659F2488D )
141 0x378eaa68ee038d04f23ced85c9e03fcadf819c49.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x000000000000000000000000645ba45dbe3c6942c812a46f9ee8115c89b524ec, 0x000000000000000000000000482579f93dc13e6b434e38b5a0447ca543d88a46, 0x000000000000000000000000000000000000000000000000001fdda2cc4bf200, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000000 )
142 RelayerManager.Refund( wallet=0x378eaa68ee038d04f23ced85c9e03fcadf819c49, refundAddress=0x482579f93dc13e6b434e38b5a0447ca543d88a46, refundToken=0xEeeeeEee...eeeeeEEeE, refundAmount=8969415560000000 )
143 RelayerManager.TransactionExecuted( wallet=0x378eaa68ee038d04f23ced85c9e03fcadf819c49, success=True, returnData=0x, signedHash=08563885267FA0EE1CB1A6C0AF84FE1E0CDEB543FEC3ABE5836FA3B959204916 )

Account State Difference:

  Address   Before After State Difference Code
0x045B32ef...Ad7299a55
0x10A0847c...493630032
(Argent: Relayer Manager)
0x378EAA68...ADf819C49 0.020721035484528426 Eth0.011751619924528426 Eth0.00896941556
0x482579F9...543D88A46 9.330333882267771229 Eth9.339303297827771229 Eth0.00896941556
0xc12eCeE4...33AfF9b03
0xdd5a1C14...d4730a608
(Argent: Relayer)
5.201320986109176827 Eth
Nonce: 182115
5.189791086109176827 Eth
Nonce: 182116
0.0115299
(Ethermine)
1,324.248332301340859629 Eth1,324.259862201340859629 Eth0.0115299

Execution Trace

RelayerManager.execute( _wallet=0x378EAA68ee038D04F23CEd85C9E03FcADf819C49, _feature=0x5094a8f54B12AEc540bF7cCd0Dd7B62f4FecF7f2, _data=0x12EF080D000000000000000000000000378EAA68EE038D04F23CED85C9E03FCADF819C49000000000000000000000000C12ECEE46ED65D970EE5C899FCC7AE133AFF9B030000000000000000000000007A250D5630B4CF539739DF2C5DACB4C659F2488D00000000000000000000000000000000000000000000000149D092D532147C00, _nonce=4088033937924466213463697279321877038710214341, _signatures=0xAF60CF1E15F1BC5D8901E731DBA3C0C2799F35694D57A8738C0952B4D82338421830AB14476FF94D9A663DA1E5169F5406860B609722DCABE30EFA96B197B8DE1C, _gasPrice=80920000000, _gasLimit=200000, _refundToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, _refundAddress=0x482579F93dC13e6B434E38b5a0447ca543D88A46 ) => ( True )
  • VersionManager.isFeatureAuthorised( _wallet=0x378EAA68ee038D04F23CEd85C9E03FcADf819C49, _feature=0x5094a8f54B12AEc540bF7cCd0Dd7B62f4FecF7f2 ) => ( True )
  • TransferManager.getRequiredSignatures( 0x378EAA68ee038D04F23CEd85C9E03FcADf819C49, 0x12EF080D000000000000000000000000378EAA68EE038D04F23CED85C9E03FCADF819C49000000000000000000000000C12ECEE46ED65D970EE5C899FCC7AE133AFF9B030000000000000000000000007A250D5630B4CF539739DF2C5DACB4C659F2488D00000000000000000000000000000000000000000000000149D092D532147C00 ) => ( 1, 1 )
  • Null: 0x000...001.08563885( )
  • 0x378eaa68ee038d04f23ced85c9e03fcadf819c49.STATICCALL( )
    • BaseWallet.DELEGATECALL( )
    • TransferManager.approveToken( _wallet=0x378EAA68ee038D04F23CEd85C9E03FcADf819C49, _token=0xc12eCeE46ed65D970EE5C899FCC7AE133AfF9b03, _spender=0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D, _amount=23765656678000000000 )
      • VersionManager.isFeatureAuthorised( _wallet=0x378EAA68ee038D04F23CEd85C9E03FcADf819C49, _feature=0x10A0847c2D170008dDCa7C3a688124f493630032 ) => ( True )
      • LockStorage.isLocked( _wallet=0x378EAA68ee038D04F23CEd85C9E03FcADf819C49 ) => ( False )
      • 0x391f0e86da951c03b1183c60b195090671adea88.13f4a0ea( )
      • TRYfinance.allowance( owner=0x378EAA68ee038D04F23CEd85C9E03FcADf819C49, spender=0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D ) => ( 23765656678320000000 )
      • VersionManager.checkAuthorisedFeatureAndInvokeWallet( _wallet=0x378EAA68ee038D04F23CEd85C9E03FcADf819C49, _to=0xc12eCeE46ed65D970EE5C899FCC7AE133AfF9b03, _value=0, _data=0x095EA7B30000000000000000000000007A250D5630B4CF539739DF2C5DACB4C659F2488D00000000000000000000000000000000000000000000000149D092D532147C00 ) => ( _res=0x0000000000000000000000000000000000000000000000000000000000000001 )
        • 0x378eaa68ee038d04f23ced85c9e03fcadf819c49.8f6f0332( )
          • BaseWallet.invoke( _target=0xc12eCeE46ed65D970EE5C899FCC7AE133AfF9b03, _value=0, _data=0x095EA7B30000000000000000000000007A250D5630B4CF539739DF2C5DACB4C659F2488D00000000000000000000000000000000000000000000000149D092D532147C00 ) => ( _result=0x0000000000000000000000000000000000000000000000000000000000000001 )
            • TRYfinance.approve( spender=0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D, amount=23765656678000000000 ) => ( True )
            • 0x045b32efa0d97a681cc415f1b37c972ad7299a55.13565b2c( )
            • VersionManager.invokeStorage( _wallet=0x378EAA68ee038D04F23CEd85C9E03FcADf819C49, _storage=0x045B32efA0D97a681Cc415f1B37C972Ad7299a55, _data=0x5AE5BC52000000000000000000000000378EAA68EE038D04F23CED85C9E03FCADF819C49000000000000000000000000000000000000000000000000004F26A41E00C20000000000000000000000000000000000000000000000000000000000604A9F11 )
              • 0x045b32efa0d97a681cc415f1b37c972ad7299a55.5ae5bc52( )
                • 0x378eaa68ee038d04f23ced85c9e03fcadf819c49.d6eb1bbf( )
                  • BaseWallet.authorised( 0x645BA45dBe3c6942c812A46f9EE8115C89B524EC ) => ( True )
                  • VersionManager.checkAuthorisedFeatureAndInvokeWallet( _wallet=0x378EAA68ee038D04F23CEd85C9E03FcADf819C49, _to=0x482579F93dC13e6B434E38b5a0447ca543D88A46, _value=8969415560000000, _data=0x ) => ( _res=0x )
                    • 0x378eaa68ee038d04f23ced85c9e03fcadf819c49.8f6f0332( )
                      • BaseWallet.invoke( _target=0x482579F93dC13e6B434E38b5a0447ca543D88A46, _value=8969415560000000, _data=0x ) => ( _result=0x )
                        • ETH 0.00896941556 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 - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                           */
                          interface IFeature {
                              enum OwnerSignature {
                                  Anyone,             // Anyone
                                  Required,           // Owner required
                                  Optional,           // Owner and/or guardians
                                  Disallowed          // guardians only
                              }
                              /**
                              * @notice Utility method to recover any ERC20 token that was sent to the Feature by mistake.
                              * @param _token The token to recover.
                              */
                              function recoverToken(address _token) external;
                              /**
                               * @notice Inits a Feature for a wallet by e.g. setting some wallet specific parameters in storage.
                               * @param _wallet The wallet.
                               */
                              function init(address _wallet) external;
                              /**
                               * @notice Helper method to check if an address is an authorised feature of a target wallet.
                               * @param _wallet The target wallet.
                               * @param _feature The address.
                               */
                              function isFeatureAuthorisedInVersionManager(address _wallet, address _feature) external view returns (bool);
                              /**
                              * @notice Gets the number of valid signatures that must be provided to execute a
                              * specific relayed transaction.
                              * @param _wallet The target wallet.
                              * @param _data The data of the relayed transaction.
                              * @return The number of required signatures and the wallet owner signature requirement.
                              */
                              function getRequiredSignatures(address _wallet, bytes calldata _data) external view returns (uint256, OwnerSignature);
                              /**
                              * @notice Gets the list of static call signatures that this feature responds to on behalf of wallets
                              */
                              function getStaticCallSignatures() external view returns (bytes4[] memory);
                          }
                          // File: lib/other/ERC20.sol
                          pragma solidity >=0.5.4 <0.7.0;
                          /**
                           * ERC20 contract interface.
                           */
                          interface ERC20 {
                              function totalSupply() external view returns (uint);
                              function decimals() external view returns (uint);
                              function balanceOf(address tokenOwner) external view returns (uint balance);
                              function allowance(address tokenOwner, address spender) external view returns (uint remaining);
                              function transfer(address to, uint tokens) external returns (bool success);
                              function approve(address spender, uint tokens) external returns (bool success);
                              function transferFrom(address from, address to, uint tokens) external returns (bool success);
                          }
                          // File: contracts/infrastructure/storage/ILimitStorage.sol
                          // This program is free software: you can redistribute it and/or modify
                          // it under the terms of the GNU General Public License as published by
                          // the Free Software Foundation, either version 3 of the License, or
                          // (at your option) any later version.
                          // This program is distributed in the hope that it will be useful,
                          // but WITHOUT ANY WARRANTY; without even the implied warranty of
                          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          // GNU General Public License for more details.
                          // You should have received a copy of the GNU General Public License
                          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                           
                          /**
                           * @title ILimitStorage
                           * @notice LimitStorage interface
                           */
                          interface ILimitStorage {
                              struct Limit {
                                  // the current limit
                                  uint128 current;
                                  // the pending limit if any
                                  uint128 pending;
                                  // when the pending limit becomes the current limit
                                  uint64 changeAfter;
                              }
                              struct DailySpent {
                                  // The amount already spent during the current period
                                  uint128 alreadySpent;
                                  // The end of the current period
                                  uint64 periodEnd;
                              }
                              function setLimit(address _wallet, Limit memory _limit) external;
                              function getLimit(address _wallet) external view returns (Limit memory _limit);
                              function setDailySpent(address _wallet, DailySpent memory _dailySpent) external;
                              function getDailySpent(address _wallet) external view returns (DailySpent memory _dailySpent);
                              function setLimitAndDailySpent(address _wallet, Limit memory _limit, DailySpent memory _dailySpent) external;
                              function getLimitAndDailySpent(address _wallet) external view returns (Limit memory _limit, DailySpent memory _dailySpent);
                          }
                          // File: contracts/modules/common/IVersionManager.sol
                          // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                          // This program is free software: you can redistribute it and/or modify
                          // it under the terms of the GNU General Public License as published by
                          // the Free Software Foundation, either version 3 of the License, or
                          // (at your option) any later version.
                          // This program is distributed in the hope that it will be useful,
                          // but WITHOUT ANY WARRANTY; without even the implied warranty of
                          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          // GNU General Public License for more details.
                          // You should have received a copy of the GNU General Public License
                          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                           
                          pragma solidity >=0.5.4 <0.7.0;
                          /**
                           * @title IVersionManager
                           * @notice Interface for the VersionManager module.
                           * @author Olivier VDB - <olivier@argent.xyz>
                           */
                          interface IVersionManager {
                              /**
                               * @notice Returns true if the feature is authorised for the wallet
                               * @param _wallet The target wallet.
                               * @param _feature The feature.
                               */
                              function isFeatureAuthorised(address _wallet, address _feature) external view returns (bool);
                              /**
                               * @notice Lets a feature (caller) invoke a wallet.
                               * @param _wallet The target wallet.
                               * @param _to The target address for the transaction.
                               * @param _value The value of the transaction.
                               * @param _data The data of the transaction.
                               */
                              function checkAuthorisedFeatureAndInvokeWallet(
                                  address _wallet,
                                  address _to,
                                  uint256 _value,
                                  bytes calldata _data
                              ) external returns (bytes memory _res);
                              /* ******* Backward Compatibility with old Storages and BaseWallet *************** */
                              /**
                               * @notice Sets a new owner for the wallet.
                               * @param _newOwner The new owner.
                               */
                              function setOwner(address _wallet, address _newOwner) external;
                              /**
                               * @notice Lets a feature write data to a storage contract.
                               * @param _wallet The target wallet.
                               * @param _storage The storage contract.
                               * @param _data The data of the call
                               */
                              function invokeStorage(address _wallet, address _storage, bytes calldata _data) external;
                              /**
                               * @notice Upgrade a wallet to a new version.
                               * @param _wallet the wallet to upgrade
                               * @param _toVersion the new version
                               */
                              function upgradeWallet(address _wallet, uint256 _toVersion) external;
                          }
                          // File: contracts/modules/common/BaseFeature.sol
                          // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                          // This program is free software: you can redistribute it and/or modify
                          // it under the terms of the GNU General Public License as published by
                          // the Free Software Foundation, either version 3 of the License, or
                          // (at your option) any later version.
                          // This program is distributed in the hope that it will be useful,
                          // but WITHOUT ANY WARRANTY; without even the implied warranty of
                          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          // GNU General Public License for more details.s
                          // You should have received a copy of the GNU General Public License
                          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                           
                          /**
                           * @title BaseFeature
                           * @notice Base Feature contract that contains methods common to all Feature contracts.
                           * @author Julien Niset - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                           */
                          contract BaseFeature is IFeature {
                              // Empty calldata
                              bytes constant internal EMPTY_BYTES = "";
                              // Mock token address for ETH
                              address constant internal ETH_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                              // The address of the Lock storage
                              ILockStorage internal lockStorage;
                              // The address of the Version Manager
                              IVersionManager internal versionManager;
                              event FeatureCreated(bytes32 name);
                              /**
                               * @notice Throws if the wallet is locked.
                               */
                              modifier onlyWhenUnlocked(address _wallet) {
                                  require(!lockStorage.isLocked(_wallet), "BF: wallet locked");
                                  _;
                              }
                              /**
                               * @notice Throws if the sender is not the VersionManager.
                               */
                              modifier onlyVersionManager() {
                                  require(msg.sender == address(versionManager), "BF: caller must be VersionManager");
                                  _;
                              }
                              /**
                               * @notice Throws if the sender is not the owner of the target wallet.
                               */
                              modifier onlyWalletOwner(address _wallet) {
                                  require(isOwner(_wallet, msg.sender), "BF: must be wallet owner");
                                  _;
                              }
                              /**
                               * @notice Throws if the sender is not an authorised feature of the target wallet.
                               */
                              modifier onlyWalletFeature(address _wallet) {
                                  require(versionManager.isFeatureAuthorised(_wallet, msg.sender), "BF: must be a wallet feature");
                                  _;
                              }
                              /**
                               * @notice Throws if the sender is not the owner of the target wallet or the feature itself.
                               */
                              modifier onlyWalletOwnerOrFeature(address _wallet) {
                                  // Wrapping in an internal method reduces deployment cost by avoiding duplication of inlined code
                                  verifyOwnerOrAuthorisedFeature(_wallet, msg.sender);
                                  _;
                              }
                              constructor(
                                  ILockStorage _lockStorage,
                                  IVersionManager _versionManager,
                                  bytes32 _name
                              ) public {
                                  lockStorage = _lockStorage;
                                  versionManager = _versionManager;
                                  emit FeatureCreated(_name);
                              }
                              /**
                              * @inheritdoc IFeature
                              */
                              function recoverToken(address _token) external virtual override {
                                  uint total = ERC20(_token).balanceOf(address(this));
                                  _token.call(abi.encodeWithSelector(ERC20(_token).transfer.selector, address(versionManager), total));
                              }
                              /**
                               * @notice Inits the feature for a wallet by doing nothing.
                               * @dev !! Overriding methods need make sure `init()` can only be called by the VersionManager !!
                               * @param _wallet The wallet.
                               */
                              function init(address _wallet) external virtual override  {}
                              /**
                               * @inheritdoc IFeature
                               */
                              function getRequiredSignatures(address, bytes calldata) external virtual view override returns (uint256, OwnerSignature) {
                                  revert("BF: disabled method");
                              }
                              /**
                               * @inheritdoc IFeature
                               */
                              function getStaticCallSignatures() external virtual override view returns (bytes4[] memory _sigs) {}
                              /**
                               * @inheritdoc IFeature
                               */
                              function isFeatureAuthorisedInVersionManager(address _wallet, address _feature) public override view returns (bool) {
                                  return versionManager.isFeatureAuthorised(_wallet, _feature);
                              }
                              /**
                              * @notice Checks that the wallet address provided as the first parameter of _data matches _wallet
                              * @return false if the addresses are different.
                              */
                              function verifyData(address _wallet, bytes calldata _data) internal pure returns (bool) {
                                  require(_data.length >= 36, "RM: Invalid dataWallet");
                                  address dataWallet = abi.decode(_data[4:], (address));
                                  return dataWallet == _wallet;
                              }
                               /**
                               * @notice Helper method to check if an address is the owner of a target wallet.
                               * @param _wallet The target wallet.
                               * @param _addr The address.
                               */
                              function isOwner(address _wallet, address _addr) internal view returns (bool) {
                                  return IWallet(_wallet).owner() == _addr;
                              }
                              /**
                               * @notice Verify that the caller is an authorised feature or the wallet owner.
                               * @param _wallet The target wallet.
                               * @param _sender The caller.
                               */
                              function verifyOwnerOrAuthorisedFeature(address _wallet, address _sender) internal view {
                                  require(isFeatureAuthorisedInVersionManager(_wallet, _sender) || isOwner(_wallet, _sender), "BF: must be owner or feature");
                              }
                              /**
                               * @notice Helper method to invoke a wallet.
                               * @param _wallet The target wallet.
                               * @param _to The target address for the transaction.
                               * @param _value The value of the transaction.
                               * @param _data The data of the transaction.
                               */
                              function invokeWallet(address _wallet, address _to, uint256 _value, bytes memory _data)
                                  internal
                                  returns (bytes memory _res) 
                              {
                                  _res = versionManager.checkAuthorisedFeatureAndInvokeWallet(_wallet, _to, _value, _data);
                              }
                          }
                          // File: contracts/modules/common/GuardianUtils.sol
                          // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                          // This program is free software: you can redistribute it and/or modify
                          // it under the terms of the GNU General Public License as published by
                          // the Free Software Foundation, either version 3 of the License, or
                          // (at your option) any later version.
                          // This program is distributed in the hope that it will be useful,
                          // but WITHOUT ANY WARRANTY; without even the implied warranty of
                          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          // GNU General Public License for more details.
                          // You should have received a copy of the GNU General Public License
                          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                           
                          /**
                           * @title GuardianUtils
                           * @notice Bundles guardian read logic.
                           */
                          library GuardianUtils {
                              /**
                              * @notice Checks if an address is a guardian or an account authorised to sign on behalf of a smart-contract guardian
                              * given a list of guardians.
                              * @param _guardians the list of guardians
                              * @param _guardian the address to test
                              * @return true and the list of guardians minus the found guardian upon success, false and the original list of guardians if not found.
                              */
                              function isGuardianOrGuardianSigner(address[] memory _guardians, address _guardian) internal view returns (bool, address[] memory) {
                                  if (_guardians.length == 0 || _guardian == address(0)) {
                                      return (false, _guardians);
                                  }
                                  bool isFound = false;
                                  address[] memory updatedGuardians = new address[](_guardians.length - 1);
                                  uint256 index = 0;
                                  for (uint256 i = 0; i < _guardians.length; i++) {
                                      if (!isFound) {
                                          // check if _guardian is an account guardian
                                          if (_guardian == _guardians[i]) {
                                              isFound = true;
                                              continue;
                                          }
                                          // check if _guardian is the owner of a smart contract guardian
                                          if (isContract(_guardians[i]) && isGuardianOwner(_guardians[i], _guardian)) {
                                              isFound = true;
                                              continue;
                                          }
                                      }
                                      if (index < updatedGuardians.length) {
                                          updatedGuardians[index] = _guardians[i];
                                          index++;
                                      }
                                  }
                                  return isFound ? (true, updatedGuardians) : (false, _guardians);
                              }
                             /**
                              * @notice Checks if an address is a contract.
                              * @param _addr The address.
                              */
                              function isContract(address _addr) internal view returns (bool) {
                                  uint32 size;
                                  // solhint-disable-next-line no-inline-assembly
                                  assembly {
                                      size := extcodesize(_addr)
                                  }
                                  return (size > 0);
                              }
                              /**
                              * @notice Checks if an address is the owner of a guardian contract.
                              * The method does not revert if the call to the owner() method consumes more then 5000 gas.
                              * @param _guardian The guardian contract
                              * @param _owner The owner to verify.
                              */
                              function isGuardianOwner(address _guardian, address _owner) internal view returns (bool) {
                                  address owner = address(0);
                                  bytes4 sig = bytes4(keccak256("owner()"));
                                  // solhint-disable-next-line no-inline-assembly
                                  assembly {
                                      let ptr := mload(0x40)
                                      mstore(ptr,sig)
                                      let result := staticcall(5000, _guardian, ptr, 0x20, ptr, 0x20)
                                      if eq(result, 1) {
                                          owner := mload(ptr)
                                      }
                                  }
                                  return owner == _owner;
                              }
                          }
                          // File: contracts/infrastructure/ITokenPriceRegistry.sol
                          // This program is free software: you can redistribute it and/or modify
                          // it under the terms of the GNU General Public License as published by
                          // the Free Software Foundation, either version 3 of the License, or
                          // (at your option) any later version.
                          // This program is distributed in the hope that it will be useful,
                          // but WITHOUT ANY WARRANTY; without even the implied warranty of
                          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          // GNU General Public License for more details.
                          // You should have received a copy of the GNU General Public License
                          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                           
                          /**
                           * @title ITokenPriceRegistry
                           * @notice TokenPriceRegistry interface
                           */
                          interface ITokenPriceRegistry {
                              function getTokenPrice(address _token) external view returns (uint184 _price);
                              function isTokenTradable(address _token) external view returns (bool _isTradable);
                          }
                          // File: contracts/modules/common/LimitUtils.sol
                          // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                          // This program is free software: you can redistribute it and/or modify
                          // it under the terms of the GNU General Public License as published by
                          // the Free Software Foundation, either version 3 of the License, or
                          // (at your option) any later version.
                          // This program is distributed in the hope that it will be useful,
                          // but WITHOUT ANY WARRANTY; without even the implied warranty of
                          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          // GNU General Public License for more details.
                          // You should have received a copy of the GNU General Public License
                          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                           
                          /**
                           * @title LimitUtils
                           * @notice Helper library to manage the daily limit and interact with a contract implementing the ILimitStorage interface.
                           * @author Julien Niset - <julien@argent.xyz>
                           */
                          library LimitUtils {
                              // large limit when the limit can be considered disabled
                              uint128 constant internal LIMIT_DISABLED = uint128(-1);
                              using SafeMath for uint256;
                              // *************** Internal Functions ********************* //
                              /**
                               * @notice Changes the daily limit (expressed in ETH).
                               * Decreasing the limit is immediate while increasing the limit is pending for the security period.
                               * @param _lStorage The storage contract.
                               * @param _versionManager The version manager.
                               * @param _wallet The target wallet.
                               * @param _targetLimit The target limit.
                               * @param _securityPeriod The security period.
                               */
                              function changeLimit(
                                  ILimitStorage _lStorage,
                                  IVersionManager _versionManager,
                                  address _wallet,
                                  uint256 _targetLimit,
                                  uint256 _securityPeriod
                              )
                                  internal
                                  returns (ILimitStorage.Limit memory)
                              {
                                  ILimitStorage.Limit memory limit = _lStorage.getLimit(_wallet);
                                  uint256 currentLimit = currentLimit(limit);
                                  ILimitStorage.Limit memory newLimit;
                                  if (_targetLimit <= currentLimit) {
                                      uint128 targetLimit = safe128(_targetLimit);
                                      newLimit = ILimitStorage.Limit(targetLimit, targetLimit, safe64(block.timestamp));
                                  } else {
                                      newLimit = ILimitStorage.Limit(safe128(currentLimit), safe128(_targetLimit), safe64(block.timestamp.add(_securityPeriod)));
                                  }
                                  setLimit(_versionManager, _lStorage, _wallet, newLimit);
                                  return newLimit;
                              }
                               /**
                               * @notice Disable the daily limit.
                               * The change is pending for the security period.
                               * @param _lStorage The storage contract.
                               * @param _versionManager The version manager.
                               * @param _wallet The target wallet.
                               * @param _securityPeriod The security period.
                               */
                              function disableLimit(
                                  ILimitStorage _lStorage,
                                  IVersionManager _versionManager,
                                  address _wallet,
                                  uint256 _securityPeriod
                              )
                                  internal
                              {
                                  changeLimit(_lStorage, _versionManager, _wallet, LIMIT_DISABLED, _securityPeriod);
                              }
                              /**
                              * @notice Returns whether the daily limit is disabled for a wallet.
                              * @param _wallet The target wallet.
                              * @return _limitDisabled true if the daily limit is disabled, false otherwise.
                              */
                              function isLimitDisabled(ILimitStorage _lStorage, address _wallet) internal view returns (bool) {
                                  ILimitStorage.Limit memory limit = _lStorage.getLimit(_wallet);
                                  uint256 currentLimit = currentLimit(limit);
                                  return (currentLimit == LIMIT_DISABLED);
                              }
                              /**
                              * @notice Checks if a transfer is within the limit. If yes the daily spent is updated.
                              * @param _lStorage The storage contract.
                              * @param _versionManager The Version Manager.
                              * @param _wallet The target wallet.
                              * @param _amount The amount for the transfer
                              * @return true if the transfer is withing the daily limit.
                              */
                              function checkAndUpdateDailySpent(
                                  ILimitStorage _lStorage,
                                  IVersionManager _versionManager,
                                  address _wallet,
                                  uint256 _amount
                              )
                                  internal
                                  returns (bool)
                              {
                                  (ILimitStorage.Limit memory limit, ILimitStorage.DailySpent memory dailySpent) = _lStorage.getLimitAndDailySpent(_wallet);
                                  uint256 currentLimit = currentLimit(limit);
                                  if (_amount == 0 || currentLimit == LIMIT_DISABLED) {
                                      return true;
                                  }
                                  ILimitStorage.DailySpent memory newDailySpent;
                                  if (dailySpent.periodEnd <= block.timestamp && _amount <= currentLimit) {
                                      newDailySpent = ILimitStorage.DailySpent(safe128(_amount), safe64(block.timestamp + 24 hours));
                                      setDailySpent(_versionManager, _lStorage, _wallet, newDailySpent);
                                      return true;
                                  } else if (dailySpent.periodEnd > block.timestamp && _amount.add(dailySpent.alreadySpent) <= currentLimit) {
                                      newDailySpent = ILimitStorage.DailySpent(safe128(_amount.add(dailySpent.alreadySpent)), safe64(dailySpent.periodEnd));
                                      setDailySpent(_versionManager, _lStorage, _wallet, newDailySpent);
                                      return true;
                                  }
                                  return false;
                              }
                              /**
                              * @notice Helper method to Reset the daily consumption.
                              * @param _versionManager The Version Manager.
                              * @param _wallet The target wallet.
                              */
                              function resetDailySpent(IVersionManager _versionManager, ILimitStorage limitStorage, address _wallet) internal {
                                  setDailySpent(_versionManager, limitStorage, _wallet, ILimitStorage.DailySpent(uint128(0), uint64(0)));
                              }
                              /**
                              * @notice Helper method to get the ether value equivalent of a token amount.
                              * @notice For low value amounts of tokens we accept this to return zero as these are small enough to disregard.
                              * Note that the price stored for tokens = price for 1 token (in ETH wei) * 10^(18-token decimals).
                              * @param _amount The token amount.
                              * @param _token The address of the token.
                              * @return The ether value for _amount of _token.
                              */
                              function getEtherValue(ITokenPriceRegistry _priceRegistry, uint256 _amount, address _token) internal view returns (uint256) {
                                  uint256 price = _priceRegistry.getTokenPrice(_token);
                                  uint256 etherValue = price.mul(_amount).div(10**18);
                                  return etherValue;
                              }
                              /**
                              * @notice Helper method to get the current limit from a Limit struct.
                              * @param _limit The limit struct
                              */
                              function currentLimit(ILimitStorage.Limit memory _limit) internal view returns (uint256) {
                                  if (_limit.changeAfter > 0 && _limit.changeAfter < block.timestamp) {
                                      return _limit.pending;
                                  }
                                  return _limit.current;
                              }
                              function safe128(uint256 _num) internal pure returns (uint128) {
                                  require(_num < 2**128, "LU: more then 128 bits");
                                  return uint128(_num);
                              }
                              function safe64(uint256 _num) internal pure returns (uint64) {
                                  require(_num < 2**64, "LU: more then 64 bits");
                                  return uint64(_num);
                              }
                              // *************** Storage invocations in VersionManager ********************* //
                              function setLimit(
                                  IVersionManager _versionManager,
                                  ILimitStorage _lStorage,
                                  address _wallet, 
                                  ILimitStorage.Limit memory _limit
                              ) internal {
                                  _versionManager.invokeStorage(
                                      _wallet,
                                      address(_lStorage),
                                      abi.encodeWithSelector(_lStorage.setLimit.selector, _wallet, _limit)
                                  );
                              }
                              function setDailySpent(
                                  IVersionManager _versionManager,
                                  ILimitStorage _lStorage,
                                  address _wallet, 
                                  ILimitStorage.DailySpent memory _dailySpent
                              ) private {
                                  _versionManager.invokeStorage(
                                      _wallet,
                                      address(_lStorage),
                                      abi.encodeWithSelector(_lStorage.setDailySpent.selector, _wallet, _dailySpent)
                                  );
                              }
                          }
                          // File: contracts/infrastructure/storage/IGuardianStorage.sol
                          // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                          // This program is free software: you can redistribute it and/or modify
                          // it under the terms of the GNU General Public License as published by
                          // the Free Software Foundation, either version 3 of the License, or
                          // (at your option) any later version.
                          // This program is distributed in the hope that it will be useful,
                          // but WITHOUT ANY WARRANTY; without even the implied warranty of
                          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          // GNU General Public License for more details.
                          // You should have received a copy of the GNU General Public License
                          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                           
                          pragma solidity >=0.5.4 <0.7.0;
                          interface IGuardianStorage {
                              /**
                               * @notice Lets an authorised module add a guardian to a wallet.
                               * @param _wallet The target wallet.
                               * @param _guardian The guardian to add.
                               */
                              function addGuardian(address _wallet, address _guardian) external;
                              /**
                               * @notice Lets an authorised module revoke a guardian from a wallet.
                               * @param _wallet The target wallet.
                               * @param _guardian The guardian to revoke.
                               */
                              function revokeGuardian(address _wallet, address _guardian) external;
                              /**
                               * @notice Checks if an account is a guardian for a wallet.
                               * @param _wallet The target wallet.
                               * @param _guardian The account.
                               * @return true if the account is a guardian for a wallet.
                               */
                              function isGuardian(address _wallet, address _guardian) external view returns (bool);
                              function isLocked(address _wallet) external view returns (bool);
                              function getLock(address _wallet) external view returns (uint256);
                              function getLocker(address _wallet) external view returns (address);
                              function setLock(address _wallet, uint256 _releaseAfter) external;
                              function getGuardians(address _wallet) external view returns (address[] memory);
                              function guardianCount(address _wallet) external view returns (uint256);
                          }
                          // File: modules/RelayerManager.sol
                          // Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
                          // This program is free software: you can redistribute it and/or modify
                          // it under the terms of the GNU General Public License as published by
                          // the Free Software Foundation, either version 3 of the License, or
                          // (at your option) any later version.
                          // This program is distributed in the hope that it will be useful,
                          // but WITHOUT ANY WARRANTY; without even the implied warranty of
                          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          // GNU General Public License for more details.
                          // You should have received a copy of the GNU General Public License
                          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                           
                          /**
                           * @title RelayerManager
                           * @notice Feature to execute transactions signed by ETH-less accounts and sent by a relayer.
                           * @author Julien Niset <julien@argent.xyz>, Olivier VDB <olivier@argent.xyz>
                           */
                          contract RelayerManager is BaseFeature {
                              bytes32 constant NAME = "RelayerManager";
                              uint256 constant internal BLOCKBOUND = 10000;
                              using SafeMath for uint256;
                              mapping (address => RelayerConfig) public relayer;
                              // The storage of the limit
                              ILimitStorage public limitStorage;
                              // The Token price storage
                              ITokenPriceRegistry public tokenPriceRegistry;
                              // The Guardian storage
                              IGuardianStorage public guardianStorage;
                              struct RelayerConfig {
                                  uint256 nonce;
                                  mapping (bytes32 => bool) executedTx;
                              }
                              // Used to avoid stack too deep error
                              struct StackExtension {
                                  uint256 requiredSignatures;
                                  OwnerSignature ownerSignatureRequirement;
                                  bytes32 signHash;
                                  bool success;
                                  bytes returnData;
                              }
                              event TransactionExecuted(address indexed wallet, bool indexed success, bytes returnData, bytes32 signedHash);
                              event Refund(address indexed wallet, address indexed refundAddress, address refundToken, uint256 refundAmount);
                              /* ***************** External methods ************************* */
                              constructor(
                                  ILockStorage _lockStorage,
                                  IGuardianStorage _guardianStorage,
                                  ILimitStorage _limitStorage,
                                  ITokenPriceRegistry _tokenPriceRegistry,
                                  IVersionManager _versionManager
                              )
                                  BaseFeature(_lockStorage, _versionManager, NAME)
                                  public
                              {
                                  limitStorage = _limitStorage;
                                  tokenPriceRegistry = _tokenPriceRegistry;
                                  guardianStorage = _guardianStorage;
                              }
                              /**
                              * @notice Executes a relayed transaction.
                              * @param _wallet The target wallet.
                              * @param _feature The target feature.
                              * @param _data The data for the relayed transaction
                              * @param _nonce The nonce used to prevent replay attacks.
                              * @param _signatures The signatures as a concatenated byte array.
                              * @param _gasPrice The gas price to use for the gas refund.
                              * @param _gasLimit The gas limit to use for the gas refund.
                              * @param _refundToken The token to use for the gas refund.
                              * @param _refundAddress The address refunded to prevent front-running.
                              */
                              function execute(
                                  address _wallet,
                                  address _feature,
                                  bytes calldata _data,
                                  uint256 _nonce,
                                  bytes calldata _signatures,
                                  uint256 _gasPrice,
                                  uint256 _gasLimit,
                                  address _refundToken,
                                  address _refundAddress
                              )
                                  external
                                  returns (bool)
                              {
                                  uint startGas = gasleft();
                                  require(startGas >= _gasLimit, "RM: not enough gas provided");
                                  require(verifyData(_wallet, _data), "RM: Target of _data != _wallet");
                                  require(isFeatureAuthorisedInVersionManager(_wallet, _feature), "RM: feature not authorised");
                                  StackExtension memory stack;
                                  (stack.requiredSignatures, stack.ownerSignatureRequirement) = IFeature(_feature).getRequiredSignatures(_wallet, _data);
                                  require(stack.requiredSignatures > 0 || stack.ownerSignatureRequirement == OwnerSignature.Anyone, "RM: Wrong signature requirement");
                                  require(stack.requiredSignatures * 65 == _signatures.length, "RM: Wrong number of signatures");
                                  stack.signHash = getSignHash(
                                      address(this),
                                      _feature,
                                      0,
                                      _data,
                                      _nonce,
                                      _gasPrice,
                                      _gasLimit,
                                      _refundToken,
                                      _refundAddress);
                                  require(checkAndUpdateUniqueness(
                                      _wallet,
                                      _nonce,
                                      stack.signHash,
                                      stack.requiredSignatures,
                                      stack.ownerSignatureRequirement), "RM: Duplicate request");
                                  require(validateSignatures(_wallet, stack.signHash, _signatures, stack.ownerSignatureRequirement), "RM: Invalid signatures");
                                  (stack.success, stack.returnData) = _feature.call(_data);
                                  // only refund when approved by owner and positive gas price
                                  if (_gasPrice > 0 && stack.ownerSignatureRequirement == OwnerSignature.Required) {
                                      refund(
                                          _wallet,
                                          startGas,
                                          _gasPrice,
                                          _gasLimit,
                                          _refundToken,
                                          _refundAddress,
                                          stack.requiredSignatures);
                                  }
                                  emit TransactionExecuted(_wallet, stack.success, stack.returnData, stack.signHash);
                                  return stack.success;
                              }
                              /**
                              * @notice Gets the current nonce for a wallet.
                              * @param _wallet The target wallet.
                              */
                              function getNonce(address _wallet) external view returns (uint256 nonce) {
                                  return relayer[_wallet].nonce;
                              }
                              /**
                              * @notice Checks if a transaction identified by its sign hash has already been executed.
                              * @param _wallet The target wallet.
                              * @param _signHash The sign hash of the transaction.
                              */
                              function isExecutedTx(address _wallet, bytes32 _signHash) external view returns (bool executed) {
                                  return relayer[_wallet].executedTx[_signHash];
                              }
                              /* ***************** Internal & Private methods ************************* */
                              /**
                              * @notice Generates the signed hash of a relayed transaction according to ERC 1077.
                              * @param _from The starting address for the relayed transaction (should be the relayer module)
                              * @param _to The destination address for the relayed transaction (should be the target module)
                              * @param _value The value for the relayed transaction.
                              * @param _data The data for the relayed transaction which includes the wallet address.
                              * @param _nonce The nonce used to prevent replay attacks.
                              * @param _gasPrice The gas price to use for the gas refund.
                              * @param _gasLimit The gas limit to use for the gas refund.
                              * @param _refundToken The token to use for the gas refund.
                              * @param _refundAddress The address refunded to prevent front-running.
                              */
                              function getSignHash(
                                  address _from,
                                  address _to,
                                  uint256 _value,
                                  bytes memory _data,
                                  uint256 _nonce,
                                  uint256 _gasPrice,
                                  uint256 _gasLimit,
                                  address _refundToken,
                                  address _refundAddress
                              )
                                  internal
                                  pure
                                  returns (bytes32)
                              {
                                  return keccak256(
                                      abi.encodePacked(
                                          "\x19Ethereum Signed Message:\n32",
                                          keccak256(abi.encodePacked(
                                              byte(0x19),
                                              byte(0),
                                              _from,
                                              _to,
                                              _value,
                                              _data,
                                              getChainId(),
                                              _nonce,
                                              _gasPrice,
                                              _gasLimit,
                                              _refundToken,
                                              _refundAddress))
                                  ));
                              }
                              /**
                              * @notice Checks if the relayed transaction is unique. If yes the state is updated.
                              * For actions requiring 1 signature by the owner we use the incremental nonce.
                              * For all other actions we check/store the signHash in a mapping.
                              * @param _wallet The target wallet.
                              * @param _nonce The nonce.
                              * @param _signHash The signed hash of the transaction.
                              * @param requiredSignatures The number of signatures required.
                              * @param ownerSignatureRequirement The wallet owner signature requirement.
                              * @return true if the transaction is unique.
                              */
                              function checkAndUpdateUniqueness(
                                  address _wallet,
                                  uint256 _nonce,
                                  bytes32 _signHash,
                                  uint256 requiredSignatures,
                                  OwnerSignature ownerSignatureRequirement
                              )
                                  internal
                                  returns (bool)
                              {
                                  if (requiredSignatures == 1 && ownerSignatureRequirement == OwnerSignature.Required) {
                                      // use the incremental nonce
                                      if (_nonce <= relayer[_wallet].nonce) {
                                          return false;
                                      }
                                      uint256 nonceBlock = (_nonce & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) >> 128;
                                      if (nonceBlock > block.number + BLOCKBOUND) {
                                          return false;
                                      }
                                      relayer[_wallet].nonce = _nonce;
                                      return true;
                                  } else {
                                      // use the txHash map
                                      if (relayer[_wallet].executedTx[_signHash] == true) {
                                          return false;
                                      }
                                      relayer[_wallet].executedTx[_signHash] = true;
                                      return true;
                                  }
                              }
                              /**
                              * @notice Validates the signatures provided with a relayed transaction.
                              * The method MUST throw if one or more signatures are not valid.
                              * @param _wallet The target wallet.
                              * @param _signHash The signed hash representing the relayed transaction.
                              * @param _signatures The signatures as a concatenated byte array.
                              * @param _option An enum indicating whether the owner is required, optional or disallowed.
                              * @return A boolean indicating whether the signatures are valid.
                              */
                              function validateSignatures(
                                  address _wallet,
                                  bytes32 _signHash,
                                  bytes memory _signatures,
                                  OwnerSignature _option
                              )
                                  internal
                                  view
                                  returns (bool)
                              {
                                  if (_signatures.length == 0) {
                                      return true;
                                  }
                                  address lastSigner = address(0);
                                  address[] memory guardians;
                                  if (_option != OwnerSignature.Required || _signatures.length > 65) {
                                      guardians = guardianStorage.getGuardians(_wallet); // guardians are only read if they may be needed
                                  }
                                  bool isGuardian;
                                  for (uint256 i = 0; i < _signatures.length / 65; i++) {
                                      address signer = Utils.recoverSigner(_signHash, _signatures, i);
                                      if (i == 0) {
                                          if (_option == OwnerSignature.Required) {
                                              // First signer must be owner
                                              if (isOwner(_wallet, signer)) {
                                                  continue;
                                              }
                                              return false;
                                          } else if (_option == OwnerSignature.Optional) {
                                              // First signer can be owner
                                              if (isOwner(_wallet, signer)) {
                                                  continue;
                                              }
                                          }
                                      }
                                      if (signer <= lastSigner) {
                                          return false; // Signers must be different
                                      }
                                      lastSigner = signer;
                                      (isGuardian, guardians) = GuardianUtils.isGuardianOrGuardianSigner(guardians, signer);
                                      if (!isGuardian) {
                                          return false;
                                      }
                                  }
                                  return true;
                              }
                              /**
                              * @notice Refunds the gas used to the Relayer.
                              * @param _wallet The target wallet.
                              * @param _startGas The gas provided at the start of the execution.
                              * @param _gasPrice The gas price for the refund.
                              * @param _gasLimit The gas limit for the refund.
                              * @param _refundToken The token to use for the gas refund.
                              * @param _refundAddress The address refunded to prevent front-running.
                              * @param _requiredSignatures The number of signatures required.
                              */
                              function refund(
                                  address _wallet,
                                  uint _startGas,
                                  uint _gasPrice,
                                  uint _gasLimit,
                                  address _refundToken,
                                  address _refundAddress,
                                  uint256 _requiredSignatures
                              )
                                  internal
                              {
                                  address refundAddress = _refundAddress == address(0) ? msg.sender : _refundAddress;
                                  uint256 refundAmount;
                                  // skip daily limit when approved by guardians (and signed by owner)
                                  if (_requiredSignatures > 1) {
                                      uint256 gasConsumed = _startGas.sub(gasleft()).add(30000);
                                      refundAmount = Utils.min(gasConsumed, _gasLimit).mul(_gasPrice);
                                  } else {
                                      uint256 gasConsumed = _startGas.sub(gasleft()).add(40000);
                                      refundAmount = Utils.min(gasConsumed, _gasLimit).mul(_gasPrice);
                                      uint256 ethAmount = (_refundToken == ETH_TOKEN) ? refundAmount : LimitUtils.getEtherValue(tokenPriceRegistry, refundAmount, _refundToken);
                                      require(LimitUtils.checkAndUpdateDailySpent(limitStorage, versionManager, _wallet, ethAmount), "RM: refund is above daily limit");
                                  }
                                  // refund in ETH or ERC20
                                  if (_refundToken == ETH_TOKEN) {
                                      invokeWallet(_wallet, refundAddress, refundAmount, EMPTY_BYTES);
                                  } else {
                                      bytes memory methodData = abi.encodeWithSignature("transfer(address,uint256)", refundAddress, refundAmount);
                          		    bytes memory transferSuccessBytes = invokeWallet(_wallet, _refundToken, 0, methodData);
                                      // Check token refund is successful, when `transfer` returns a success bool result
                                      if (transferSuccessBytes.length > 0) {
                                          require(abi.decode(transferSuccessBytes, (bool)), "RM: Refund transfer failed");
                                      }
                                  }
                                  emit Refund(_wallet, refundAddress, _refundToken, refundAmount);
                              }
                             /**
                              * @notice Returns the current chainId
                              * @return chainId the chainId
                              */
                              function getChainId() private pure returns (uint256 chainId) {
                                  // solhint-disable-next-line no-inline-assembly
                                  assembly { chainId := chainid() }
                              }
                          }

                          File 2 of 6: TRYfinance
                          /* TRY token has been being developed since 10/2020 and has more features than most other DEFI token's on the market, here is a summary of built in features that TRY token offers:
                           
                           * We added a brand new feature never seen before in any DEFI token, a tx reward pool, 1% of all transactions are given to the reward pool and are awarded to the sender of every 25th transaction.
                           * We added a deflationary burn fee of 1% on every tx which is automatically sent directly to the burn address upon every transfer, this feature will ensure a truly deflationary model.
                           * We wanted to discourage token dumping so we added a 5% antiDumpFee to all TRY sold on UNIswap. This fee is distributed to all TRYstake users when buyback feature is performed.
                           * Previous rebalance liquidity models used a liquidity divisor as a liquidity reward, however that process made the rebalance feature not as effective since it had to rebalance its own rewards.
                           * To help replace the removal of awarding liquidity providers via the Buyback function we will allow LP tokens to farm TRY tokens directly on TRYfarm.
                           * We coded this contract to have the ability to ADDfunds into TRYstake so it can directly be its own UNIswap sell fee rewards distributor. The staking rewards distribution is called every time 
                             a user performs the rebalance liquidity function. The rebalance function still burns TRY that it purchases with the rebalance increasing the effectiveness of the deflationary model.
                           * When Buyback function is called the caller gets a 4% reward of the buyback TRY amount and 96% of the buyback TRY amount gets sent directly to the burn address.
                           * We coded the buyback function to work on 2 hour intervals and set the rate to 1%, we also added the ability for this contract to add 20 seconds to the buyback interval on each use of the 
                             buyback function. This will help ensure that the buyback feature cannot be manipulated and insure maximum life expectancy of the feature.
                           * We ensured that all of TRY protocols are whitelist able so when you use them you will not incur any transactional fee's when sending TRY to those protocols.
                           * Once this contract creates the UNIswap pair the LP tokens that are sent back are unable to be removed, there is no withdrawal code for these LP tokens this locked them for their intended purpose forever.
                           * We added the ability to add and remove blacklist addresses, this will help insure that we can properly fight hackers and malicious intents on TRY token's economy.
                           * We added createUNISwapPair function that will ensure ETH collected for liquidity can only be used for that one specific purpose, TRY presale contract automatically sends ETH liquidity to this contract.
                           * We are sure that TRY will be the most successful project to ever use a rebalancer style feature, TRYstake will ensure TRY tokens are happy earning in the staking contracts and not on the market to lower 
                             the price. UNIswap sell fees will discourage selling, while offering incentivized rewards for staking. TRYfarm will directly reward liquidity providers in replacement of the liquidity reward distribution 
                             on the previous model. The Tx Reward pool feature helps complete the package, TRY token has the most rewarding features of any DEFI token!
                           
                           For more information please visit try.finance/whitepaper.html 
                          */
                          
                          pragma solidity ^0.5.17;
                          
                          
                          contract Context {
                          
                              constructor () internal { }
                          
                          
                              function _msgSender() internal view returns (address payable) {
                                  return msg.sender;
                              }
                          
                              function _msgData() internal view returns (bytes memory) {
                                  this; 
                                  return msg.data;
                              }
                          }
                          
                          contract WhitelistAdminRole is Context {
                              using Roles for Roles.Role;
                          
                              event WhitelistAdminAdded(address indexed account);
                              event WhitelistAdminRemoved(address indexed account);
                          
                              Roles.Role private _whitelistAdmins;
                          
                              constructor () internal {
                                  _addWhitelistAdmin(_msgSender());
                              }
                          
                              modifier onlyWhitelistAdmin() {
                                  require(isWhitelistAdmin(_msgSender()), "WhitelistAdminRole: caller does not have the WhitelistAdmin role");
                                  _;
                              }
                          
                              function isWhitelistAdmin(address account) public view returns (bool) {
                                  return _whitelistAdmins.has(account);
                              }
                              function addWhitelistAdmin(address account) public onlyWhitelistAdmin {
                                  _addWhitelistAdmin(account);
                              }
                          
                              function renounceWhitelistAdmin() public {
                                  _removeWhitelistAdmin(_msgSender());
                              }
                          
                              function _addWhitelistAdmin(address account) internal {
                                  _whitelistAdmins.add(account);
                                  emit WhitelistAdminAdded(account);
                              } 
                          
                              function _removeWhitelistAdmin(address account) internal {
                                  _whitelistAdmins.remove(account);
                                  emit WhitelistAdminRemoved(account);
                              }
                          }
                          
                          
                          interface IERC20 {
                          
                              function totalSupply() external view returns (uint256);
                          
                              function balanceOf(address account) external view returns (uint256);
                               
                              function transfer(address recipient, uint256 amount) external returns (bool);
                          
                              function allowance(address owner, address spender) external view returns (uint256);
                          
                              function approve(address spender, uint256 amount) external returns (bool);
                          
                              function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
                          
                              event Transfer(address indexed from, address indexed to, uint256 value);
                          
                              event Approval(address indexed owner, address indexed spender, uint256 value);
                          }
                          
                          library SafeMath {
                          
                              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                  uint256 c = a + b;
                                  require(c >= a, "SafeMath: addition overflow");
                          
                                  return c;
                              }
                          
                              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                  return sub(a, b, "SafeMath: subtraction overflow");
                              }
                          
                              function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                  require(b <= a, errorMessage);
                                  uint256 c = a - b;
                          
                                  return c;
                              }
                          
                              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                          
                                  if (a == 0) {
                                      return 0;
                                  }
                          
                                  uint256 c = a * b;
                                  require(c / a == b, "SafeMath: multiplication overflow");
                          
                                  return c;
                              }
                          
                              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                  return div(a, b, "SafeMath: division by zero");
                              }
                          
                              function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                          
                                  require(b > 0, errorMessage);
                                  uint256 c = a / b;
                          
                                  return c;
                              }
                          
                              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                  return mod(a, b, "SafeMath: modulo by zero");
                              }
                          
                              function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                  require(b != 0, errorMessage);
                                  return a % b;
                              }
                              function ceil(uint a, uint m) internal pure returns (uint r) {
                                  return (a + m - 1) / m * m;
                              }
                          }
                          
                          contract ERC20 is Context, IERC20 {
                              using SafeMath for uint256;
                          
                              mapping (address => uint256) private _balances;
                              mapping (address => mapping (address => uint256)) private _allowances;
                          
                              uint256 private _totalSupply;
                              constructor (uint256 totalSupply) public {
                                  _mint(_msgSender(),totalSupply);
                              }
                              
                              function totalSupply() public view returns (uint256) {
                                  return _totalSupply;
                              }
                          
                              function balanceOf(address account) public view returns (uint256) {
                                  return _balances[account];
                              }
                          
                              function transfer(address recipient, uint256 amount) public returns (bool) {
                                  _transfer(_msgSender(), recipient, amount);
                                  return true;
                              }
                          
                              function allowance(address owner, address spender) public view returns (uint256) {
                                  return _allowances[owner][spender];
                              }
                          
                              function approve(address spender, uint256 amount) public returns (bool) {
                                  _approve(_msgSender(), spender, amount);
                                  return true;
                              }
                              
                              function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
                                  _transfer(sender, recipient, amount);
                                  _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                                  return true;
                              }
                          
                              function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
                                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                                  return true;
                              }
                          
                              function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
                                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                                  return true;
                              }
                          
                              function _transfer(address sender, address recipient, uint256 amount) internal {
                                  require(sender != address(0), "ERC20: transfer from the zero address");
                                  require(recipient != address(0), "ERC20: transfer to the zero address");
                          
                                  _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                                  _balances[recipient] = _balances[recipient].add(amount);
                                  emit Transfer(sender, recipient, amount);
                              }
                          
                              function _mint(address account, uint256 amount) internal {
                                  require(account != address(0), "ERC20: mint to the zero address");
                          
                                  _totalSupply = _totalSupply.add(amount);
                                  _balances[account] = _balances[account].add(amount);
                                  emit Transfer(address(0), account, amount);
                              }
                          
                              function _burn(address account, uint256 amount) internal {
                                  require(account != address(0), "ERC20: burn from the zero address");
                          
                                  _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                                  _totalSupply = _totalSupply.sub(amount);
                                  emit Transfer(account, address(0), amount);
                              }
                          
                              function _approve(address owner, address spender, uint256 amount) internal {
                                  require(owner != address(0), "ERC20: approve from the zero address");
                                  require(spender != address(0), "ERC20: approve to the zero address");
                          
                                  _allowances[owner][spender] = amount;
                                  emit Approval(owner, spender, amount);
                              }
                          
                              function _burnFrom(address account, uint256 amount) internal {
                                  _burn(account, amount);
                                  _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
                              }
                          }
                          
                          contract ERC20Burnable is Context, ERC20 {
                          
                              function burn(uint256 amount) public {
                                  _burn(_msgSender(), amount);
                              }
                          
                              function burnFrom(address account, uint256 amount) public {
                                  _burnFrom(account, amount);
                              }
                          }
                          
                          library Roles {
                              struct Role {
                                  mapping (address => bool) bearer;
                              }
                          
                              function add(Role storage role, address account) internal {
                                  require(!has(role, account), "Roles: account already has role");
                                  role.bearer[account] = true;
                              }
                          
                              function remove(Role storage role, address account) internal {
                                  require(has(role, account), "Roles: account does not have role");
                                  role.bearer[account] = false;
                              }
                          
                              function has(Role storage role, address account) internal view returns (bool) {
                                  require(account != address(0), "Roles: account is the zero address");
                                  return role.bearer[account];
                              }
                          }
                          
                          contract ERC20Detailed is IERC20 {
                              string private _name;
                              string private _symbol;
                              uint8 private _decimals;
                          
                              constructor (string memory name, string memory symbol, uint8 decimals) public {
                                  _name = name;
                                  _symbol = symbol;
                                  _decimals = decimals;
                              }
                          
                              function name() public view returns (string memory) {
                                  return _name;
                              }
                          
                              function symbol() public view returns (string memory) {
                                  return _symbol;
                              }
                          
                              function decimals() public view returns (uint8) {
                                  return _decimals;
                              }
                          }
                          
                          contract ERC20TransferLiquidityLock is ERC20 {
                              using SafeMath for uint256;
                          
                          
                              event Rebalance(uint256 tokenBurnt);
                              event SupplyTRYStake(uint256 tokenAmount);
                              event RewardStakers(uint256 stakingRewards);
                              
                              address public uniswapV2Router = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
                              address public uniswapV2Factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
                              address public uniswapV2Pair; 
                              address public TRYStake;
                              address public presaleAddress;
                              address public LPFarm; 
                              address public Master = address (uniswapV2Router);     
                              address public Trident = address (this);
                              address payable public treasury;
                              mapping(address => bool) public feelessAddr;
                              mapping(address => bool) public unlocked;
                              mapping(address => bool) public oracle; 
                              mapping(address => bool) public blacklist; 
                              
                              uint256 public rewardPoolDivisor;
                              uint256 public rebalanceRewardDivisor;
                              uint256 public rebalanceDivisor; 
                              uint256 public burnTxFee;    
                              uint256 public antiDumpFee;       
                              uint256 public minRebalanceAmount;
                              uint256 public lastRebalance;
                              uint256 public rebalanceInterval;
                              address public burnAddr = 0x000000000000000000000000000000000000dEaD;
                              bool public LPLocked; 
                              
                              uint256 public txNumber;
                              uint256 one = 1000000000000000000;
                              uint256 public trans100 = 25000000000000000000; 
                              
                              uint256 public stakePool = 0;
                              uint256 public rewardPool = 0;    
                          
                              bool public locked;
                              Balancer balancer;
                              
                              constructor() public {
                                  lastRebalance = block.timestamp;
                                  burnTxFee = 100;
                                  rewardPoolDivisor = 100;
                                  antiDumpFee = 20;
                                  rebalanceRewardDivisor = 25;
                                  rebalanceDivisor = 100;
                                  rebalanceInterval = 2 hours;
                                  minRebalanceAmount = 100e18; 
                                  treasury = msg.sender;
                                  balancer = new Balancer(treasury);
                                  feelessAddr[address(this)] = true;
                                  feelessAddr[address(balancer)] = true;
                                  feelessAddr[address(uniswapV2Router)] = true; 
                                  feelessAddr[address(uniswapV2Factory)] = true;        
                                  feelessAddr[address(TRYStake)] = true; 
                                  feelessAddr[address(presaleAddress)] = true;
                                  locked = true;
                                  LPLocked = true;
                                  unlocked[msg.sender] = false;
                                  unlocked[address(this)] = true;
                                  unlocked[address(balancer)] = true; 
                                  unlocked[address(balancer)] = true; 
                                  unlocked[address(uniswapV2Router)] = true;
                                  unlocked[address(presaleAddress)] = true;
                                  txNumber = 0;
                              } 
                              
                              function calculateFees(address from, address to, uint256 amount) public view returns( uint256 rewardtx, uint256  Burntx, uint256  selltx){
                              }
                              
                              function isContract(address _addr) public view returns (bool _isContract){
                                  uint32 size;
                                  assembly {
                                  size := extcodesize(_addr)}
                                  
                                  return (size > 0);
                              }
                          
                              function _transfer(address from, address to, uint256 amount) internal {
                                  
                                  if(locked && unlocked[from] != true && unlocked[to] != true)
                                      revert("Transfers are locked until after presale.");
                          
                                  if(blacklist [from] == true || blacklist [to] == true) 
                                      revert("Address is blacklisted");
                                    
                                 uint256  Burntx = 0;
                                  uint256  rewardtx = 0;
                                  
                              if(feelessAddr[from] == false && feelessAddr[to] == false){    
                                  
                                 if (burnTxFee != 0) { 
                                  Burntx = amount.div(burnTxFee); 
                                  amount = amount.sub(Burntx);
                                     super._transfer(from, address(burnAddr), Burntx); 
                                  } 
                                  
                                  if (rewardPoolDivisor != 0) { 
                                      txNumber = txNumber.add(one);
                                      rewardtx = amount.div(rewardPoolDivisor); 
                                      amount = amount.sub(rewardtx);
                                      super._transfer(from, address(this), rewardtx); 
                                    
                                      rewardPool += rewardtx;
                                      if(txNumber == trans100){
                                          require( !(isContract(from)), 'inValid caller');
                                          super._transfer(address(this), from, rewardPool);
                                          rewardPool = 0;
                                          txNumber = 0;  
                                      }
                                  }
                                  
                                  if (antiDumpFee != 0 && oracle[to]) {
                                     uint256 selltx = amount.div(antiDumpFee); 
                                     stakePool += selltx;
                                     amount = amount.sub(selltx);
                                          super._transfer(from, address(this), selltx);
                                      }
                                      
                                   super._transfer(from, to, amount);
                                  }
                              
                                  else {
                                   super._transfer(from, to, amount);   
                                  }
                              }
                          
                          
                              function () external payable {}
                          
                              function RebalanceLiquidity() public {
                                  require(balanceOf(msg.sender) >= minRebalanceAmount, "You do not have the required amount of TRY.");
                                  require(block.timestamp > lastRebalance + rebalanceInterval, "It is too early to use this function."); 
                                  lastRebalance = block.timestamp;
                                  uint256 _lockableSupply = stakePool;  
                                  _addRebalanceInterval();        
                                  _rewardStakers(_lockableSupply);
                                  
                                  uint256 amountToRemove = ERC20(uniswapV2Pair).balanceOf(address(this)).div(rebalanceDivisor);
                                  
                                  remLiquidity(amountToRemove);
                                  uint _locked = balancer.rebalance(rebalanceRewardDivisor);
                          
                                  emit Rebalance(_locked);
                              }
                              
                              function _addRebalanceInterval() private {
                                  rebalanceInterval = rebalanceInterval.add(20 seconds);
                              }
                              
                              function _rewardStakers(uint256 stakingRewards) private {
                                  if(TRYStake != address(0)) {
                                     TRYstakingContract(TRYStake).ADDFUNDS(stakingRewards);
                                     stakePool= 0;
                                      emit RewardStakers(stakingRewards); 
                                  }
                              }
                          
                              function remLiquidity(uint256 lpAmount) private returns(uint ETHAmount) {
                                  ERC20(uniswapV2Pair).approve(uniswapV2Router, lpAmount);
                                  (ETHAmount) = IUniswapV2Router02(uniswapV2Router)
                                      .removeLiquidityETHSupportingFeeOnTransferTokens(
                                          address(this),
                                          lpAmount,
                                          0,
                                          0,
                                          address(balancer),
                                          block.timestamp);
                              }
                              
                          
                              function lockableSupply() external view returns (uint256) {
                                  return balanceOf(address(this));
                              }
                          
                              function lockedSupply() external view returns (uint256) {
                                  uint256 lpTotalSupply = ERC20(uniswapV2Pair).totalSupply();
                                  uint256 lpBalance = lockedLiquidity();
                                  uint256 percentOfLpTotalSupply = lpBalance.mul(1e12).div(lpTotalSupply);
                          
                                  uint256 uniswapBalance = balanceOf(uniswapV2Pair);
                                  uint256 _lockedSupply = uniswapBalance.mul(percentOfLpTotalSupply).div(1e12);
                                  return _lockedSupply;
                              }
                          
                              function burnedSupply() external view returns (uint256) {
                                  uint256 lpTotalSupply = ERC20(uniswapV2Pair).totalSupply();
                                  uint256 lpBalance = burnedLiquidity();
                                  uint256 percentOfLpTotalSupply = lpBalance.mul(1e12).div(lpTotalSupply);
                          
                                  uint256 uniswapBalance = balanceOf(uniswapV2Pair);
                                  uint256 _burnedSupply = uniswapBalance.mul(percentOfLpTotalSupply).div(1e12);
                                  return _burnedSupply;
                              }
                          
                              function burnableLiquidity() public view returns (uint256) {
                                  return ERC20(uniswapV2Pair).balanceOf(address(this));
                              }
                          
                              function burnedLiquidity() public view returns (uint256) {
                                  return ERC20(uniswapV2Pair).balanceOf(address(0));
                              }
                          
                              function lockedLiquidity() public view returns (uint256) {
                                  return burnableLiquidity().add(burnedLiquidity());
                              }
                          }
                          
                          interface TRYstakingContract {
                              function ADDFUNDS(uint256 stakingRewards) external;
                          }
                          
                          interface IUniswapV2Router01 {
                              function factory() external pure returns (address);
                              function WETH() external pure returns (address);
                              function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
                              function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
                              function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
                              function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
                              function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
                          
                              function addLiquidityETH(
                                  address token,
                                  uint amountTokenDesired,
                                  uint amountTokenMin,
                                  uint amountETHMin,
                                  address to,
                                  uint deadline
                              ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
                          }
                          
                          interface IUniswapV2Router02 {
                              function WETH() external pure returns (address);
                              function swapExactETHForTokensSupportingFeeOnTransferTokens(
                                uint amountOutMin,
                                address[] calldata path,
                                address to,
                                uint deadline
                              ) external payable;
                              function removeLiquidityETH(
                                address token,
                                uint liquidity,
                                uint amountTokenMin,
                                uint amountETHMin,
                                address to,
                                uint deadline
                              ) external returns (uint amountToken, uint amountETH);
                              function removeLiquidityETHSupportingFeeOnTransferTokens(
                                address token,
                                uint liquidity,
                                uint amountTokenMin,
                                uint amountETHMin,
                                address to,
                                uint deadline
                              ) external returns (uint amountETH);    
                          }
                          
                          interface IUniswapV2Pair {
                              function sync() external;
                          }
                          
                          contract ERC20Governance is ERC20, ERC20Detailed {
                              using SafeMath for uint256;
                          
                              function _transfer(address from, address to, uint256 amount) internal {
                                  _moveDelegates(_delegates[from], _delegates[to], amount);
                                  super._transfer(from, to, amount);
                              }
                          
                              function _mint(address account, uint256 amount) internal {
                                  _moveDelegates(address(0), _delegates[account], amount);
                                  super._mint(account, amount);
                              }
                          
                              function _burn(address account, uint256 amount) internal {
                                  _moveDelegates(_delegates[account], address(0), amount);
                                  super._burn(account, amount);
                              }
                          
                              mapping (address => address) internal _delegates;
                          
                              struct Checkpoint {
                                  uint32 fromBlock;
                                  uint256 votes;
                              }
                              mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
                          
                              mapping (address => uint32) public numCheckpoints;
                          
                              bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
                          
                              bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
                          
                              mapping (address => uint) public nonces;
                          
                              event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
                          
                              event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
                          
                              function delegates(address delegator)
                                  external
                                  view
                                  returns (address)
                              {
                                  return _delegates[delegator];
                              }
                          
                              function delegate(address delegatee) external {
                                  return _delegate(msg.sender, delegatee);
                              }
                          
                              function delegateBySig(
                                  address delegatee,
                                  uint nonce,
                                  uint expiry,
                                  uint8 v,
                                  bytes32 r,
                                  bytes32 s
                              )
                                  external
                              {
                                  bytes32 domainSeparator = keccak256(
                                      abi.encode(
                                          DOMAIN_TYPEHASH,
                                          keccak256(bytes(name())),
                                          getChainId(),
                                          address(this)
                                      )
                                  );
                          
                                  bytes32 structHash = keccak256(
                                      abi.encode(
                                          DELEGATION_TYPEHASH,
                                          delegatee,
                                          nonce,
                                          expiry
                                      )
                                  );
                          
                                  bytes32 digest = keccak256(
                                      abi.encodePacked(
                                          "\x19\x01",
                                          domainSeparator,
                                          structHash
                                      )
                                  );
                          
                                  address signatory = ecrecover(digest, v, r, s);
                                  require(signatory != address(0), "ERC20Governance::delegateBySig: invalid signature");
                                  require(nonce == nonces[signatory]++, "ERC20Governance::delegateBySig: invalid nonce");
                                  require(now <= expiry, "ERC20Governance::delegateBySig: signature expired");
                                  return _delegate(signatory, delegatee);
                              }
                          
                              function getCurrentVotes(address account)
                                  external
                                  view
                                  returns (uint256)
                              {
                                  uint32 nCheckpoints = numCheckpoints[account];
                                  return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
                              }
                          
                              function getPriorVotes(address account, uint blockNumber)
                                  external
                                  view
                                  returns (uint256)
                              {
                                  require(blockNumber < block.number, "ERC20Governance::getPriorVotes: not yet determined");
                          
                                  uint32 nCheckpoints = numCheckpoints[account];
                                  if (nCheckpoints == 0) {
                                      return 0;
                                  }
                          
                                  if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
                                      return checkpoints[account][nCheckpoints - 1].votes;
                                  }
                          
                                  if (checkpoints[account][0].fromBlock > blockNumber) {
                                      return 0;
                                  }
                          
                                  uint32 lower = 0;
                                  uint32 upper = nCheckpoints - 1;
                                  while (upper > lower) {
                                      uint32 center = upper - (upper - lower) / 2; 
                                      Checkpoint memory cp = checkpoints[account][center];
                                      if (cp.fromBlock == blockNumber) {
                                          return cp.votes;
                                      } else if (cp.fromBlock < blockNumber) {
                                          lower = center;
                                      } else {
                                          upper = center - 1;
                                      }
                                  }
                                  return checkpoints[account][lower].votes;
                              }
                          
                              function _delegate(address delegator, address delegatee)
                                  internal
                              {
                                  address currentDelegate = _delegates[delegator];
                                  uint256 delegatorBalance = balanceOf(delegator); 
                                  _delegates[delegator] = delegatee;
                          
                                  emit DelegateChanged(delegator, currentDelegate, delegatee);
                          
                                  _moveDelegates(currentDelegate, delegatee, delegatorBalance);
                              }
                          
                              function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal {
                                  if (srcRep != dstRep && amount > 0) {
                                      if (srcRep != address(0)) {
                                          uint32 srcRepNum = numCheckpoints[srcRep];
                                          uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
                                          uint256 srcRepNew = srcRepOld.sub(amount);
                                          _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
                                      }
                          
                                      if (dstRep != address(0)) {
                                          uint32 dstRepNum = numCheckpoints[dstRep];
                                          uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
                                          uint256 dstRepNew = dstRepOld.add(amount);
                                          _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
                                      }
                                  }
                              }
                          
                              function _writeCheckpoint(
                                  address delegatee,
                                  uint32 nCheckpoints,
                                  uint256 oldVotes,
                                  uint256 newVotes
                              )
                                  internal
                              {
                                  uint32 blockNumber = safe32(block.number, "ERC20Governance::_writeCheckpoint: block number exceeds 32 bits");
                          
                                  if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
                                      checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
                                  } else {
                                      checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
                                      numCheckpoints[delegatee] = nCheckpoints + 1;
                                  }
                          
                                  emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
                              }
                          
                              function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
                                  require(n < 2**32, errorMessage);
                                  return uint32(n);
                              }
                          
                              function getChainId() internal pure returns (uint) {
                                  uint256 chainId;
                                  assembly { chainId := chainid() }
                                  return chainId;
                              }
                          }
                          
                          contract Balancer {
                              using SafeMath for uint256;    
                              TRYfinance token;
                              address public burnAddr = 0x000000000000000000000000000000000000dEaD;
                              address payable public treasury;
                            
                              constructor(address payable treasury_) public {
                                  token = TRYfinance(msg.sender);
                                  treasury = treasury_;
                              }
                              
                              function () external payable {}
                              
                              function rebalance(uint rebalanceRewardDivisor) external returns (uint256) { 
                                  require(msg.sender == address(token), "only token contract can perform this function");
                                  swapEthForTokens(address(this).balance, rebalanceRewardDivisor);
                                  uint256 lockableBalance = token.balanceOf(address(this));
                                  uint256 callerReward = lockableBalance.div(rebalanceRewardDivisor);
                                  token.transfer(tx.origin, callerReward);
                                  token.transfer(burnAddr, lockableBalance.sub(callerReward));  
                                  return lockableBalance.sub(callerReward);
                              }
                              function swapEthForTokens(uint256 EthAmount, uint rebalanceRewardDivisor) private {
                                  address[] memory uniswapPairPath = new address[](2);
                                  uniswapPairPath[0] = IUniswapV2Router02(token.uniswapV2Router()).WETH();
                                  uniswapPairPath[1] = address(token);
                                  uint256 treasuryAmount = EthAmount.div(rebalanceRewardDivisor);
                                  treasury.transfer(treasuryAmount);
                                  
                                  token.approve(token.uniswapV2Router(), EthAmount);
                                  
                                  IUniswapV2Router02(token.uniswapV2Router())
                                      .swapExactETHForTokensSupportingFeeOnTransferTokens.value(EthAmount.sub(treasuryAmount))(
                                          0,
                                          uniswapPairPath,
                                          address(this),
                                          block.timestamp);
                              }        
                          }
                          
                          
                          contract TRYfinance is 
                              ERC20(100000e18), 
                              ERC20Detailed("TRYfinance", "TRY", 18), 
                              ERC20Burnable, 
                              ERC20Governance,
                              ERC20TransferLiquidityLock,
                              WhitelistAdminRole
                              
                          {
                          
                              function createUNISwapPair(uint amountTokenDesired) public onlyWhitelistAdmin {
                                  uint amountETH = address(this).balance;
                                  approve(address(uniswapV2Router), amountTokenDesired);
                                  IUniswapV2Router01(uniswapV2Router).addLiquidityETH.value(amountETH)(
                                      address(this),
                                      amountTokenDesired,
                                      0,
                                      0,
                                      address(this),
                                      now); 
                              }
                              
                              function quickApproveTRYStake() public {
                                  _approve(_msgSender(), TRYStake, 10000e18);
                              } 
                              
                              function quickApproveMaster() public {
                                  _approve(_msgSender(), Master, 10000e18);
                              } 
                           
                              function quickApproveFarm() public {
                                  _approve(_msgSender(), LPFarm, 10000e18);
                              } 
                              
                              function setUniswapV2Router(address _uniswapV2Router) public onlyWhitelistAdmin {
                                  uniswapV2Router = _uniswapV2Router;
                              }
                          
                              function setUniswapV2Pair(address _uniswapV2Pair) public onlyWhitelistAdmin {
                                  uniswapV2Pair = _uniswapV2Pair;  
                              }
                              
                              function setUniswapV2Factory(address _uniswapV2Factory) public onlyWhitelistAdmin {
                                  uniswapV2Factory = _uniswapV2Factory; 
                              }
                          
                              function setTrans100(uint256 _trans100) public onlyWhitelistAdmin {
                                  require(_trans100 <= 100e18, "Cannot set over 100 transactions");        
                                  trans100 = _trans100; 
                              }
                          
                              function setRewardPoolDivisor(uint256 _rdiv) public onlyWhitelistAdmin {
                                  require(_rdiv >= 100, "Cannot set over 1% RewardPoolDivisor");        
                                  rewardPoolDivisor = _rdiv;
                              } 
                              
                              function setRebalanceDivisor(uint256 _rebalanceDivisor) public onlyWhitelistAdmin {
                                  if (_rebalanceDivisor != 0) {
                                      require(_rebalanceDivisor >= 10, "Cannot set rebalanceDivisor over 10%");
                                      require(_rebalanceDivisor <= 100, "Cannot set rebalanceDivisor under 1%");
                                  }        
                                  rebalanceDivisor = _rebalanceDivisor;
                              }
                              
                              function addTRYStake(address _stake) public onlyWhitelistAdmin {
                                  TRYStake = _stake;
                              }
                          
                              function addPresaleAddress(address _presaleaddress) public onlyWhitelistAdmin {
                                  presaleAddress = _presaleaddress;  
                              }
                              
                              function addLPFarm(address _farm) public onlyWhitelistAdmin {
                                  LPFarm = _farm;  
                              }
                          
                              function addMaster(address _master) public onlyWhitelistAdmin {
                                  Master = _master;  
                              }
                               
                              function addTrident(address _Trident) public onlyWhitelistAdmin {
                                  Trident = _Trident;
                              } 
                              
                              function setMaster () public onlyWhitelistAdmin { 
                                  ERC20(Trident).approve(Master, 100000e18);       
                              }  
                              
                              function setTrident () public onlyWhitelistAdmin {
                                  ERC20(Trident).approve(TRYStake, 100000e18);        
                              }  
                              
                              function rewardStaking(uint256 stakingRewards) internal {
                                      TRYstakingContract(TRYStake).ADDFUNDS(stakingRewards);
                                      emit SupplyTRYStake(stakingRewards); 
                              }
                           
                              function setRebalanceInterval(uint256 _interval) public onlyWhitelistAdmin {
                                  require(_interval<= 7200, "Cannot set over 2 hour interval");  
                                  require(_interval>= 3600, "Cannot set under 1 hour interval");
                                  rebalanceInterval = _interval;
                              }
                               
                              function setRebalanceRewardDivisior(uint256 _rDivisor) public onlyWhitelistAdmin {
                                  if (_rDivisor != 0) {
                                      require(_rDivisor <= 25, "Cannot set rebalanceRewardDivisor under 4%");
                                      require(_rDivisor >= 10, "Cannot set rebalanceRewardDivisor over 10%");
                                  }        
                                  rebalanceRewardDivisor = _rDivisor;
                              }
                              
                              function toggleFeeless(address _addr) public onlyWhitelistAdmin {
                                  feelessAddr[_addr] = true;
                              }
                              
                              function toggleFees(address _addr) public onlyWhitelistAdmin {
                                  feelessAddr[_addr] = false;
                              }
                              
                              function toggleUnlocked(address _addr) public onlyWhitelistAdmin {
                                  unlocked[_addr] = !unlocked[_addr];
                              } 
                              
                              function setOracle(address _addr, bool _bool) public onlyWhitelistAdmin {  
                                  oracle[_addr] = _bool; 
                              }  
                           
                              function setBlackListAddress(address _addr, bool _bool) public onlyWhitelistAdmin { 
                                  blacklist[_addr] = _bool; 
                              } 
                              
                              function activateTrading() public onlyWhitelistAdmin {
                                  locked = false;
                              }   
                           
                              function setMinRebalanceAmount(uint256 amount_) public onlyWhitelistAdmin {
                                  require(amount_ <= 1000e18, "Cannot set over 1000 TRY tokens");
                                  require(amount_ >= 20e18, "Cannot set under 20 TRY tokens");
                                  minRebalanceAmount = amount_;
                              }
                              
                              function setBurnTxFee(uint256 amount_) public onlyWhitelistAdmin {
                                  require(amount_ >= 100, "Cannot set over 1% burnTxFee"); 
                                  burnTxFee = amount_;
                              }
                              
                              function setAntiDumpFee(uint256 amount_) public onlyWhitelistAdmin {
                                  require(amount_ >= 10, "Cannot set over 10% antiDumpFee"); 
                                  require(amount_ <= 100, "Cannot set under 1% antiDumpFee");
                                  antiDumpFee = amount_;
                              }
                          }

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

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

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