ETH Price: $2,049.74 (+0.65%)

Transaction Decoder

Block:
12504241 at May-25-2021 03:13:16 PM +UTC
Transaction Fee:
0.00735552 ETH $15.08
Gas Used:
153,240 Gas / 48 Gwei

Emitted Events:

348 0x1b5db98cb4447a4e47c63509a9825070b5e88991.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x0000000000000000000000009d58779365b067d5d3fcc6e92d237acd06f1e6a1, 0x000000000000000000000000c37b668fb269ab21dae2d3c3bd49c8fbe760eba5, 0x0000000000000000000000000000000000000000000000000088ca376d720190, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000000 )
349 0x1b5db98cb4447a4e47c63509a9825070b5e88991.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x0000000000000000000000009d58779365b067d5d3fcc6e92d237acd06f1e6a1, 0x000000000000000000000000482579f93dc13e6b434e38b5a0447ca543d88a46, 0x000000000000000000000000000000000000000000000000001a1c9c8701e000, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000000 )
350 ArgentModule.Refund( wallet=0x1b5db98cb4447a4e47c63509a9825070b5e88991, refundAddress=0x482579f93dc13e6b434e38b5a0447ca543d88a46, refundToken=0x00000000...000000000, refundAmount=7349808000000000 )
351 ArgentModule.TransactionExecuted( wallet=0x1b5db98cb4447a4e47c63509a9825070b5e88991, success=True, returnData=0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000, signedHash=DE587C815B944135819D7AA866ED6D2E5C1B49E87004A3EA2085A6055FCCB8D6 )

Account State Difference:

  Address   Before After State Difference Code
0x1b5dB98C...0b5e88991 0.047118928320849296 Eth0.00126618408 Eth0.045852744240849296
0x482579F9...543D88A46 5.108616333006548484 Eth5.115966141006548484 Eth0.007349808
0x5564ac43...907A00e83
(Argent: Relayer 5)
3.8259599903081 Eth
Nonce: 10446
3.8186044703081 Eth
Nonce: 10447
0.00735552
(Spark Pool)
45.528225686528465498 Eth45.535581206528465498 Eth0.00735552
0x9D587793...d06F1e6a1
(Argent: Argent Module)
0xc37B668f...BE760Eba5
0 Eth
Nonce: 0
0.038502936240849296 Eth
Nonce: 0
0.038502936240849296From: 0 To: 0

Execution Trace

ArgentModule.execute( _wallet=0x1b5dB98Cb4447A4E47C63509a9825070b5e88991, _data=0xF143DDBA0000000000000000000000001B5DB98CB4447A4E47C63509A9825070B5E88991000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000C37B668FB269AB21DAE2D3C3BD49C8FBE760EBA50000000000000000000000000000000000000000000000000088CA376D72019000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000, _nonce=4254988377018720856485050079839162635144467422, _signatures=0x3F21CFB27D5491A9502AA6965135697B3F8760A3DA94D1E35FA03156522CDF9B67FD3021D37D88179F52FD363505C70F9B9C36C056972045B5EF2BFAA095041D1CDEEE15DE6F2E226FD73218B1F255E6B7F435A5154E924290D272509851C6AFDD53FC67106FD562FBD4ABE7E378B9F0ED4752F1610C74834434D79C8453A6A92D1C, _gasPrice=54420000000, _gasLimit=158324, _refundToken=0x0000000000000000000000000000000000000000, _refundAddress=0x482579F93dC13e6B434E38b5a0447ca543D88A46 ) => ( True )
  • 0x44da3a8051ba88eab0440db3779cab9d679ae76f.5040fb76( )
  • 0x44da3a8051ba88eab0440db3779cab9d679ae76f.f18858ab( )
  • Null: 0x000...001.de587c81( )
  • 0x1b5db98cb4447a4e47c63509a9825070b5e88991.STATICCALL( )
    • BaseWallet.DELEGATECALL( )
    • Null: 0x000...001.de587c81( )
    • ArgentModule.multiCallWithGuardians( _wallet=0x1b5dB98Cb4447A4E47C63509a9825070b5e88991, _transactions= ) => ( [] )
      • 0x1b5db98cb4447a4e47c63509a9825070b5e88991.8f6f0332( )
        • BaseWallet.invoke( _target=0xc37B668fb269ab21DAE2D3c3bD49c8FBE760Eba5, _value=38502936240849296, _data=0x ) => ( _result=0x )
          • ETH 0.038502936240849296 0xc37b668fb269ab21dae2d3c3bd49c8fbe760eba5.CALL( )
          • 0x1b5db98cb4447a4e47c63509a9825070b5e88991.8f6f0332( )
            • BaseWallet.invoke( _target=0x482579F93dC13e6B434E38b5a0447ca543D88A46, _value=7349808000000000, _data=0x ) => ( _result=0x )
              • ETH 0.007349808 0x482579f93dc13e6b434e38b5a0447ca543d88a46.CALL( )
                File 1 of 2: 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 - <julien@argent.xyz>
                 */
                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 <julien@argent.xyz>, Olivier VDB <olivier@argent.xyz>
                 */
                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 - <julien@argent.xyz>
                 * @author Olivier Van Den Biggelaar - <olivier@argent.xyz>
                 */
                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 - <julien@argent.xyz>
                 */
                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 - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                 */
                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 - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                 */
                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 2: 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/>.
                // SPDX-License-Identifier: GPL-3.0-only
                pragma solidity ^0.8.3;
                /**
                 * @title IModule
                 * @notice Interface for a Module.
                 * @author Julien Niset - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
                 */
                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) 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 "../modules/common/IModule.sol";
                import "./IWallet.sol";
                /**
                 * @title BaseWallet
                 * @notice Simple modular wallet that authorises modules to call its invoke() method.
                 * @author Julien Niset - <julien@argent.xyz>
                 */
                contract BaseWallet is IWallet {
                    // The owner
                    address public override owner;
                    // The authorised modules
                    mapping (address => bool) public override authorised;
                    // module executing static calls
                    address public staticCallExecutor;
                    // The number of modules
                    uint public override modules;
                    event AuthorisedModule(address indexed module, bool value);
                    event Invoked(address indexed module, address indexed target, uint indexed value, bytes data);
                    event Received(uint indexed value, address indexed sender, bytes data);
                    event OwnerChanged(address owner);
                    /**
                     * @notice Throws if the sender is not an authorised module.
                     */
                    modifier moduleOnly {
                        require(authorised[msg.sender], "BW: sender not authorized");
                        _;
                    }
                    /**
                     * @notice Inits the wallet by setting the owner and authorising a list of modules.
                     * @param _owner The owner.
                     * @param _modules The modules to authorise.
                     */
                    function init(address _owner, address[] calldata _modules) external {
                        require(owner == address(0) && modules == 0, "BW: wallet already initialised");
                        require(_modules.length > 0, "BW: empty modules");
                        owner = _owner;
                        modules = _modules.length;
                        for (uint256 i = 0; i < _modules.length; i++) {
                            require(authorised[_modules[i]] == false, "BW: module is already added");
                            authorised[_modules[i]] = true;
                            IModule(_modules[i]).init(address(this));
                            emit AuthorisedModule(_modules[i], true);
                        }
                        if (address(this).balance > 0) {
                            emit Received(address(this).balance, address(0), "");
                        }
                    }
                    /**
                     * @inheritdoc IWallet
                     */
                    function authoriseModule(address _module, bool _value) external override moduleOnly {
                        if (authorised[_module] != _value) {
                            emit AuthorisedModule(_module, _value);
                            if (_value == true) {
                                modules += 1;
                                authorised[_module] = true;
                                IModule(_module).init(address(this));
                            } else {
                                modules -= 1;
                                require(modules > 0, "BW: cannot remove last module");
                                delete authorised[_module];
                            }
                        }
                    }
                    /**
                    * @inheritdoc IWallet
                    */
                    function enabled(bytes4 _sig) public view override returns (address) {
                        address executor = staticCallExecutor;
                        if(executor != address(0) && IModule(executor).supportsStaticCall(_sig)) {
                            return executor;
                        }
                        return address(0);
                    }
                    /**
                    * @inheritdoc IWallet
                    */
                    function enableStaticCall(address _module, bytes4 /* _method */) external override moduleOnly {
                        if(staticCallExecutor != _module) {
                            require(authorised[_module], "BW: unauthorized executor");
                            staticCallExecutor = _module;
                        }
                    }
                    /**
                     * @inheritdoc IWallet
                     */
                    function setOwner(address _newOwner) external override moduleOnly {
                        require(_newOwner != address(0), "BW: address cannot be null");
                        owner = _newOwner;
                        emit OwnerChanged(_newOwner);
                    }
                    /**
                     * @notice Performs a generic transaction.
                     * @param _target The address for the transaction.
                     * @param _value The value of the transaction.
                     * @param _data The data of the transaction.
                     */
                    function invoke(address _target, uint _value, bytes calldata _data) external moduleOnly returns (bytes memory _result) {
                        bool success;
                        (success, _result) = _target.call{value: _value}(_data);
                        if (!success) {
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                returndatacopy(0, 0, returndatasize())
                                revert(0, returndatasize())
                            }
                        }
                        emit Invoked(msg.sender, _target, _value, _data);
                    }
                    /**
                     * @notice This method delegates the static call to a target contract if the data corresponds
                     * to an enabled module, or logs the call otherwise.
                     */
                    fallback() external payable {
                        address module = enabled(msg.sig);
                        if (module == address(0)) {
                            emit Received(msg.value, msg.sender, msg.data);
                        } else {
                            require(authorised[module], "BW: unauthorised module");
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                calldatacopy(0, 0, calldatasize())
                                let result := staticcall(gas(), module, 0, calldatasize(), 0, 0)
                                returndatacopy(0, 0, returndatasize())
                                switch result
                                case 0 {revert(0, returndatasize())}
                                default {return (0, returndatasize())}
                            }
                        }
                    }
                    receive() external payable {
                    }
                }// 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;
                }