ETH Price: $2,220.46 (-2.93%)

Transaction Decoder

Block:
20389683 at Jul-26-2024 08:58:59 AM +UTC
Transaction Fee:
0.000429913637862576 ETH $0.95
Gas Used:
138,516 Gas / 3.103711036 Gwei

Emitted Events:

229 GraphToken.Transfer( from=Proxy, to=0xAcA25dd25FDFdD7e3f85ddB8D4F36d690b1ba637, value=169620000000000000000000 )
230 Proxy.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x0000000000000000000000009d58779365b067d5d3fcc6e92d237acd06f1e6a1, 0x000000000000000000000000c944e90c64b2c07662a292be6244bdf05cda44a7, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000044, a9059cbb000000000000000000000000aca25dd25fdfdd7e3f85ddb8d4f36d69, 0b1ba6370000000000000000000000000000000000000000000023eb1e5e32b9, 5dd0000000000000000000000000000000000000000000000000000000000000 )
231 Proxy.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x0000000000000000000000009d58779365b067d5d3fcc6e92d237acd06f1e6a1, 0x000000000000000000000000482579f93dc13e6b434e38b5a0447ca543d88a46, 0x000000000000000000000000000000000000000000000000000186ecd5cad220, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000000 )
232 ArgentModule.Refund( wallet=Proxy, refundAddress=0x482579f93dc13e6b434e38b5a0447ca543d88a46, refundToken=0x00000000...000000000, refundAmount=429826733953568 )
233 ArgentModule.TransactionExecuted( wallet=Proxy, success=True, returnData=0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001, signedHash=D4822FA4FDA10D9F339C5C61AF5500104E8D7780D293430D8F2377BD214C2408 )

Account State Difference:

  Address   Before After State Difference Code
0x0cf0C8eC...018e3392B 0.435879610174779746 Eth0.435449783440826178 Eth0.000429826733953568
0x482579F9...543D88A46 9.40880346175328079 Eth9.409233288487234358 Eth0.000429826733953568
(beaverbuild)
16.268957599330456914 Eth16.268967295450456914 Eth0.00000969612
0x9D587793...d06F1e6a1
(Argent: Argent Module)
0xc944E90C...05Cda44a7
0xF27696C8...f59342fA6
(Argent: Relayer 3)
4.684514097361995691 Eth
Nonce: 375396
4.684084183724133115 Eth
Nonce: 375397
0.000429913637862576

Execution Trace

ArgentModule.execute( _wallet=0x0cf0C8eCABb20ce048E4F86B417EABb018e3392B, _data=0xA5EFB2350000000000000000000000000CF0C8ECABB20CE048E4F86B417EABB018E3392B000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000C944E90C64B2C07662A292BE6244BDF05CDA44A7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044A9059CBB000000000000000000000000ACA25DD25FDFDD7E3F85DDB8D4F36D690B1BA6370000000000000000000000000000000000000000000023EB1E5E32B95DD0000000000000000000000000000000000000000000000000000000000000, _nonce=6938249251725254411586826892408590531018581325, _signatures=0xEE1162CEDA7AF74661BA5F92C8478131368C863AD5F1124BAF16B52C8D647747260B779F38458B222A952EA7A3268100BC6D9FD3C2EDB265B3F47C1C701E76F61B, _gasPrice=3850000000, _gasLimit=141094, _refundToken=0x0000000000000000000000000000000000000000, _refundAddress=0x482579F93dC13e6B434E38b5a0447ca543D88A46 ) => ( True )
  • Null: 0x000...001.d4822fa4( )
  • Proxy.STATICCALL( )
  • ArgentModule.multiCall( _wallet=0x0cf0C8eCABb20ce048E4F86B417EABb018e3392B, _transactions= ) => ( [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=] )
    • 0x391f0e86da951c03b1183c60b195090671adea88.13f4a0ea( )
    • Proxy.8f6f0332( )
      • BaseWallet.invoke( _target=0xc944E90C64B2c07662A292be6244BDf05Cda44a7, _value=0, _data=0xA9059CBB000000000000000000000000ACA25DD25FDFDD7E3F85DDB8D4F36D690B1BA6370000000000000000000000000000000000000000000023EB1E5E32B95DD00000 ) => ( _result=0x0000000000000000000000000000000000000000000000000000000000000001 )
        • GraphToken.transfer( recipient=0xAcA25dd25FDFdD7e3f85ddB8D4F36d690b1ba637, amount=169620000000000000000000 ) => ( True )
        • DappRegistry.isAuthorised( _wallet=0x0cf0C8eCABb20ce048E4F86B417EABb018e3392B, _spender=0x482579F93dC13e6B434E38b5a0447ca543D88A46, _to=0x0000000000000000000000000000000000000000, _data=0x ) => ( True )
        • Proxy.8f6f0332( )
          • BaseWallet.invoke( _target=0x482579F93dC13e6B434E38b5a0447ca543D88A46, _value=429826733953568, _data=0x ) => ( _result=0x )
            • ETH 0.000429826733953568 0x482579f93dc13e6b434e38b5a0447ca543d88a46.CALL( )
              File 1 of 5: ArgentModule
              // Copyright (C) 2021  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.8.3;
              interface IAuthoriser {
                  function isAuthorised(address _sender, address _spender, address _to, bytes calldata _data) external view returns (bool);
                  function areAuthorised(
                      address _spender,
                      address[] calldata _spenders,
                      address[] calldata _to,
                      bytes[] calldata _data
                  )
                      external
                      view
                      returns (bool);
              }// 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.9.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);
              }// 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.9.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);
              }// 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.9.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) 2021  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.8.3;
              import "./common/Utils.sol";
              import "./common/BaseModule.sol";
              import "./RelayerManager.sol";
              import "./SecurityManager.sol";
              import "./TransactionManager.sol";
              /**
               * @title ArgentModule
               * @notice Single module for the Argent wallet.
               * @author Julien Niset - <[email protected]>
               */
              contract ArgentModule is BaseModule, RelayerManager, SecurityManager, TransactionManager {
                  bytes32 constant public NAME = "ArgentModule";
                  constructor (
                      IModuleRegistry _registry,
                      IGuardianStorage _guardianStorage,
                      ITransferStorage _userWhitelist,
                      IAuthoriser _authoriser,
                      address _uniswapRouter,
                      uint256 _securityPeriod,
                      uint256 _securityWindow,
                      uint256 _recoveryPeriod,
                      uint256 _lockPeriod
                  )
                      BaseModule(_registry, _guardianStorage, _userWhitelist, _authoriser, NAME)
                      SecurityManager(_recoveryPeriod, _securityPeriod, _securityWindow, _lockPeriod)
                      TransactionManager(_securityPeriod)
                      RelayerManager(_uniswapRouter)
                  {
                      
                  }
                  /**
                   * @inheritdoc IModule
                   */
                  function init(address _wallet) external override onlyWallet(_wallet) {
                      enableDefaultStaticCalls(_wallet);
                  }
                  /**
                  * @inheritdoc IModule
                  */
                  function addModule(address _wallet, address _module) external override onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
                      require(registry.isRegisteredModule(_module), "AM: module is not registered");
                      IWallet(_wallet).authoriseModule(_module, true);
                  }
                  
                  /**
                   * @inheritdoc RelayerManager
                   */
                  function getRequiredSignatures(address _wallet, bytes calldata _data) public view override returns (uint256, OwnerSignature) {
                      bytes4 methodId = Utils.functionPrefix(_data);
                      if (methodId == TransactionManager.multiCall.selector ||
                          methodId == TransactionManager.addToWhitelist.selector ||
                          methodId == TransactionManager.removeFromWhitelist.selector ||
                          methodId == TransactionManager.enableERC1155TokenReceiver.selector ||
                          methodId == TransactionManager.clearSession.selector ||
                          methodId == ArgentModule.addModule.selector ||
                          methodId == SecurityManager.addGuardian.selector ||
                          methodId == SecurityManager.revokeGuardian.selector ||
                          methodId == SecurityManager.cancelGuardianAddition.selector ||
                          methodId == SecurityManager.cancelGuardianRevokation.selector)
                      {
                          // owner
                          return (1, OwnerSignature.Required);
                      }
                      if (methodId == TransactionManager.multiCallWithSession.selector) {
                          return (1, OwnerSignature.Session);
                      }
                      if (methodId == SecurityManager.executeRecovery.selector) {
                          // majority of guardians
                          uint numberOfSignaturesRequired = _majorityOfGuardians(_wallet);
                          require(numberOfSignaturesRequired > 0, "AM: no guardians set on wallet");
                          return (numberOfSignaturesRequired, OwnerSignature.Disallowed);
                      }
                      if (methodId == SecurityManager.cancelRecovery.selector) {
                          // majority of (owner + guardians)
                          uint numberOfSignaturesRequired = Utils.ceil(recoveryConfigs[_wallet].guardianCount + 1, 2);
                          return (numberOfSignaturesRequired, OwnerSignature.Optional);
                      }
                      if (methodId == TransactionManager.multiCallWithGuardians.selector ||
                          methodId == TransactionManager.multiCallWithGuardiansAndStartSession.selector ||
                          methodId == SecurityManager.transferOwnership.selector)
                      {
                          // owner + majority of guardians
                          uint majorityGuardians = _majorityOfGuardians(_wallet);
                          uint numberOfSignaturesRequired = majorityGuardians + 1;
                          return (numberOfSignaturesRequired, OwnerSignature.Required);
                      }
                      if (methodId == SecurityManager.finalizeRecovery.selector ||
                          methodId == SecurityManager.confirmGuardianAddition.selector ||
                          methodId == SecurityManager.confirmGuardianRevokation.selector)
                      {
                          // anyone
                          return (0, OwnerSignature.Anyone);
                      }
                      if (methodId == SecurityManager.lock.selector || methodId == SecurityManager.unlock.selector) {
                          // any guardian
                          return (1, OwnerSignature.Disallowed);
                      }
                      revert("SM: unknown method");
                  }
                  function _majorityOfGuardians(address _wallet) internal view returns (uint) {
                      return Utils.ceil(guardianStorage.guardianCount(_wallet), 2);
                  }
              }// 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.8.3;
              import "@openzeppelin/contracts/utils/math/Math.sol";
              import "./common/Utils.sol";
              import "./common/BaseModule.sol";
              import "./common/SimpleOracle.sol";
              import "../infrastructure/storage/IGuardianStorage.sol";
              /**
               * @title RelayerManager
               * @notice Abstract Module to execute transactions signed by ETH-less accounts and sent by a relayer.
               * @author Julien Niset <[email protected]>, Olivier VDB <[email protected]>
               */
              abstract contract RelayerManager is BaseModule, SimpleOracle {
                  uint256 constant internal BLOCKBOUND = 10000;
                  mapping (address => RelayerConfig) internal relayer;
                  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);
                  // *************** Constructor ************************ //
                  constructor(address _uniswapRouter) SimpleOracle(_uniswapRouter) {
                  }
                  /* ***************** External methods ************************* */
                  /**
                  * @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) public view virtual returns (uint256, OwnerSignature);
                  /**
                  * @notice Executes a relayed transaction.
                  * @param _wallet The target wallet.
                  * @param _data The data for the relayed transaction
                  * @param _nonce The nonce used to prevent replay attacks.
                  * @param _signatures The signatures as a concatenated byte array.
                  * @param _gasPrice The max gas price (in token) to use for the gas refund.
                  * @param _gasLimit The max 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,
                      bytes calldata _data,
                      uint256 _nonce,
                      bytes calldata _signatures,
                      uint256 _gasPrice,
                      uint256 _gasLimit,
                      address _refundToken,
                      address _refundAddress
                  )
                      external
                      returns (bool)
                  {
                      // initial gas = 21k + non_zero_bytes * 16 + zero_bytes * 4
                      //            ~= 21k + calldata.length * [1/3 * 16 + 2/3 * 4]
                      uint256 startGas = gasleft() + 21000 + msg.data.length * 8;
                      require(startGas >= _gasLimit, "RM: not enough gas provided");
                      require(verifyData(_wallet, _data), "RM: Target of _data != _wallet");
                      require(!_isLocked(_wallet) || _gasPrice == 0, "RM: Locked wallet refund");
                      StackExtension memory stack;
                      (stack.requiredSignatures, stack.ownerSignatureRequirement) = 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),
                          0,
                          _data,
                          _nonce,
                          _gasPrice,
                          _gasLimit,
                          _refundToken,
                          _refundAddress);
                      require(checkAndUpdateUniqueness(
                          _wallet,
                          _nonce,
                          stack.signHash,
                          stack.requiredSignatures,
                          stack.ownerSignatureRequirement), "RM: Duplicate request");
                      if (stack.ownerSignatureRequirement == OwnerSignature.Session) {
                          require(validateSession(_wallet, stack.signHash, _signatures), "RM: Invalid session");
                      } else {
                          require(validateSignatures(_wallet, stack.signHash, _signatures, stack.ownerSignatureRequirement), "RM: Invalid signatures");
                      }
                      (stack.success, stack.returnData) = address(this).call(_data);
                      refund(
                          _wallet,
                          startGas,
                          _gasPrice,
                          _gasLimit,
                          _refundToken,
                          _refundAddress,
                          stack.requiredSignatures,
                          stack.ownerSignatureRequirement);
                      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];
                  }
                  /**
                  * @notice Gets the last stored session for a wallet.
                  * @param _wallet The target wallet.
                  */
                  function getSession(address _wallet) external view returns (address key, uint64 expires) {
                      return (sessions[_wallet].key, sessions[_wallet].expires);
                  }
                  /* ***************** 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 _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 max gas price (in token) to use for the gas refund.
                  * @param _gasLimit The max 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,
                      uint256 _value,
                      bytes memory _data,
                      uint256 _nonce,
                      uint256 _gasPrice,
                      uint256 _gasLimit,
                      address _refundToken,
                      address _refundAddress
                  )
                      internal
                      view
                      returns (bytes32)
                  {
                      return keccak256(
                          abi.encodePacked(
                              "\\x19Ethereum Signed Message:\
              32",
                              keccak256(abi.encodePacked(
                                  bytes1(0x19),
                                  bytes1(0),
                                  _from,
                                  _value,
                                  _data,
                                  block.chainid,
                                  _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 or a session key 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 || ownerSignatureRequirement == OwnerSignature.Session)) {
                          // 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.
                  * @param _wallet The target wallet.
                  * @param _signHash The signed hash representing the relayed transaction.
                  * @param _signatures The signatures as a concatenated bytes array.
                  * @param _option An OwnerSignature 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) = Utils.isGuardianOrGuardianSigner(guardians, signer);
                          if (!isGuardian) {
                              return false;
                          }
                      }
                      return true;
                  }
                  /**
                  * @notice Validates the signature provided when a session key was used.
                  * @param _wallet The target wallet.
                  * @param _signHash The signed hash representing the relayed transaction.
                  * @param _signatures The signatures as a concatenated bytes array.
                  * @return A boolean indicating whether the signature is valid.
                  */
                  function validateSession(address _wallet, bytes32 _signHash, bytes calldata _signatures) internal view returns (bool) { 
                      Session memory session = sessions[_wallet];
                      address signer = Utils.recoverSigner(_signHash, _signatures, 0);
                      return (signer == session.key && session.expires >= block.timestamp);
                  }
                  /**
                  * @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 max gas price (in token) for the refund.
                  * @param _gasLimit The max 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.
                  * @param _option An OwnerSignature enum indicating the signature requirement.
                  */
                  function refund(
                      address _wallet,
                      uint _startGas,
                      uint _gasPrice,
                      uint _gasLimit,
                      address _refundToken,
                      address _refundAddress,
                      uint256 _requiredSignatures,
                      OwnerSignature _option
                  )
                      internal
                  {
                      // Only refund when the owner is one of the signers or a session key was used
                      if (_gasPrice > 0 && (_option == OwnerSignature.Required || _option == OwnerSignature.Session)) {
                          address refundAddress = _refundAddress == address(0) ? msg.sender : _refundAddress;
                          if (_requiredSignatures == 1 && _option == OwnerSignature.Required) {
                                  // refundAddress must be whitelisted/authorised
                                  if (!authoriser.isAuthorised(_wallet, refundAddress, address(0), EMPTY_BYTES)) {
                                      uint whitelistAfter = userWhitelist.getWhitelist(_wallet, refundAddress);
                                      require(whitelistAfter > 0 && whitelistAfter < block.timestamp, "RM: refund not authorised");
                                  }
                          }
                          uint256 refundAmount;
                          if (_refundToken == ETH_TOKEN) {
                              // 23k as an upper bound to cover the rest of refund logic
                              uint256 gasConsumed = _startGas - gasleft() + 23000;
                              refundAmount = Math.min(gasConsumed, _gasLimit) * (Math.min(_gasPrice, tx.gasprice));
                              invokeWallet(_wallet, refundAddress, refundAmount, EMPTY_BYTES);
                          } else {
                              // 37.5k as an upper bound to cover the rest of refund logic
                              uint256 gasConsumed = _startGas - gasleft() + 37500;
                              uint256 tokenGasPrice = inToken(_refundToken, tx.gasprice);
                              refundAmount = Math.min(gasConsumed, _gasLimit) * (Math.min(_gasPrice, tokenGasPrice));
                              bytes memory methodData = abi.encodeWithSelector(ERC20.transfer.selector, 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 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;
                  }
              }// 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.8.3;
              import "@openzeppelin/contracts/utils/math/SafeCast.sol";
              import "./common/Utils.sol";
              import "./common/BaseModule.sol";
              import "../wallet/IWallet.sol";
              /**
               * @title SecurityManager
               * @notice Abstract module implementing the key security features of the wallet: guardians, lock and recovery.
               * @author Julien Niset - <[email protected]>
               * @author Olivier Van Den Biggelaar - <[email protected]>
               */
              abstract contract SecurityManager is BaseModule {
                  struct RecoveryConfig {
                      address recovery;
                      uint64 executeAfter;
                      uint32 guardianCount;
                  }
                  struct GuardianManagerConfig {
                      // The time at which a guardian addition or revokation will be confirmable by the owner
                      mapping (bytes32 => uint256) pending;
                  }
                  // Wallet specific storage for recovery
                  mapping (address => RecoveryConfig) internal recoveryConfigs;
                  // Wallet specific storage for pending guardian addition/revokation
                  mapping (address => GuardianManagerConfig) internal guardianConfigs;
                  // Recovery period
                  uint256 internal immutable recoveryPeriod;
                  // Lock period
                  uint256 internal immutable lockPeriod;
                  // The security period to add/remove guardians
                  uint256 internal immutable securityPeriod;
                  // The security window
                  uint256 internal immutable securityWindow;
                  // *************** Events *************************** //
                  event RecoveryExecuted(address indexed wallet, address indexed _recovery, uint64 executeAfter);
                  event RecoveryFinalized(address indexed wallet, address indexed _recovery);
                  event RecoveryCanceled(address indexed wallet, address indexed _recovery);
                  event OwnershipTransfered(address indexed wallet, address indexed _newOwner);
                  event Locked(address indexed wallet, uint64 releaseAfter);
                  event Unlocked(address indexed wallet);
                  event GuardianAdditionRequested(address indexed wallet, address indexed guardian, uint256 executeAfter);
                  event GuardianRevokationRequested(address indexed wallet, address indexed guardian, uint256 executeAfter);
                  event GuardianAdditionCancelled(address indexed wallet, address indexed guardian);
                  event GuardianRevokationCancelled(address indexed wallet, address indexed guardian);
                  event GuardianAdded(address indexed wallet, address indexed guardian);
                  event GuardianRevoked(address indexed wallet, address indexed guardian);
                  // *************** Modifiers ************************ //
                  /**
                   * @notice Throws if there is no ongoing recovery procedure.
                   */
                  modifier onlyWhenRecovery(address _wallet) {
                      require(recoveryConfigs[_wallet].executeAfter > 0, "SM: no ongoing recovery");
                      _;
                  }
                  /**
                   * @notice Throws if there is an ongoing recovery procedure.
                   */
                  modifier notWhenRecovery(address _wallet) {
                      require(recoveryConfigs[_wallet].executeAfter == 0, "SM: ongoing recovery");
                      _;
                  }
                  /**
                   * @notice Throws if the caller is not a guardian for the wallet or the module itself.
                   */
                  modifier onlyGuardianOrSelf(address _wallet) {
                      require(_isSelf(msg.sender) || isGuardian(_wallet, msg.sender), "SM: must be guardian/self");
                      _;
                  }
                  // *************** Constructor ************************ //
                  constructor(
                      uint256 _recoveryPeriod,
                      uint256 _securityPeriod,
                      uint256 _securityWindow,
                      uint256 _lockPeriod
                  ) {
                      // For the wallet to be secure we must have recoveryPeriod >= securityPeriod + securityWindow
                      // where securityPeriod and securityWindow are the security parameters of adding/removing guardians.
                      require(_lockPeriod >= _recoveryPeriod, "SM: insecure lock period");
                      require(_recoveryPeriod >= _securityPeriod + _securityWindow, "SM: insecure security periods");
                      recoveryPeriod = _recoveryPeriod;
                      lockPeriod = _lockPeriod;
                      securityWindow = _securityWindow;
                      securityPeriod = _securityPeriod;
                  }
                  // *************** External functions ************************ //
                  // *************** Recovery functions ************************ //
                  /**
                   * @notice Lets the guardians start the execution of the recovery procedure.
                   * Once triggered the recovery is pending for the security period before it can be finalised.
                   * Must be confirmed by N guardians, where N = ceil(Nb Guardians / 2).
                   * @param _wallet The target wallet.
                   * @param _recovery The address to which ownership should be transferred.
                   */
                  function executeRecovery(address _wallet, address _recovery) external onlySelf() notWhenRecovery(_wallet) {
                      validateNewOwner(_wallet, _recovery);
                      uint64 executeAfter = uint64(block.timestamp + recoveryPeriod);
                      recoveryConfigs[_wallet] = RecoveryConfig(_recovery, executeAfter, uint32(guardianStorage.guardianCount(_wallet)));
                      _setLock(_wallet, block.timestamp + lockPeriod, SecurityManager.executeRecovery.selector);
                      emit RecoveryExecuted(_wallet, _recovery, executeAfter);
                  }
                  /**
                   * @notice Finalizes an ongoing recovery procedure if the security period is over.
                   * The method is public and callable by anyone to enable orchestration.
                   * @param _wallet The target wallet.
                   */
                  function finalizeRecovery(address _wallet) external onlyWhenRecovery(_wallet) {
                      RecoveryConfig storage config = recoveryConfigs[_wallet];
                      require(uint64(block.timestamp) > config.executeAfter, "SM: ongoing recovery period");
                      address recoveryOwner = config.recovery;
                      delete recoveryConfigs[_wallet];
                      _clearSession(_wallet);
                      IWallet(_wallet).setOwner(recoveryOwner);
                      _setLock(_wallet, 0, bytes4(0));
                      emit RecoveryFinalized(_wallet, recoveryOwner);
                  }
                  /**
                   * @notice Lets the owner cancel an ongoing recovery procedure.
                   * Must be confirmed by N guardians, where N = ceil(Nb Guardian at executeRecovery + 1) / 2) - 1.
                   * @param _wallet The target wallet.
                   */
                  function cancelRecovery(address _wallet) external onlySelf() onlyWhenRecovery(_wallet) {
                      address recoveryOwner = recoveryConfigs[_wallet].recovery;
                      delete recoveryConfigs[_wallet];
                      _setLock(_wallet, 0, bytes4(0));
                      emit RecoveryCanceled(_wallet, recoveryOwner);
                  }
                  /**
                   * @notice Lets the owner transfer the wallet ownership. This is executed immediately.
                   * @param _wallet The target wallet.
                   * @param _newOwner The address to which ownership should be transferred.
                   */
                  function transferOwnership(address _wallet, address _newOwner) external onlySelf() onlyWhenUnlocked(_wallet) {
                      validateNewOwner(_wallet, _newOwner);
                      IWallet(_wallet).setOwner(_newOwner);
                      emit OwnershipTransfered(_wallet, _newOwner);
                  }
                  /**
                  * @notice Gets the details of the ongoing recovery procedure if any.
                  * @param _wallet The target wallet.
                  */
                  function getRecovery(address _wallet) external view returns(address _address, uint64 _executeAfter, uint32 _guardianCount) {
                      RecoveryConfig storage config = recoveryConfigs[_wallet];
                      return (config.recovery, config.executeAfter, config.guardianCount);
                  }
                  // *************** Lock functions ************************ //
                  /**
                   * @notice Lets a guardian lock a wallet.
                   * @param _wallet The target wallet.
                   */
                  function lock(address _wallet) external onlyGuardianOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
                      _setLock(_wallet, block.timestamp + lockPeriod, SecurityManager.lock.selector);
                      emit Locked(_wallet, uint64(block.timestamp + lockPeriod));
                  }
                  /**
                   * @notice Lets a guardian unlock a locked wallet.
                   * @param _wallet The target wallet.
                   */
                  function unlock(address _wallet) external onlyGuardianOrSelf(_wallet) onlyWhenLocked(_wallet) {
                      require(locks[_wallet].locker == SecurityManager.lock.selector, "SM: cannot unlock");
                      _setLock(_wallet, 0, bytes4(0));
                      emit Unlocked(_wallet);
                  }
                  /**
                   * @notice Returns the release time of a wallet lock or 0 if the wallet is unlocked.
                   * @param _wallet The target wallet.
                   * @return _releaseAfter The epoch time at which the lock will release (in seconds).
                   */
                  function getLock(address _wallet) external view returns(uint64 _releaseAfter) {
                      return _isLocked(_wallet) ? locks[_wallet].release : 0;
                  }
                  /**
                   * @notice Checks if a wallet is locked.
                   * @param _wallet The target wallet.
                   * @return _isLocked `true` if the wallet is locked otherwise `false`.
                   */
                  function isLocked(address _wallet) external view returns (bool) {
                      return _isLocked(_wallet);
                  }
                  // *************** Guardian functions ************************ //
                  /**
                   * @notice Lets the owner add a guardian to its wallet.
                   * The first guardian is added immediately. All following additions must be confirmed
                   * by calling the confirmGuardianAddition() method.
                   * @param _wallet The target wallet.
                   * @param _guardian The guardian to add.
                   */
                  function addGuardian(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
                      require(!_isOwner(_wallet, _guardian), "SM: guardian cannot be owner");
                      require(!isGuardian(_wallet, _guardian), "SM: duplicate guardian");
                      // Guardians must either be an EOA or a contract with an owner()
                      // method that returns an address with a 25000 gas stipend.
                      // Note that this test is not meant to be strict and can be bypassed by custom malicious contracts.
                      (bool success,) = _guardian.call{gas: 25000}(abi.encodeWithSignature("owner()"));
                      require(success, "SM: must be EOA/Argent wallet");
                      bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "addition"));
                      GuardianManagerConfig storage config = guardianConfigs[_wallet];
                      require(
                          config.pending[id] == 0 || block.timestamp > config.pending[id] + securityWindow,
                          "SM: duplicate pending addition");
                      config.pending[id] = block.timestamp + securityPeriod;
                      emit GuardianAdditionRequested(_wallet, _guardian, block.timestamp + securityPeriod);
                  }
                  /**
                   * @notice Confirms the pending addition of a guardian to a wallet.
                   * The method must be called during the confirmation window and can be called by anyone to enable orchestration.
                   * @param _wallet The target wallet.
                   * @param _guardian The guardian.
                   */
                  function confirmGuardianAddition(address _wallet, address _guardian) external onlyWhenUnlocked(_wallet) {
                      bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "addition"));
                      GuardianManagerConfig storage config = guardianConfigs[_wallet];
                      require(config.pending[id] > 0, "SM: unknown pending addition");
                      require(config.pending[id] < block.timestamp, "SM: pending addition not over");
                      require(block.timestamp < config.pending[id] + securityWindow, "SM: pending addition expired");
                      guardianStorage.addGuardian(_wallet, _guardian);
                      emit GuardianAdded(_wallet, _guardian);
                      delete config.pending[id];
                  }
                  /**
                   * @notice Lets the owner cancel a pending guardian addition.
                   * @param _wallet The target wallet.
                   * @param _guardian The guardian.
                   */
                  function cancelGuardianAddition(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
                      bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "addition"));
                      GuardianManagerConfig storage config = guardianConfigs[_wallet];
                      require(config.pending[id] > 0, "SM: unknown pending addition");
                      delete config.pending[id];
                      emit GuardianAdditionCancelled(_wallet, _guardian);
                  }
                  /**
                   * @notice Lets the owner revoke a guardian from its wallet.
                   * @dev Revokation must be confirmed by calling the confirmGuardianRevokation() method.
                   * @param _wallet The target wallet.
                   * @param _guardian The guardian to revoke.
                   */
                  function revokeGuardian(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) {
                      require(isGuardian(_wallet, _guardian), "SM: must be existing guardian");
                      bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "revokation"));
                      GuardianManagerConfig storage config = guardianConfigs[_wallet];
                      require(
                          config.pending[id] == 0 || block.timestamp > config.pending[id] + securityWindow,
                          "SM: duplicate pending revoke"); // TODO need to allow if confirmation window passed
                      config.pending[id] = block.timestamp + securityPeriod;
                      emit GuardianRevokationRequested(_wallet, _guardian, block.timestamp + securityPeriod);
                  }
                  /**
                   * @notice Confirms the pending revokation of a guardian to a wallet.
                   * The method must be called during the confirmation window and can be called by anyone to enable orchestration.
                   * @param _wallet The target wallet.
                   * @param _guardian The guardian.
                   */
                  function confirmGuardianRevokation(address _wallet, address _guardian) external {
                      bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "revokation"));
                      GuardianManagerConfig storage config = guardianConfigs[_wallet];
                      require(config.pending[id] > 0, "SM: unknown pending revoke");
                      require(config.pending[id] < block.timestamp, "SM: pending revoke not over");
                      require(block.timestamp < config.pending[id] + securityWindow, "SM: pending revoke expired");
                      guardianStorage.revokeGuardian(_wallet, _guardian);
                      emit GuardianRevoked(_wallet, _guardian);
                      delete config.pending[id];
                  }
                  /**
                   * @notice Lets the owner cancel a pending guardian revokation.
                   * @param _wallet The target wallet.
                   * @param _guardian The guardian.
                   */
                  function cancelGuardianRevokation(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
                      bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "revokation"));
                      GuardianManagerConfig storage config = guardianConfigs[_wallet];
                      require(config.pending[id] > 0, "SM: unknown pending revoke");
                      delete config.pending[id];
                      emit GuardianRevokationCancelled(_wallet, _guardian);
                  }
                  /**
                   * @notice Checks if an address is a guardian for a wallet.
                   * @param _wallet The target wallet.
                   * @param _guardian The address to check.
                   * @return _isGuardian `true` if the address is a guardian for the wallet otherwise `false`.
                   */
                  function isGuardian(address _wallet, address _guardian) public view returns (bool _isGuardian) {
                      return guardianStorage.isGuardian(_wallet, _guardian);
                  }
                  /**
                  * @notice Checks if an address is a guardian or an account authorised to sign on behalf of a smart-contract guardian.
                  * @param _wallet The target wallet.
                  * @param _guardian the address to test
                  * @return _isGuardian `true` if the address is a guardian for the wallet otherwise `false`.
                  */
                  function isGuardianOrGuardianSigner(address _wallet, address _guardian) external view returns (bool _isGuardian) {
                      (_isGuardian, ) = Utils.isGuardianOrGuardianSigner(guardianStorage.getGuardians(_wallet), _guardian);
                  }
                  /**
                   * @notice Counts the number of active guardians for a wallet.
                   * @param _wallet The target wallet.
                   * @return _count The number of active guardians for a wallet.
                   */
                  function guardianCount(address _wallet) external view returns (uint256 _count) {
                      return guardianStorage.guardianCount(_wallet);
                  }
                  /**
                   * @notice Get the active guardians for a wallet.
                   * @param _wallet The target wallet.
                   * @return _guardians the active guardians for a wallet.
                   */
                  function getGuardians(address _wallet) external view returns (address[] memory _guardians) {
                      return guardianStorage.getGuardians(_wallet);
                  }
                  // *************** Internal Functions ********************* //
                  function validateNewOwner(address _wallet, address _newOwner) internal view {
                      require(_newOwner != address(0), "SM: new owner cannot be null");
                      require(!isGuardian(_wallet, _newOwner), "SM: new owner cannot be guardian");
                  }
                  function _setLock(address _wallet, uint256 _releaseAfter, bytes4 _locker) internal {
                      locks[_wallet] = Lock(SafeCast.toUint64(_releaseAfter), _locker);
                  }
              }// 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.8.3;
              import "@openzeppelin/contracts/utils/math/SafeCast.sol";
              import "./common/Utils.sol";
              import "./common/BaseModule.sol";
              import "../../lib_0.5/other/ERC20.sol";
              /**
               * @title TransactionManager
               * @notice Module to execute transactions in sequence to e.g. transfer tokens (ETH, ERC20, ERC721, ERC1155) or call third-party contracts.
               * @author Julien Niset - <[email protected]>
               */
              abstract contract TransactionManager is BaseModule {
                  // Static calls
                  bytes4 private constant ERC1271_IS_VALID_SIGNATURE = bytes4(keccak256("isValidSignature(bytes32,bytes)"));
                  bytes4 private constant ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
                  bytes4 private constant ERC1155_RECEIVED = bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"));
                  bytes4 private constant ERC1155_BATCH_RECEIVED = bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"));
                  bytes4 private constant ERC165_INTERFACE = bytes4(keccak256("supportsInterface(bytes4)"));
                  struct Call {
                      address to;
                      uint256 value;
                      bytes data;
                  }
                  // The time delay for adding a trusted contact
                  uint256 internal immutable whitelistPeriod;
                  // *************** Events *************************** //
                  event AddedToWhitelist(address indexed wallet, address indexed target, uint64 whitelistAfter);
                  event RemovedFromWhitelist(address indexed wallet, address indexed target);
                  event SessionCreated(address indexed wallet, address sessionKey, uint64 expires);
                  event SessionCleared(address indexed wallet, address sessionKey);
                  // *************** Constructor ************************ //
                  constructor(uint256 _whitelistPeriod) {
                      whitelistPeriod = _whitelistPeriod;
                  }
                  // *************** External functions ************************ //
                  /**
                   * @notice Makes the target wallet execute a sequence of transactions authorised by the wallet owner.
                   * The method reverts if any of the inner transactions reverts.
                   * The method reverts if any of the inner transaction is not to a trusted contact or an authorised dapp.
                   * @param _wallet The target wallet.
                   * @param _transactions The sequence of transactions.
                   */
                  function multiCall(
                      address _wallet,
                      Call[] calldata _transactions
                  )
                      external
                      onlySelf()
                      onlyWhenUnlocked(_wallet)
                      returns (bytes[] memory)
                  {
                      bytes[] memory results = new bytes[](_transactions.length);
                      for(uint i = 0; i < _transactions.length; i++) {
                          address spender = Utils.recoverSpender(_transactions[i].to, _transactions[i].data);
                          require(
                              (_transactions[i].value == 0 || spender == _transactions[i].to) &&
                              (isWhitelisted(_wallet, spender) || authoriser.isAuthorised(_wallet, spender, _transactions[i].to, _transactions[i].data)),
                              "TM: call not authorised");
                          results[i] = invokeWallet(_wallet, _transactions[i].to, _transactions[i].value, _transactions[i].data);
                      }
                      return results;
                  }
                  /**
                   * @notice Makes the target wallet execute a sequence of transactions authorised by a session key.
                   * The method reverts if any of the inner transactions reverts.
                   * @param _wallet The target wallet.
                   * @param _transactions The sequence of transactions.
                   */
                  function multiCallWithSession(
                      address _wallet,
                      Call[] calldata _transactions
                  )
                      external
                      onlySelf()
                      onlyWhenUnlocked(_wallet)
                      returns (bytes[] memory)
                  {
                      return multiCallWithApproval(_wallet, _transactions);
                  }
                  /**
                   * @notice Makes the target wallet execute a sequence of transactions approved by a majority of guardians.
                   * The method reverts if any of the inner transactions reverts.
                   * @param _wallet The target wallet.
                   * @param _transactions The sequence of transactions.
                   */
                  function multiCallWithGuardians(
                      address _wallet,
                      Call[] calldata _transactions
                  )
                      external 
                      onlySelf()
                      onlyWhenUnlocked(_wallet)
                      returns (bytes[] memory)
                  {
                      return multiCallWithApproval(_wallet, _transactions);
                  }
                  /**
                   * @notice Makes the target wallet execute a sequence of transactions approved by a majority of guardians.
                   * The method reverts if any of the inner transactions reverts.
                   * Upon success a new session is started.
                   * @param _wallet The target wallet.
                   * @param _transactions The sequence of transactions.
                   */
                  function multiCallWithGuardiansAndStartSession(
                      address _wallet,
                      Call[] calldata _transactions,
                      address _sessionUser,
                      uint64 _duration
                  )
                      external 
                      onlySelf()
                      onlyWhenUnlocked(_wallet)
                      returns (bytes[] memory)
                  {
                      startSession(_wallet, _sessionUser, _duration);
                      return multiCallWithApproval(_wallet, _transactions);
                  }
                  /**
                  * @notice Clears the active session of a wallet if any.
                  * @param _wallet The target wallet.
                  */
                  function clearSession(address _wallet) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
                      emit SessionCleared(_wallet, sessions[_wallet].key);
                      _clearSession(_wallet);
                  }
                  /**
                   * @notice Adds an address to the list of trusted contacts.
                   * @param _wallet The target wallet.
                   * @param _target The address to add.
                   */
                  function addToWhitelist(address _wallet, address _target) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
                      require(_target != _wallet, "TM: Cannot whitelist wallet");
                      require(!registry.isRegisteredModule(_target), "TM: Cannot whitelist module");
                      require(!isWhitelisted(_wallet, _target), "TM: target already whitelisted");
                      uint256 whitelistAfter = block.timestamp + whitelistPeriod;
                      setWhitelist(_wallet, _target, whitelistAfter);
                      emit AddedToWhitelist(_wallet, _target, uint64(whitelistAfter));
                  }
                  /**
                   * @notice Removes an address from the list of trusted contacts.
                   * @param _wallet The target wallet.
                   * @param _target The address to remove.
                   */
                  function removeFromWhitelist(address _wallet, address _target) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
                      setWhitelist(_wallet, _target, 0);
                      emit RemovedFromWhitelist(_wallet, _target);
                  }
                  /**
                  * @notice Checks if an address is a trusted contact for a wallet.
                  * @param _wallet The target wallet.
                  * @param _target The address.
                  * @return _isWhitelisted true if the address is a trusted contact.
                  */
                  function isWhitelisted(address _wallet, address _target) public view returns (bool _isWhitelisted) {
                      uint whitelistAfter = userWhitelist.getWhitelist(_wallet, _target);
                      return whitelistAfter > 0 && whitelistAfter < block.timestamp;
                  }
                  
                  /*
                  * @notice Enable the static calls required to make the wallet compatible with the ERC1155TokenReceiver 
                  * interface (see https://eips.ethereum.org/EIPS/eip-1155#erc-1155-token-receiver). This method only 
                  * needs to be called for wallets deployed in version lower or equal to 2.4.0 as the ERC1155 static calls
                  * are not available by default for these versions of BaseWallet
                  * @param _wallet The target wallet.
                  */
                  function enableERC1155TokenReceiver(address _wallet) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
                      IWallet(_wallet).enableStaticCall(address(this), ERC165_INTERFACE);
                      IWallet(_wallet).enableStaticCall(address(this), ERC1155_RECEIVED);
                      IWallet(_wallet).enableStaticCall(address(this), ERC1155_BATCH_RECEIVED);
                  }
                  /**
                   * @inheritdoc IModule
                   */
                  function supportsStaticCall(bytes4 _methodId) external pure override returns (bool _isSupported) {
                      return _methodId == ERC1271_IS_VALID_SIGNATURE ||
                             _methodId == ERC721_RECEIVED ||
                             _methodId == ERC165_INTERFACE ||
                             _methodId == ERC1155_RECEIVED ||
                             _methodId == ERC1155_BATCH_RECEIVED;
                  }
                  /** ******************* Callbacks ************************** */
                  /**
                   * @notice Returns true if this contract implements the interface defined by
                   * `interfaceId` (see https://eips.ethereum.org/EIPS/eip-165).
                   */
                  function supportsInterface(bytes4 _interfaceID) external pure returns (bool) {
                      return  _interfaceID == ERC165_INTERFACE || _interfaceID == (ERC1155_RECEIVED ^ ERC1155_BATCH_RECEIVED);          
                  }
                  /**
                  * @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) external 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_IS_VALID_SIGNATURE;
                  }
                  fallback() external {
                      bytes4 methodId = Utils.functionPrefix(msg.data);
                      if(methodId == ERC721_RECEIVED || methodId == ERC1155_RECEIVED || methodId == ERC1155_BATCH_RECEIVED) {
                          // solhint-disable-next-line no-inline-assembly
                          assembly {                
                              calldatacopy(0, 0, 0x04)
                              return (0, 0x20)
                          }
                      }
                  }
                  // *************** Internal Functions ********************* //
                  function enableDefaultStaticCalls(address _wallet) internal {
                      // setup the static calls that are available for free for all wallets
                      IWallet(_wallet).enableStaticCall(address(this), ERC1271_IS_VALID_SIGNATURE);
                      IWallet(_wallet).enableStaticCall(address(this), ERC721_RECEIVED);
                  }
                  function multiCallWithApproval(address _wallet, Call[] calldata _transactions) internal returns (bytes[] memory) {
                      bytes[] memory results = new bytes[](_transactions.length);
                      for(uint i = 0; i < _transactions.length; i++) {
                          results[i] = invokeWallet(_wallet, _transactions[i].to, _transactions[i].value, _transactions[i].data);
                      }
                      return results;
                  }
                  function startSession(address _wallet, address _sessionUser, uint64 _duration) internal {
                      require(_sessionUser != address(0), "TM: Invalid session user");
                      require(_duration > 0, "TM: Invalid session duration");
                      uint64 expiry = SafeCast.toUint64(block.timestamp + _duration);
                      sessions[_wallet] = Session(_sessionUser, expiry);
                      emit SessionCreated(_wallet, _sessionUser, expiry);
                  }
                  function setWhitelist(address _wallet, address _target, uint256 _whitelistAfter) internal {
                      userWhitelist.setWhitelist(_wallet, _target, _whitelistAfter);
                  }
              }// 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.8.3;
              import "../../wallet/IWallet.sol";
              import "../../infrastructure/IModuleRegistry.sol";
              import "../../infrastructure/storage/IGuardianStorage.sol";
              import "../../infrastructure/IAuthoriser.sol";
              import "../../infrastructure/storage/ITransferStorage.sol";
              import "./IModule.sol";
              import "../../../lib_0.5/other/ERC20.sol";
              /**
               * @title BaseModule
               * @notice Base Module contract that contains methods common to all Modules.
               * @author Julien Niset - <[email protected]>, Olivier VDB - <[email protected]>
               */
              abstract contract BaseModule is IModule {
                  // Empty calldata
                  bytes constant internal EMPTY_BYTES = "";
                  // Mock token address for ETH
                  address constant internal ETH_TOKEN = address(0);
                  // The module registry
                  IModuleRegistry internal immutable registry;
                  // The guardians storage
                  IGuardianStorage internal immutable guardianStorage;
                  // The trusted contacts storage
                  ITransferStorage internal immutable userWhitelist;
                  // The authoriser
                  IAuthoriser internal immutable authoriser;
                  event ModuleCreated(bytes32 name);
                  enum OwnerSignature {
                      Anyone,             // Anyone
                      Required,           // Owner required
                      Optional,           // Owner and/or guardians
                      Disallowed,         // Guardians only
                      Session             // Session only
                  }
                  struct Session {
                      address key;
                      uint64 expires;
                  }
                  // Maps wallet to session
                  mapping (address => Session) internal sessions;
                  struct Lock {
                      // the lock's release timestamp
                      uint64 release;
                      // the signature of the method that set the last lock
                      bytes4 locker;
                  }
                  
                  // Wallet specific lock storage
                  mapping (address => Lock) internal locks;
                  /**
                   * @notice Throws if the wallet is not locked.
                   */
                  modifier onlyWhenLocked(address _wallet) {
                      require(_isLocked(_wallet), "BM: wallet must be locked");
                      _;
                  }
                  /**
                   * @notice Throws if the wallet is locked.
                   */
                  modifier onlyWhenUnlocked(address _wallet) {
                      require(!_isLocked(_wallet), "BM: wallet locked");
                      _;
                  }
                  /**
                   * @notice Throws if the sender is not the module itself.
                   */
                  modifier onlySelf() {
                      require(_isSelf(msg.sender), "BM: must be module");
                      _;
                  }
                  /**
                   * @notice Throws if the sender is not the module itself or the owner of the target wallet.
                   */
                  modifier onlyWalletOwnerOrSelf(address _wallet) {
                      require(_isSelf(msg.sender) || _isOwner(_wallet, msg.sender), "BM: must be wallet owner/self");
                      _;
                  }
                  /**
                   * @dev Throws if the sender is not the target wallet of the call.
                   */
                  modifier onlyWallet(address _wallet) {
                      require(msg.sender == _wallet, "BM: caller must be wallet");
                      _;
                  }
                  constructor(
                      IModuleRegistry _registry,
                      IGuardianStorage _guardianStorage,
                      ITransferStorage _userWhitelist,
                      IAuthoriser _authoriser,
                      bytes32 _name
                  ) {
                      registry = _registry;
                      guardianStorage = _guardianStorage;
                      userWhitelist = _userWhitelist;
                      authoriser = _authoriser;
                      emit ModuleCreated(_name);
                  }
                  /**
                   * @notice Moves tokens that have been sent to the module by mistake.
                   * @param _token The target token.
                   */
                  function recoverToken(address _token) external {
                      uint total = ERC20(_token).balanceOf(address(this));
                      ERC20(_token).transfer(address(registry), total);
                  }
                  function _clearSession(address _wallet) internal {
                      delete sessions[_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 Helper method to check if a wallet is locked.
                   * @param _wallet The target wallet.
                   */
                  function _isLocked(address _wallet) internal view returns (bool) {
                      return locks[_wallet].release > uint64(block.timestamp);
                  }
                  /**
                   * @notice Helper method to check if an address is the module itself.
                   * @param _addr The target address.
                   */
                  function _isSelf(address _addr) internal view returns (bool) {
                      return _addr == address(this);
                  }
                  /**
                   * @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) {
                      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("BM: wallet invoke reverted");
                      }
                  }
              }// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>
              // This program is free software: you can redistribute it and/or modify
              // it under the terms of the GNU General Public License as published by
              // the Free Software Foundation, either version 3 of the License, or
              // (at your option) any later version.
              // This program is distributed in the hope that it will be useful,
              // but WITHOUT ANY WARRANTY; without even the implied warranty of
              // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
              // GNU General Public License for more details.
              // You should have received a copy of the GNU General Public License
              // along with this program.  If not, see <http://www.gnu.org/licenses/>.
              // SPDX-License-Identifier: GPL-3.0-only
              pragma solidity ^0.8.3;
              /**
               * @title IModule
               * @notice Interface for a Module.
               * @author Julien Niset - <[email protected]>, Olivier VDB - <[email protected]>
               */
              interface IModule {
                  /**\t
                   * @notice Adds a module to a wallet. Cannot execute when wallet is locked (or under recovery)\t
                   * @param _wallet The target wallet.\t
                   * @param _module The modules to authorise.\t
                   */\t
                  function addModule(address _wallet, address _module) external;
                  /**
                   * @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 Returns whether the module implements a callback for a given static call method.
                   * @param _methodId The method id.
                   */
                  function supportsStaticCall(bytes4 _methodId) external view returns (bool _isSupported);
              }// Copyright (C) 2021  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.8.3;
              import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol";
              import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
              contract SimpleOracle {
                  address internal immutable weth;
                  address internal immutable uniswapV2Factory;
                  constructor(address _uniswapRouter) {
                      weth = IUniswapV2Router01(_uniswapRouter).WETH();
                      uniswapV2Factory = IUniswapV2Router01(_uniswapRouter).factory();
                  }
                  function inToken(address _token, uint256 _ethAmount) internal view returns (uint256) {
                      (uint256 wethReserve, uint256 tokenReserve) = getReservesForTokenPool(_token);
                      return _ethAmount * tokenReserve / wethReserve;
                  }
                  function getReservesForTokenPool(address _token) internal view returns (uint256 wethReserve, uint256 tokenReserve) {
                      if (weth < _token) {
                          address pair = getPairForSorted(weth, _token);
                          (wethReserve, tokenReserve,) = IUniswapV2Pair(pair).getReserves();
                      } else {
                          address pair = getPairForSorted(_token, weth);
                          (tokenReserve, wethReserve,) = IUniswapV2Pair(pair).getReserves();
                      }
                      require(wethReserve != 0 && tokenReserve != 0, "SO: no liquidity");
                  }
                  function getPairForSorted(address tokenA, address tokenB) internal virtual view returns (address pair) {    
                      pair = address(uint160(uint256(keccak256(abi.encodePacked(
                              hex'ff',
                              uniswapV2Factory,
                              keccak256(abi.encodePacked(tokenA, tokenB)),
                              hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f'
                          )))));
                  }
              }// 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.8.3;
              /**
               * @title Utils
               * @notice Common utility methods used by modules.
               */
              library Utils {
                  // ERC20, ERC721 & ERC1155 transfers & approvals
                  bytes4 private constant ERC20_TRANSFER = bytes4(keccak256("transfer(address,uint256)"));
                  bytes4 private constant ERC20_APPROVE = bytes4(keccak256("approve(address,uint256)"));
                  bytes4 private constant ERC721_SET_APPROVAL_FOR_ALL = bytes4(keccak256("setApprovalForAll(address,bool)"));
                  bytes4 private constant ERC721_TRANSFER_FROM = bytes4(keccak256("transferFrom(address,address,uint256)"));
                  bytes4 private constant ERC721_SAFE_TRANSFER_FROM = bytes4(keccak256("safeTransferFrom(address,address,uint256)"));
                  bytes4 private constant ERC721_SAFE_TRANSFER_FROM_BYTES = bytes4(keccak256("safeTransferFrom(address,address,uint256,bytes)"));
                  bytes4 private constant ERC1155_SAFE_TRANSFER_FROM = bytes4(keccak256("safeTransferFrom(address,address,uint256,uint256,bytes)"));
                  bytes4 private constant OWNER_SIG = 0x8da5cb5b;
                  /**
                  * @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, "Utils: bad v value in signature");
                      address recoveredAddress = ecrecover(_signedHash, v, r, s);
                      require(recoveredAddress != address(0), "Utils: ecrecover returned 0");
                      return recoveredAddress;
                  }
                  /**
                  * @notice Helper method to recover the spender from a contract call. 
                  * The method returns the contract unless the call is to a standard method of a ERC20/ERC721/ERC1155 token
                  * in which case the spender is recovered from the data.
                  * @param _to The target contract.
                  * @param _data The data payload.
                  */
                  function recoverSpender(address _to, bytes memory _data) internal pure returns (address spender) {
                      if(_data.length >= 68) {
                          bytes4 methodId;
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              methodId := mload(add(_data, 0x20))
                          }
                          if(
                              methodId == ERC20_TRANSFER ||
                              methodId == ERC20_APPROVE ||
                              methodId == ERC721_SET_APPROVAL_FOR_ALL) 
                          {
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  spender := mload(add(_data, 0x24))
                              }
                              return spender;
                          }
                          if(
                              methodId == ERC721_TRANSFER_FROM ||
                              methodId == ERC721_SAFE_TRANSFER_FROM ||
                              methodId == ERC721_SAFE_TRANSFER_FROM_BYTES ||
                              methodId == ERC1155_SAFE_TRANSFER_FROM)
                          {
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  spender := mload(add(_data, 0x44))
                              }
                              return spender;
                          }
                      }
                      spender = _to;
                  }
                  /**
                  * @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, "Utils: Invalid functionPrefix");
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          prefix := mload(add(_data, 0x20))
                      }
                  }
                  /**
                  * @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 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 the owner of a guardian contract.
                  * The method does not revert if the call to the owner() method consumes more then 25000 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);
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let ptr := mload(0x40)
                          mstore(ptr,OWNER_SIG)
                          let result := staticcall(25000, _guardian, ptr, 0x20, ptr, 0x20)
                          if eq(result, 1) {
                              owner := mload(ptr)
                          }
                      }
                      return owner == _owner;
                  }
                  /**
                  * @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;
                      }
                  }
              }
              // 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.9.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.9.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);
              }// SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library Math {
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a >= b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow, so we distribute
                      return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
               * checks.
               *
               * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
               * easily result in undesired exploitation or bugs, since developers usually
               * assume that overflows raise errors. `SafeCast` restores this intuition by
               * reverting the transaction when such 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.
               *
               * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
               * all math on `uint256` and `int256` and then downcasting.
               */
              library SafeCast {
                  /**
                   * @dev Returns the downcasted uint128 from uint256, reverting on
                   * overflow (when the input is greater than largest uint128).
                   *
                   * Counterpart to Solidity's `uint128` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 128 bits
                   */
                  function toUint128(uint256 value) internal pure returns (uint128) {
                      require(value < 2**128, "SafeCast: value doesn\\'t fit in 128 bits");
                      return uint128(value);
                  }
                  /**
                   * @dev Returns the downcasted uint64 from uint256, reverting on
                   * overflow (when the input is greater than largest uint64).
                   *
                   * Counterpart to Solidity's `uint64` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 64 bits
                   */
                  function toUint64(uint256 value) internal pure returns (uint64) {
                      require(value < 2**64, "SafeCast: value doesn\\'t fit in 64 bits");
                      return uint64(value);
                  }
                  /**
                   * @dev Returns the downcasted uint32 from uint256, reverting on
                   * overflow (when the input is greater than largest uint32).
                   *
                   * Counterpart to Solidity's `uint32` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 32 bits
                   */
                  function toUint32(uint256 value) internal pure returns (uint32) {
                      require(value < 2**32, "SafeCast: value doesn\\'t fit in 32 bits");
                      return uint32(value);
                  }
                  /**
                   * @dev Returns the downcasted uint16 from uint256, reverting on
                   * overflow (when the input is greater than largest uint16).
                   *
                   * Counterpart to Solidity's `uint16` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 16 bits
                   */
                  function toUint16(uint256 value) internal pure returns (uint16) {
                      require(value < 2**16, "SafeCast: value doesn\\'t fit in 16 bits");
                      return uint16(value);
                  }
                  /**
                   * @dev Returns the downcasted uint8 from uint256, reverting on
                   * overflow (when the input is greater than largest uint8).
                   *
                   * Counterpart to Solidity's `uint8` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 8 bits.
                   */
                  function toUint8(uint256 value) internal pure returns (uint8) {
                      require(value < 2**8, "SafeCast: value doesn\\'t fit in 8 bits");
                      return uint8(value);
                  }
                  /**
                   * @dev Converts a signed int256 into an unsigned uint256.
                   *
                   * Requirements:
                   *
                   * - input must be greater than or equal to 0.
                   */
                  function toUint256(int256 value) internal pure returns (uint256) {
                      require(value >= 0, "SafeCast: value must be positive");
                      return uint256(value);
                  }
                  /**
                   * @dev Returns the downcasted int128 from int256, reverting on
                   * overflow (when the input is less than smallest int128 or
                   * greater than largest int128).
                   *
                   * Counterpart to Solidity's `int128` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 128 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt128(int256 value) internal pure returns (int128) {
                      require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\\'t fit in 128 bits");
                      return int128(value);
                  }
                  /**
                   * @dev Returns the downcasted int64 from int256, reverting on
                   * overflow (when the input is less than smallest int64 or
                   * greater than largest int64).
                   *
                   * Counterpart to Solidity's `int64` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 64 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt64(int256 value) internal pure returns (int64) {
                      require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\\'t fit in 64 bits");
                      return int64(value);
                  }
                  /**
                   * @dev Returns the downcasted int32 from int256, reverting on
                   * overflow (when the input is less than smallest int32 or
                   * greater than largest int32).
                   *
                   * Counterpart to Solidity's `int32` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 32 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt32(int256 value) internal pure returns (int32) {
                      require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\\'t fit in 32 bits");
                      return int32(value);
                  }
                  /**
                   * @dev Returns the downcasted int16 from int256, reverting on
                   * overflow (when the input is less than smallest int16 or
                   * greater than largest int16).
                   *
                   * Counterpart to Solidity's `int16` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 16 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt16(int256 value) internal pure returns (int16) {
                      require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\\'t fit in 16 bits");
                      return int16(value);
                  }
                  /**
                   * @dev Returns the downcasted int8 from int256, reverting on
                   * overflow (when the input is less than smallest int8 or
                   * greater than largest int8).
                   *
                   * Counterpart to Solidity's `int8` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 8 bits.
                   *
                   * _Available since v3.1._
                   */
                  function toInt8(int256 value) internal pure returns (int8) {
                      require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\\'t fit in 8 bits");
                      return int8(value);
                  }
                  /**
                   * @dev Converts an unsigned uint256 into a signed int256.
                   *
                   * Requirements:
                   *
                   * - input must be less than or equal to maxInt256.
                   */
                  function toInt256(uint256 value) internal pure returns (int256) {
                      require(value < 2**255, "SafeCast: value doesn't fit in an int256");
                      return int256(value);
                  }
              }
              pragma solidity >=0.5.0;
              interface IUniswapV2Pair {
                  event Approval(address indexed owner, address indexed spender, uint value);
                  event Transfer(address indexed from, address indexed to, uint value);
                  function name() external pure returns (string memory);
                  function symbol() external pure returns (string memory);
                  function decimals() external pure returns (uint8);
                  function totalSupply() external view returns (uint);
                  function balanceOf(address owner) external view returns (uint);
                  function allowance(address owner, address spender) external view returns (uint);
                  function approve(address spender, uint value) external returns (bool);
                  function transfer(address to, uint value) external returns (bool);
                  function transferFrom(address from, address to, uint value) external returns (bool);
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
                  function PERMIT_TYPEHASH() external pure returns (bytes32);
                  function nonces(address owner) external view returns (uint);
                  function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
                  event Mint(address indexed sender, uint amount0, uint amount1);
                  event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                  event Swap(
                      address indexed sender,
                      uint amount0In,
                      uint amount1In,
                      uint amount0Out,
                      uint amount1Out,
                      address indexed to
                  );
                  event Sync(uint112 reserve0, uint112 reserve1);
                  function MINIMUM_LIQUIDITY() external pure returns (uint);
                  function factory() external view returns (address);
                  function token0() external view returns (address);
                  function token1() external view returns (address);
                  function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
                  function price0CumulativeLast() external view returns (uint);
                  function price1CumulativeLast() external view returns (uint);
                  function kLast() external view returns (uint);
                  function mint(address to) external returns (uint liquidity);
                  function burn(address to) external returns (uint amount0, uint amount1);
                  function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
                  function skim(address to) external;
                  function sync() external;
                  function initialize(address, address) external;
              }
              pragma solidity >=0.6.2;
              interface IUniswapV2Router01 {
                  function factory() external pure returns (address);
                  function WETH() external pure returns (address);
                  function addLiquidity(
                      address tokenA,
                      address tokenB,
                      uint amountADesired,
                      uint amountBDesired,
                      uint amountAMin,
                      uint amountBMin,
                      address to,
                      uint deadline
                  ) external returns (uint amountA, uint amountB, uint liquidity);
                  function addLiquidityETH(
                      address token,
                      uint amountTokenDesired,
                      uint amountTokenMin,
                      uint amountETHMin,
                      address to,
                      uint deadline
                  ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
                  function removeLiquidity(
                      address tokenA,
                      address tokenB,
                      uint liquidity,
                      uint amountAMin,
                      uint amountBMin,
                      address to,
                      uint deadline
                  ) external returns (uint amountA, uint amountB);
                  function removeLiquidityETH(
                      address token,
                      uint liquidity,
                      uint amountTokenMin,
                      uint amountETHMin,
                      address to,
                      uint deadline
                  ) external returns (uint amountToken, uint amountETH);
                  function removeLiquidityWithPermit(
                      address tokenA,
                      address tokenB,
                      uint liquidity,
                      uint amountAMin,
                      uint amountBMin,
                      address to,
                      uint deadline,
                      bool approveMax, uint8 v, bytes32 r, bytes32 s
                  ) external returns (uint amountA, uint amountB);
                  function removeLiquidityETHWithPermit(
                      address token,
                      uint liquidity,
                      uint amountTokenMin,
                      uint amountETHMin,
                      address to,
                      uint deadline,
                      bool approveMax, uint8 v, bytes32 r, bytes32 s
                  ) external returns (uint amountToken, uint amountETH);
                  function swapExactTokensForTokens(
                      uint amountIn,
                      uint amountOutMin,
                      address[] calldata path,
                      address to,
                      uint deadline
                  ) external returns (uint[] memory amounts);
                  function swapTokensForExactTokens(
                      uint amountOut,
                      uint amountInMax,
                      address[] calldata path,
                      address to,
                      uint deadline
                  ) external returns (uint[] memory amounts);
                  function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
                      external
                      payable
                      returns (uint[] memory amounts);
                  function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
                      external
                      returns (uint[] memory amounts);
                  function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
                      external
                      returns (uint[] memory amounts);
                  function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
                      external
                      payable
                      returns (uint[] memory amounts);
                  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);
              }
              

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

              File 3 of 5: GraphToken
              // Sources flattened with hardhat v2.0.2 https://hardhat.org
              
              // File @openzeppelin/contracts/GSN/[email protected]
              
              // SPDX-License-Identifier: MIT
              
              pragma solidity ^0.7.0;
              
              /*
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with GSN meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address payable) {
                      return msg.sender;
                  }
              
                  function _msgData() internal view virtual returns (bytes memory) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                  }
              }
              
              
              // File @openzeppelin/contracts/token/ERC20/[email protected]
              
              pragma solidity ^0.7.0;
              
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
              
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
              
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `recipient`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address recipient, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
              
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Moves `amount` tokens from `sender` to `recipient` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
              
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
              
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              
              
              // File @openzeppelin/contracts/math/[email protected]
              
              pragma solidity ^0.7.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) {
                      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 @openzeppelin/contracts/token/ERC20/[email protected]
              
              pragma solidity ^0.7.0;
              
              
              
              /**
               * @dev Implementation of the {IERC20} interface.
               *
               * This implementation is agnostic to the way tokens are created. This means
               * that a supply mechanism has to be added in a derived contract using {_mint}.
               * For a generic mechanism see {ERC20PresetMinterPauser}.
               *
               * TIP: For a detailed writeup see our guide
               * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
               * to implement supply mechanisms].
               *
               * We have followed general OpenZeppelin guidelines: functions revert instead
               * of returning `false` on failure. This behavior is nonetheless conventional
               * and does not conflict with the expectations of ERC20 applications.
               *
               * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
               * This allows applications to reconstruct the allowance for all accounts just
               * by listening to said events. Other implementations of the EIP may not emit
               * these events, as it isn't required by the specification.
               *
               * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
               * functions have been added to mitigate the well-known issues around setting
               * allowances. See {IERC20-approve}.
               */
              contract ERC20 is Context, IERC20 {
                  using SafeMath for uint256;
              
                  mapping (address => uint256) private _balances;
              
                  mapping (address => mapping (address => uint256)) private _allowances;
              
                  uint256 private _totalSupply;
              
                  string private _name;
                  string private _symbol;
                  uint8 private _decimals;
              
                  /**
                   * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
                   * a default value of 18.
                   *
                   * To select a different value for {decimals}, use {_setupDecimals}.
                   *
                   * All three of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor (string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                      _decimals = 18;
                  }
              
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view returns (string memory) {
                      return _name;
                  }
              
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view returns (string memory) {
                      return _symbol;
                  }
              
                  /**
                   * @dev Returns the number of decimals used to get its user representation.
                   * For example, if `decimals` equals `2`, a balance of `505` tokens should
                   * be displayed to a user as `5,05` (`505 / 10 ** 2`).
                   *
                   * Tokens usually opt for a value of 18, imitating the relationship between
                   * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
                   * called.
                   *
                   * NOTE: This information is only used for _display_ purposes: it in
                   * no way affects any of the arithmetic of the contract, including
                   * {IERC20-balanceOf} and {IERC20-transfer}.
                   */
                  function decimals() public view returns (uint8) {
                      return _decimals;
                  }
              
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view override returns (uint256) {
                      return _totalSupply;
                  }
              
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view override returns (uint256) {
                      return _balances[account];
                  }
              
                  /**
                   * @dev See {IERC20-transfer}.
                   *
                   * Requirements:
                   *
                   * - `recipient` cannot be the zero address.
                   * - the caller must have a balance of at least `amount`.
                   */
                  function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                      _transfer(_msgSender(), recipient, amount);
                      return true;
                  }
              
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                  }
              
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 amount) public virtual override returns (bool) {
                      _approve(_msgSender(), spender, amount);
                      return true;
                  }
              
                  /**
                   * @dev See {IERC20-transferFrom}.
                   *
                   * Emits an {Approval} event indicating the updated allowance. This is not
                   * required by the EIP. See the note at the beginning of {ERC20}.
                   *
                   * Requirements:
                   *
                   * - `sender` and `recipient` cannot be the zero address.
                   * - `sender` must have a balance of at least `amount`.
                   * - the caller must have allowance for ``sender``'s tokens of at least
                   * `amount`.
                   */
                  function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
                      _transfer(sender, recipient, amount);
                      _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                      return true;
                  }
              
                  /**
                   * @dev Atomically increases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                      return true;
                  }
              
                  /**
                   * @dev Atomically decreases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `spender` must have allowance for the caller of at least
                   * `subtractedValue`.
                   */
                  function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                      _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                      return true;
                  }
              
                  /**
                   * @dev Moves tokens `amount` from `sender` to `recipient`.
                   *
                   * This is internal function is equivalent to {transfer}, and can be used to
                   * e.g. implement automatic token fees, slashing mechanisms, etc.
                   *
                   * Emits a {Transfer} event.
                   *
                   * Requirements:
                   *
                   * - `sender` cannot be the zero address.
                   * - `recipient` cannot be the zero address.
                   * - `sender` must have a balance of at least `amount`.
                   */
                  function _transfer(address sender, address recipient, uint256 amount) internal virtual {
                      require(sender != address(0), "ERC20: transfer from the zero address");
                      require(recipient != address(0), "ERC20: transfer to the zero address");
              
                      _beforeTokenTransfer(sender, recipient, amount);
              
                      _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                      _balances[recipient] = _balances[recipient].add(amount);
                      emit Transfer(sender, recipient, amount);
                  }
              
                  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                   * the total supply.
                   *
                   * Emits a {Transfer} event with `from` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   */
                  function _mint(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: mint to the zero address");
              
                      _beforeTokenTransfer(address(0), account, amount);
              
                      _totalSupply = _totalSupply.add(amount);
                      _balances[account] = _balances[account].add(amount);
                      emit Transfer(address(0), account, amount);
                  }
              
                  /**
                   * @dev Destroys `amount` tokens from `account`, reducing the
                   * total supply.
                   *
                   * Emits a {Transfer} event with `to` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   * - `account` must have at least `amount` tokens.
                   */
                  function _burn(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: burn from the zero address");
              
                      _beforeTokenTransfer(account, address(0), amount);
              
                      _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                      _totalSupply = _totalSupply.sub(amount);
                      emit Transfer(account, address(0), amount);
                  }
              
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                   *
                   * This internal function is equivalent to `approve`, and can be used to
                   * e.g. set automatic allowances for certain subsystems, etc.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `owner` cannot be the zero address.
                   * - `spender` cannot be the zero address.
                   */
                  function _approve(address owner, address spender, uint256 amount) internal virtual {
                      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);
                  }
              
                  /**
                   * @dev Sets {decimals} to a value other than the default one of 18.
                   *
                   * WARNING: This function should only be called from the constructor. Most
                   * applications that interact with token contracts will not expect
                   * {decimals} to ever change, and may work incorrectly if it does.
                   */
                  function _setupDecimals(uint8 decimals_) internal {
                      _decimals = decimals_;
                  }
              
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * will be to transferred to `to`.
                   * - when `from` is zero, `amount` tokens will be minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
              }
              
              
              // File @openzeppelin/contracts/token/ERC20/[email protected]
              
              pragma solidity ^0.7.0;
              
              
              /**
               * @dev Extension of {ERC20} that allows token holders to destroy both their own
               * tokens and those that they have an allowance for, in a way that can be
               * recognized off-chain (via event analysis).
               */
              abstract contract ERC20Burnable is Context, ERC20 {
                  using SafeMath for uint256;
              
                  /**
                   * @dev Destroys `amount` tokens from the caller.
                   *
                   * See {ERC20-_burn}.
                   */
                  function burn(uint256 amount) public virtual {
                      _burn(_msgSender(), amount);
                  }
              
                  /**
                   * @dev Destroys `amount` tokens from `account`, deducting from the caller's
                   * allowance.
                   *
                   * See {ERC20-_burn} and {ERC20-allowance}.
                   *
                   * Requirements:
                   *
                   * - the caller must have allowance for ``accounts``'s tokens of at least
                   * `amount`.
                   */
                  function burnFrom(address account, uint256 amount) public virtual {
                      uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
              
                      _approve(account, _msgSender(), decreasedAllowance);
                      _burn(account, amount);
                  }
              }
              
              
              // File @openzeppelin/contracts/cryptography/[email protected]
              
              pragma solidity ^0.7.0;
              
              /**
               * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
               *
               * These functions can be used to verify that a message was signed by the holder
               * of the private keys of a given address.
               */
              library ECDSA {
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature`. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   */
                  function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                      // Check the signature length
                      if (signature.length != 65) {
                          revert("ECDSA: invalid signature length");
                      }
              
                      // Divide the signature in r, s and v variables
                      bytes32 r;
                      bytes32 s;
                      uint8 v;
              
                      // ecrecover takes the signature parameters, and the only way to get them
                      // currently is to use assembly.
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          r := mload(add(signature, 0x20))
                          s := mload(add(signature, 0x40))
                          v := byte(0, mload(add(signature, 0x60)))
                      }
              
                      // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                      // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                      // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
                      // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                      //
                      // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                      // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                      // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                      // these malleable signatures as well.
                      require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
                      require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
              
                      // If the signature is valid (and not malleable), return the signer address
                      address signer = ecrecover(hash, v, r, s);
                      require(signer != address(0), "ECDSA: invalid signature");
              
                      return signer;
                  }
              
                  /**
                   * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                   * replicates the behavior of the
                   * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
                   * JSON-RPC method.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                      // 32 is the length in bytes of hash,
                      // enforced by the type signature above
                      return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
                  }
              }
              
              
              // File contracts/governance/Governed.sol
              
              pragma solidity ^0.7.3;
              
              /**
               * @title Graph Governance contract
               * @dev All contracts that will be owned by a Governor entity should extend this contract.
               */
              contract Governed {
                  // -- State --
              
                  address public governor;
                  address public pendingGovernor;
              
                  // -- Events --
              
                  event NewPendingOwnership(address indexed from, address indexed to);
                  event NewOwnership(address indexed from, address indexed to);
              
                  /**
                   * @dev Check if the caller is the governor.
                   */
                  modifier onlyGovernor {
                      require(msg.sender == governor, "Only Governor can call");
                      _;
                  }
              
                  /**
                   * @dev Initialize the governor to the contract caller.
                   */
                  function _initialize(address _initGovernor) internal {
                      governor = _initGovernor;
                  }
              
                  /**
                   * @dev Admin function to begin change of governor. The `_newGovernor` must call
                   * `acceptOwnership` to finalize the transfer.
                   * @param _newGovernor Address of new `governor`
                   */
                  function transferOwnership(address _newGovernor) external onlyGovernor {
                      require(_newGovernor != address(0), "Governor must be set");
              
                      address oldPendingGovernor = pendingGovernor;
                      pendingGovernor = _newGovernor;
              
                      emit NewPendingOwnership(oldPendingGovernor, pendingGovernor);
                  }
              
                  /**
                   * @dev Admin function for pending governor to accept role and update governor.
                   * This function must called by the pending governor.
                   */
                  function acceptOwnership() external {
                      require(
                          pendingGovernor != address(0) && msg.sender == pendingGovernor,
                          "Caller must be pending governor"
                      );
              
                      address oldGovernor = governor;
                      address oldPendingGovernor = pendingGovernor;
              
                      governor = pendingGovernor;
                      pendingGovernor = address(0);
              
                      emit NewOwnership(oldGovernor, governor);
                      emit NewPendingOwnership(oldPendingGovernor, pendingGovernor);
                  }
              }
              
              
              // File contracts/token/GraphToken.sol
              
              pragma solidity ^0.7.3;
              
              
              
              
              /**
               * @title GraphToken contract
               * @dev This is the implementation of the ERC20 Graph Token.
               * The implementation exposes a Permit() function to allow for a spender to send a signed message
               * and approve funds to a spender following EIP2612 to make integration with other contracts easier.
               *
               * The token is initially owned by the deployer address that can mint tokens to create the initial
               * distribution. For convenience, an initial supply can be passed in the constructor that will be
               * assigned to the deployer.
               *
               * The governor can add the RewardsManager contract to mint indexing rewards.
               *
               */
              contract GraphToken is Governed, ERC20, ERC20Burnable {
                  using SafeMath for uint256;
              
                  // -- EIP712 --
                  // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator
              
                  bytes32 private constant DOMAIN_TYPE_HASH = keccak256(
                      "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"
                  );
                  bytes32 private constant DOMAIN_NAME_HASH = keccak256("Graph Token");
                  bytes32 private constant DOMAIN_VERSION_HASH = keccak256("0");
                  bytes32
                      private constant DOMAIN_SALT = 0x51f3d585afe6dfeb2af01bba0889a36c1db03beec88c6a4d0c53817069026afa; // Randomly generated salt
                  bytes32 private constant PERMIT_TYPEHASH = keccak256(
                      "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                  );
              
                  // -- State --
              
                  bytes32 private DOMAIN_SEPARATOR;
                  mapping(address => bool) private _minters;
                  mapping(address => uint256) public nonces;
              
                  // -- Events --
              
                  event MinterAdded(address indexed account);
                  event MinterRemoved(address indexed account);
              
                  modifier onlyMinter() {
                      require(isMinter(msg.sender), "Only minter can call");
                      _;
                  }
              
                  /**
                   * @dev Graph Token Contract Constructor.
                   * @param _initialSupply Initial supply of GRT
                   */
                  constructor(uint256 _initialSupply) ERC20("Graph Token", "GRT") {
                      Governed._initialize(msg.sender);
              
                      // The Governor has the initial supply of tokens
                      _mint(msg.sender, _initialSupply);
              
                      // The Governor is the default minter
                      _addMinter(msg.sender);
              
                      // EIP-712 domain separator
                      DOMAIN_SEPARATOR = keccak256(
                          abi.encode(
                              DOMAIN_TYPE_HASH,
                              DOMAIN_NAME_HASH,
                              DOMAIN_VERSION_HASH,
                              _getChainID(),
                              address(this),
                              DOMAIN_SALT
                          )
                      );
                  }
              
                  /**
                   * @dev Approve token allowance by validating a message signed by the holder.
                   * @param _owner Address of the token holder
                   * @param _spender Address of the approved spender
                   * @param _value Amount of tokens to approve the spender
                   * @param _deadline Expiration time of the signed permit
                   * @param _v Signature version
                   * @param _r Signature r value
                   * @param _s Signature s value
                   */
                  function permit(
                      address _owner,
                      address _spender,
                      uint256 _value,
                      uint256 _deadline,
                      uint8 _v,
                      bytes32 _r,
                      bytes32 _s
                  ) external {
                      bytes32 digest = keccak256(
                          abi.encodePacked(
                              "\x19\x01",
                              DOMAIN_SEPARATOR,
                              keccak256(
                                  abi.encode(
                                      PERMIT_TYPEHASH,
                                      _owner,
                                      _spender,
                                      _value,
                                      nonces[_owner],
                                      _deadline
                                  )
                              )
                          )
                      );
                      nonces[_owner] = nonces[_owner].add(1);
              
                      address recoveredAddress = ECDSA.recover(digest, abi.encodePacked(_r, _s, _v));
                      require(_owner == recoveredAddress, "GRT: invalid permit");
                      require(_deadline == 0 || block.timestamp <= _deadline, "GRT: expired permit");
              
                      _approve(_owner, _spender, _value);
                  }
              
                  /**
                   * @dev Add a new minter.
                   * @param _account Address of the minter
                   */
                  function addMinter(address _account) external onlyGovernor {
                      _addMinter(_account);
                  }
              
                  /**
                   * @dev Remove a minter.
                   * @param _account Address of the minter
                   */
                  function removeMinter(address _account) external onlyGovernor {
                      _removeMinter(_account);
                  }
              
                  /**
                   * @dev Renounce to be a minter.
                   */
                  function renounceMinter() external {
                      _removeMinter(msg.sender);
                  }
              
                  /**
                   * @dev Mint new tokens.
                   * @param _to Address to send the newly minted tokens
                   * @param _amount Amount of tokens to mint
                   */
                  function mint(address _to, uint256 _amount) external onlyMinter {
                      _mint(_to, _amount);
                  }
              
                  /**
                   * @dev Return if the `_account` is a minter or not.
                   * @param _account Address to check
                   * @return True if the `_account` is minter
                   */
                  function isMinter(address _account) public view returns (bool) {
                      return _minters[_account];
                  }
              
                  /**
                   * @dev Add a new minter.
                   * @param _account Address of the minter
                   */
                  function _addMinter(address _account) private {
                      _minters[_account] = true;
                      emit MinterAdded(_account);
                  }
              
                  /**
                   * @dev Remove a minter.
                   * @param _account Address of the minter
                   */
                  function _removeMinter(address _account) private {
                      _minters[_account] = false;
                      emit MinterRemoved(_account);
                  }
              
                  /**
                   * @dev Get the running network chain ID.
                   * @return The chain ID
                   */
                  function _getChainID() private pure returns (uint256) {
                      uint256 id;
                      assembly {
                          id := chainid()
                      }
                      return id;
                  }
              }

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

              File 5 of 5: DappRegistry
              // Copyright (C) 2021  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.8.3;
              import "./IAuthoriser.sol";
              import "./dapp/IFilter.sol";
              contract DappRegistry is IAuthoriser {
                  // The timelock period
                  uint64 public timelockPeriod;
                  // The new timelock period
                  uint64 public newTimelockPeriod;
                  // Time at which the new timelock becomes effective
                  uint64 public timelockPeriodChangeAfter;
                  // bit vector of enabled registry ids for each wallet
                  mapping (address => bytes32) public enabledRegistryIds; // [wallet] => [bit vector of 256 registry ids]
                  // authorised dapps and their filters for each registry id
                  mapping (uint8 => mapping (address => bytes32)) public authorisations; // [registryId] => [dapp] => [{filter:160}{validAfter:64}]
                  // pending authorised dapps and their filters for each registry id
                  mapping (uint8 => mapping (address => bytes32)) public pendingFilterUpdates; // [registryId] => [dapp] => [{filter:160}{validAfter:64}]
                  // owners for each registry id
                  mapping (uint8 => address) public registryOwners; // [registryId] => [owner]
                  
                  event RegistryCreated(uint8 registryId, address registryOwner);
                  event OwnerChanged(uint8 registryId, address newRegistryOwner);
                  event TimelockChangeRequested(uint64 newTimelockPeriod);
                  event TimelockChanged(uint64 newTimelockPeriod);
                  event FilterUpdated(uint8 indexed registryId, address dapp, address filter, uint256 validAfter);
                  event FilterUpdateRequested(uint8 indexed registryId, address dapp, address filter, uint256 validAfter);
                  event DappAdded(uint8 indexed registryId, address dapp, address filter, uint256 validAfter);
                  event DappRemoved(uint8 indexed registryId, address dapp);
                  event ToggledRegistry(address indexed sender, uint8 registryId, bool enabled);
                  modifier onlyOwner(uint8 _registryId) {
                      validateOwner(_registryId);
                      _;
                  }
                  
                  constructor(uint64 _timelockPeriod) {
                      // set the timelock period
                      timelockPeriod = _timelockPeriod;
                      // set the owner of the Argent Registry (registryId = 0)
                      registryOwners[0] = msg.sender;
                      emit RegistryCreated(0, msg.sender);
                      emit TimelockChanged(_timelockPeriod);
                  }
                  /********* Wallet-centered functions *************/
                  /**
                  * @notice Returns whether a registry is enabled for a wallet
                  * @param _wallet The wallet
                  * @param _registryId The registry id
                  */
                  function isEnabledRegistry(address _wallet, uint8 _registryId) external view returns (bool isEnabled) {
                      uint registries = uint(enabledRegistryIds[_wallet]);
                      return (((registries >> _registryId) & 1) > 0) /* "is bit set for regId?" */ == (_registryId > 0) /* "not Argent registry?" */;
                  }
                  /**
                  * @notice Returns whether a (_spender, _to, _data) call is authorised for a wallet
                  * @param _wallet The wallet
                  * @param _spender The spender of the tokens for token approvals, or the target of the transaction otherwise
                  * @param _to The target of the transaction
                  * @param _data The calldata of the transaction
                  */
                  function isAuthorised(address _wallet, address _spender, address _to, bytes calldata _data) public view override returns (bool) {
                      uint registries = uint(enabledRegistryIds[_wallet]);
                      // Check Argent Default Registry first. It is enabled by default, implying that a zero 
                      // at position 0 of the `registries` bit vector means that the Argent Registry is enabled)
                      for(uint registryId = 0; registryId == 0 || (registries >> registryId) > 0; registryId++) {
                          bool isEnabled = (((registries >> registryId) & 1) > 0) /* "is bit set for regId?" */ == (registryId > 0) /* "not Argent registry?" */;
                          if(isEnabled) { // if registryId is enabled
                              uint auth = uint(authorisations[uint8(registryId)][_spender]); 
                              uint validAfter = auth & 0xffffffffffffffff;
                              if (0 < validAfter && validAfter <= block.timestamp) { // if the current time is greater than the validity time
                                  address filter = address(uint160(auth >> 64));
                                  if(filter == address(0) || IFilter(filter).isValid(_wallet, _spender, _to, _data)) {
                                      return true;
                                  }
                              }
                          }
                      }
                      return false;
                  }
                  /**
                  * @notice Returns whether a collection of (_spender, _to, _data) calls are authorised for a wallet
                  * @param _wallet The wallet
                  * @param _spenders The spenders of the tokens for token approvals, or the targets of the transaction otherwise
                  * @param _to The targets of the transaction
                  * @param _data The calldata of the transaction
                  */
                  function areAuthorised(
                      address _wallet,
                      address[] calldata _spenders,
                      address[] calldata _to,
                      bytes[] calldata _data
                  )
                      external
                      view
                      override
                      returns (bool) 
                  {
                      for(uint i = 0; i < _spenders.length; i++) {
                          if(!isAuthorised(_wallet, _spenders[i], _to[i], _data[i])) {
                              return false;
                          }
                      }
                      return true;
                  }
                  /**
                  * @notice Allows a wallet to decide whether _registryId should be part of the list of enabled registries for that wallet
                  * @param _registryId The id of the registry to enable/disable
                  * @param _enabled Whether the registry should be enabled (true) or disabled (false)
                  */
                  function toggleRegistry(uint8 _registryId, bool _enabled) external {
                      require(registryOwners[_registryId] != address(0), "DR: unknown registry");
                      uint registries = uint(enabledRegistryIds[msg.sender]);
                      bool current = (((registries >> _registryId) & 1) > 0) /* "is bit set for regId?" */ == (_registryId > 0) /* "not Argent registry?" */;
                      if(current != _enabled) {
                          enabledRegistryIds[msg.sender] = bytes32(registries ^ (uint(1) << _registryId)); // toggle [_registryId]^th bit
                          emit ToggledRegistry(msg.sender, _registryId, _enabled);
                      }
                  }
                  /**************  Management of registry list  *****************/
                  /**
                  * @notice Create a new registry. Only the owner of the Argent registry (i.e. the registry with id 0 -- hence the use of `onlyOwner(0)`)
                  * can create a new registry.
                  * @param _registryId The id of the registry to create
                  * @param _registryOwner The owner of that new registry
                  */
                  function createRegistry(uint8 _registryId, address _registryOwner) external onlyOwner(0) {
                      require(_registryOwner != address(0), "DR: registry owner is 0");
                      require(registryOwners[_registryId] == address(0), "DR: duplicate registry");
                      registryOwners[_registryId] = _registryOwner;
                      emit RegistryCreated(_registryId, _registryOwner);
                  }
                  // Note: removeRegistry is not supported because that would allow the owner to replace registries that 
                  // have already been enabled by users with a new (potentially maliciously populated) registry 
                  /**
                  * @notice Lets a registry owner change the owner of the registry.
                  * @param _registryId The id of the registry
                  * @param _newRegistryOwner The new owner of the registry
                  */
                  function changeOwner(uint8 _registryId, address _newRegistryOwner) external onlyOwner(_registryId) {
                      require(_newRegistryOwner != address(0), "DR: new registry owner is 0");
                      registryOwners[_registryId] = _newRegistryOwner;
                      emit OwnerChanged(_registryId, _newRegistryOwner);
                  }
                  /**
                  * @notice Request a change of the timelock value. Only the owner of the Argent registry (i.e. the registry with id 0 -- 
                  * hence the use of `onlyOwner(0)`) can perform that action. This action can be confirmed after the (old) timelock period.
                  * @param _newTimelockPeriod The new timelock period
                  */
                  function requestTimelockChange(uint64 _newTimelockPeriod) external onlyOwner(0) {
                      newTimelockPeriod = _newTimelockPeriod;
                      timelockPeriodChangeAfter = uint64(block.timestamp) + timelockPeriod;
                      emit TimelockChangeRequested(_newTimelockPeriod);
                  }
                  /**
                  * @notice Confirm a change of the timelock value requested by `requestTimelockChange()`.
                  */
                  function confirmTimelockChange() external {
                      uint64 newPeriod = newTimelockPeriod;
                      require(timelockPeriodChangeAfter > 0 && timelockPeriodChangeAfter <= block.timestamp, "DR: can't (yet) change timelock");
                      timelockPeriod = newPeriod;
                      newTimelockPeriod = 0;
                      timelockPeriodChangeAfter = 0;
                      emit TimelockChanged(newPeriod);
                  }
                  /**************  Management of registries' content  *****************/
                  /**
                  * @notice Returns the (filter, validAfter) tuple recorded for a dapp in a given registry.
                  * `filter` is the authorisation filter stored for the dapp (if any) and `validAfter` is the 
                  * timestamp after which the filter becomes active.
                  * @param _registryId The registry id
                  * @param _dapp The dapp
                  */
                  function getAuthorisation(uint8 _registryId, address _dapp) external view returns (address filter, uint64 validAfter) {
                      uint auth = uint(authorisations[_registryId][_dapp]);
                      filter = address(uint160(auth >> 64));
                      validAfter = uint64(auth & 0xffffffffffffffff);
                  }
                  /**
                  * @notice Add a new dapp to the registry with an optional filter
                  * @param _registryId The id of the registry to modify
                  * @param _dapp The address of the dapp contract to authorise.
                  * @param _filter The address of the filter contract to use, if any.
                  */
                  function addDapp(uint8 _registryId, address _dapp, address _filter) external onlyOwner(_registryId) {
                      require(authorisations[_registryId][_dapp] == bytes32(0), "DR: dapp already added");
                      uint validAfter = block.timestamp + timelockPeriod;
                      // Store the new authorisation as {filter:160}{validAfter:64}.
                      authorisations[_registryId][_dapp] = bytes32((uint(uint160(_filter)) << 64) | validAfter);
                      emit DappAdded(_registryId, _dapp, _filter, validAfter);
                  }
                  /**
                  * @notice Deauthorise a dapp in a registry
                  * @param _registryId The id of the registry to modify
                  * @param _dapp The address of the dapp contract to deauthorise.
                  */
                  function removeDapp(uint8 _registryId, address _dapp) external onlyOwner(_registryId) {
                      require(authorisations[_registryId][_dapp] != bytes32(0), "DR: unknown dapp");
                      delete authorisations[_registryId][_dapp];
                      delete pendingFilterUpdates[_registryId][_dapp];
                      emit DappRemoved(_registryId, _dapp);
                  }
                  /**
                  * @notice Request to change an authorisation filter for a dapp that has previously been authorised. We cannot 
                  * immediately override the existing filter and need to store the new filter for a timelock period before being 
                  * able to change the filter.
                  * @param _registryId The id of the registry to modify
                  * @param _dapp The address of the dapp contract to authorise.
                  * @param _filter The address of the new filter contract to use.
                  */
                  function requestFilterUpdate(uint8 _registryId, address _dapp, address _filter) external onlyOwner(_registryId) {
                      require(authorisations[_registryId][_dapp] != bytes32(0), "DR: unknown dapp");
                      uint validAfter = block.timestamp + timelockPeriod;
                      // Store the future authorisation as {filter:160}{validAfter:64}
                      pendingFilterUpdates[_registryId][_dapp] = bytes32((uint(uint160(_filter)) << 64) | validAfter);
                      emit FilterUpdateRequested(_registryId, _dapp, _filter, validAfter);
                  }
                  /**
                  * @notice Confirm the filter change requested by `requestFilterUpdate`
                  * @param _registryId The id of the registry to modify
                  * @param _dapp The address of the dapp contract to authorise.
                  */
                  function confirmFilterUpdate(uint8 _registryId, address _dapp) external {
                      uint newAuth = uint(pendingFilterUpdates[_registryId][_dapp]);
                      require(newAuth > 0, "DR: no pending filter update");
                      uint validAfter = newAuth & 0xffffffffffffffff;
                      require(validAfter <= block.timestamp, "DR: too early to confirm auth");
                      authorisations[_registryId][_dapp] = bytes32(newAuth);
                      emit FilterUpdated(_registryId, _dapp, address(uint160(newAuth >> 64)), validAfter); 
                      delete pendingFilterUpdates[_registryId][_dapp];
                  }
                  /********  Internal Functions ***********/
                  function validateOwner(uint8 _registryId) internal view {
                      address owner = registryOwners[_registryId];
                      require(owner != address(0), "DR: unknown registry");
                      require(msg.sender == owner, "DR: sender != registry owner");
                  }
              }// Copyright (C) 2021  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.8.3;
              interface IAuthoriser {
                  function isAuthorised(address _sender, address _spender, address _to, bytes calldata _data) external view returns (bool);
                  function areAuthorised(
                      address _spender,
                      address[] calldata _spenders,
                      address[] calldata _to,
                      bytes[] calldata _data
                  )
                      external
                      view
                      returns (bool);
              }// Copyright (C) 2021  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.8.3;
              interface IFilter {
                  function isValid(address _wallet, address _spender, address _to, bytes calldata _data) external view returns (bool valid);
              }