ETH Price: $2,418.55 (+0.03%)

Contract

0xf6D9851893C315ae6f4916E1e29A72Dfb39612Ac
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Execute208622602024-09-30 8:25:595 days ago1727684759IN
0xf6D98518...fb39612Ac
0 ETH0.001289886.38347395
Execute207784962024-09-18 15:52:5917 days ago1726674779IN
0xf6D98518...fb39612Ac
0 ETH0.0016401813.54406286
Execute207782092024-09-18 14:55:2317 days ago1726671323IN
0xf6D98518...fb39612Ac
0 ETH0.001388611.752
Execute207781452024-09-18 14:42:3517 days ago1726670555IN
0xf6D98518...fb39612Ac
0 ETH0.0029945111.19239815
Execute207781412024-09-18 14:41:4717 days ago1726670507IN
0xf6D98518...fb39612Ac
0 ETH0.0014690811.27046164
Execute207715372024-09-17 16:31:3518 days ago1726590695IN
0xf6D98518...fb39612Ac
0 ETH0.0035206222.97997456
Execute207711762024-09-17 15:18:2318 days ago1726586303IN
0xf6D98518...fb39612Ac
0 ETH0.0020580123.048878
Execute207710422024-09-17 14:51:2318 days ago1726584683IN
0xf6D98518...fb39612Ac
0 ETH0.0028551710.52616164
Execute207709752024-09-17 14:37:5918 days ago1726583879IN
0xf6D98518...fb39612Ac
0 ETH0.0012862311.4689582
Execute207709662024-09-17 14:36:1118 days ago1726583771IN
0xf6D98518...fb39612Ac
0 ETH0.0018339911.82065683
Execute207650452024-09-16 18:44:4719 days ago1726512287IN
0xf6D98518...fb39612Ac
0 ETH0.000615493.966
Execute207648182024-09-16 17:59:1119 days ago1726509551IN
0xf6D98518...fb39612Ac
0 ETH0.000695487.55120565
Finalize Recover...207647842024-09-16 17:52:2319 days ago1726509143IN
0xf6D98518...fb39612Ac
0 ETH0.0004541810.9581642
Execute207641822024-09-16 15:51:1119 days ago1726501871IN
0xf6D98518...fb39612Ac
0 ETH0.0015483512.483
Execute207641212024-09-16 15:38:5919 days ago1726501139IN
0xf6D98518...fb39612Ac
0 ETH0.0036449812.47632282
Execute207622022024-09-16 9:10:2319 days ago1726477823IN
0xf6D98518...fb39612Ac
0 ETH0.0029563310.63711238
Execute207621982024-09-16 9:09:3519 days ago1726477775IN
0xf6D98518...fb39612Ac
0 ETH0.0014683411.26481899
Execute207403482024-09-13 7:57:3522 days ago1726214255IN
0xf6D98518...fb39612Ac
0 ETH0.000172451.76632861
Execute207403162024-09-13 7:51:1122 days ago1726213871IN
0xf6D98518...fb39612Ac
0 ETH0.001429341.76952333
Execute207350272024-09-12 14:07:5923 days ago1726150079IN
0xf6D98518...fb39612Ac
0 ETH0.00055664.36879776
Execute207349772024-09-12 13:57:5923 days ago1726149479IN
0xf6D98518...fb39612Ac
0 ETH0.000773313.65068664
Execute207349752024-09-12 13:57:3523 days ago1726149455IN
0xf6D98518...fb39612Ac
0 ETH0.000444673.79720393
Execute207349732024-09-12 13:57:1123 days ago1726149431IN
0xf6D98518...fb39612Ac
0 ETH0.000456653.90883083
Execute207349692024-09-12 13:56:2323 days ago1726149383IN
0xf6D98518...fb39612Ac
0 ETH0.000492933.89210416
Execute207349672024-09-12 13:55:5923 days ago1726149359IN
0xf6D98518...fb39612Ac
0 ETH0.000454773.88547648
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ArgentModule

Compiler Version
v0.8.3+commit.8d00100c

Optimization Enabled:
Yes with 300 runs

Other Settings:
default evmVersion
File 1 of 18 : ArgentModule.sol
// Copyright (C) 2021  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.3;

import "./common/Utils.sol";
import "./common/BaseModule.sol";
import "./RelayerManager.sol";
import "./SecurityManager.sol";
import "./TransactionManager.sol";

/**
 * @title ArgentModule
 * @notice Single module for the Argent wallet.
 * @author Julien Niset - <[email protected]>
 */
contract ArgentModule is BaseModule, RelayerManager, SecurityManager, TransactionManager {

    bytes32 constant public NAME = "ArgentModule";

    constructor (
        IModuleRegistry _registry,
        IGuardianStorage _guardianStorage,
        ITransferStorage _userWhitelist,
        IAuthoriser _authoriser,
        address _uniswapRouter,
        uint256 _securityPeriod,
        uint256 _securityWindow,
        uint256 _recoveryPeriod,
        uint256 _lockPeriod
    )
        BaseModule(_registry, _guardianStorage, _userWhitelist, _authoriser, NAME)
        SecurityManager(_recoveryPeriod, _securityPeriod, _securityWindow, _lockPeriod)
        TransactionManager(_securityPeriod)
        RelayerManager(_uniswapRouter)
    {
        
    }

    /**
     * @inheritdoc IModule
     */
    function init(address _wallet) external override onlyWallet(_wallet) {
        enableDefaultStaticCalls(_wallet);
    }

    /**
    * @inheritdoc IModule
    */
    function addModule(address _wallet, address _module) external override onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
        require(registry.isRegisteredModule(_module), "AM: module is not registered");
        IWallet(_wallet).authoriseModule(_module, true);
    }
    
    /**
     * @inheritdoc RelayerManager
     */
    function getRequiredSignatures(address _wallet, bytes calldata _data) public view override returns (uint256, OwnerSignature) {
        bytes4 methodId = Utils.functionPrefix(_data);

        if (methodId == TransactionManager.multiCall.selector ||
            methodId == TransactionManager.addToWhitelist.selector ||
            methodId == TransactionManager.removeFromWhitelist.selector ||
            methodId == TransactionManager.enableERC1155TokenReceiver.selector ||
            methodId == TransactionManager.clearSession.selector ||
            methodId == ArgentModule.addModule.selector ||
            methodId == SecurityManager.addGuardian.selector ||
            methodId == SecurityManager.revokeGuardian.selector ||
            methodId == SecurityManager.cancelGuardianAddition.selector ||
            methodId == SecurityManager.cancelGuardianRevokation.selector)
        {
            // owner
            return (1, OwnerSignature.Required);
        }
        if (methodId == TransactionManager.multiCallWithSession.selector) {
            return (1, OwnerSignature.Session);
        }
        if (methodId == SecurityManager.executeRecovery.selector) {
            // majority of guardians
            uint numberOfSignaturesRequired = _majorityOfGuardians(_wallet);
            require(numberOfSignaturesRequired > 0, "AM: no guardians set on wallet");
            return (numberOfSignaturesRequired, OwnerSignature.Disallowed);
        }
        if (methodId == SecurityManager.cancelRecovery.selector) {
            // majority of (owner + guardians)
            uint numberOfSignaturesRequired = Utils.ceil(recoveryConfigs[_wallet].guardianCount + 1, 2);
            return (numberOfSignaturesRequired, OwnerSignature.Optional);
        }
        if (methodId == TransactionManager.multiCallWithGuardians.selector ||
            methodId == TransactionManager.multiCallWithGuardiansAndStartSession.selector ||
            methodId == SecurityManager.transferOwnership.selector)
        {
            // owner + majority of guardians
            uint majorityGuardians = _majorityOfGuardians(_wallet);
            uint numberOfSignaturesRequired = majorityGuardians + 1;
            return (numberOfSignaturesRequired, OwnerSignature.Required);
        }
        if (methodId == SecurityManager.finalizeRecovery.selector ||
            methodId == SecurityManager.confirmGuardianAddition.selector ||
            methodId == SecurityManager.confirmGuardianRevokation.selector)
        {
            // anyone
            return (0, OwnerSignature.Anyone);
        }
        if (methodId == SecurityManager.lock.selector || methodId == SecurityManager.unlock.selector) {
            // any guardian
            return (1, OwnerSignature.Disallowed);
        }
        revert("SM: unknown method");
    }

    function _majorityOfGuardians(address _wallet) internal view returns (uint) {
        return Utils.ceil(guardianStorage.guardianCount(_wallet), 2);
    }
}

File 2 of 18 : IAuthoriser.sol
// 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);
}

File 3 of 18 : IModuleRegistry.sol
// Copyright (C) 2020  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// 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);
}

File 4 of 18 : IGuardianStorage.sol
// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// 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);
}

File 5 of 18 : ITransferStorage.sol
// Copyright (C) 2020  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// 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);
}

File 6 of 18 : RelayerManager.sol
// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.3;

import "@openzeppelin/contracts/utils/math/Math.sol";
import "./common/Utils.sol";
import "./common/BaseModule.sol";
import "./common/SimpleOracle.sol";
import "../infrastructure/storage/IGuardianStorage.sol";

/**
 * @title RelayerManager
 * @notice Abstract Module to execute transactions signed by ETH-less accounts and sent by a relayer.
 * @author Julien Niset <[email protected]>, Olivier VDB <[email protected]>
 */
abstract contract RelayerManager is BaseModule, SimpleOracle {

    uint256 constant internal BLOCKBOUND = 10000;

    mapping (address => RelayerConfig) internal relayer;

    struct RelayerConfig {
        uint256 nonce;
        mapping (bytes32 => bool) executedTx;
    }

    // Used to avoid stack too deep error
    struct StackExtension {
        uint256 requiredSignatures;
        OwnerSignature ownerSignatureRequirement;
        bytes32 signHash;
        bool success;
        bytes returnData;
    }

    event TransactionExecuted(address indexed wallet, bool indexed success, bytes returnData, bytes32 signedHash);
    event Refund(address indexed wallet, address indexed refundAddress, address refundToken, uint256 refundAmount);

    // *************** Constructor ************************ //

    constructor(address _uniswapRouter) SimpleOracle(_uniswapRouter) {

    }

    /* ***************** External methods ************************* */

    /**
    * @notice Gets the number of valid signatures that must be provided to execute a
    * specific relayed transaction.
    * @param _wallet The target wallet.
    * @param _data The data of the relayed transaction.
    * @return The number of required signatures and the wallet owner signature requirement.
    */
    function getRequiredSignatures(address _wallet, bytes calldata _data) public view virtual returns (uint256, OwnerSignature);

    /**
    * @notice Executes a relayed transaction.
    * @param _wallet The target wallet.
    * @param _data The data for the relayed transaction
    * @param _nonce The nonce used to prevent replay attacks.
    * @param _signatures The signatures as a concatenated byte array.
    * @param _gasPrice The max gas price (in token) to use for the gas refund.
    * @param _gasLimit The max gas limit to use for the gas refund.
    * @param _refundToken The token to use for the gas refund.
    * @param _refundAddress The address refunded to prevent front-running.
    */
    function execute(
        address _wallet,
        bytes calldata _data,
        uint256 _nonce,
        bytes calldata _signatures,
        uint256 _gasPrice,
        uint256 _gasLimit,
        address _refundToken,
        address _refundAddress
    )
        external
        returns (bool)
    {
        // initial gas = 21k + non_zero_bytes * 16 + zero_bytes * 4
        //            ~= 21k + calldata.length * [1/3 * 16 + 2/3 * 4]
        uint256 startGas = gasleft() + 21000 + msg.data.length * 8;
        require(startGas >= _gasLimit, "RM: not enough gas provided");
        require(verifyData(_wallet, _data), "RM: Target of _data != _wallet");

        require(!_isLocked(_wallet) || _gasPrice == 0, "RM: Locked wallet refund");

        StackExtension memory stack;
        (stack.requiredSignatures, stack.ownerSignatureRequirement) = getRequiredSignatures(_wallet, _data);

        require(stack.requiredSignatures > 0 || stack.ownerSignatureRequirement == OwnerSignature.Anyone, "RM: Wrong signature requirement");
        require(stack.requiredSignatures * 65 == _signatures.length, "RM: Wrong number of signatures");
        stack.signHash = getSignHash(
            address(this),
            0,
            _data,
            _nonce,
            _gasPrice,
            _gasLimit,
            _refundToken,
            _refundAddress);
        require(checkAndUpdateUniqueness(
            _wallet,
            _nonce,
            stack.signHash,
            stack.requiredSignatures,
            stack.ownerSignatureRequirement), "RM: Duplicate request");

        if (stack.ownerSignatureRequirement == OwnerSignature.Session) {
            require(validateSession(_wallet, stack.signHash, _signatures), "RM: Invalid session");
        } else {
            require(validateSignatures(_wallet, stack.signHash, _signatures, stack.ownerSignatureRequirement), "RM: Invalid signatures");
        }
        (stack.success, stack.returnData) = address(this).call(_data);
        refund(
            _wallet,
            startGas,
            _gasPrice,
            _gasLimit,
            _refundToken,
            _refundAddress,
            stack.requiredSignatures,
            stack.ownerSignatureRequirement);
        emit TransactionExecuted(_wallet, stack.success, stack.returnData, stack.signHash);
        return stack.success;
    }

    /**
    * @notice Gets the current nonce for a wallet.
    * @param _wallet The target wallet.
    */
    function getNonce(address _wallet) external view returns (uint256 nonce) {
        return relayer[_wallet].nonce;
    }

    /**
    * @notice Checks if a transaction identified by its sign hash has already been executed.
    * @param _wallet The target wallet.
    * @param _signHash The sign hash of the transaction.
    */
    function isExecutedTx(address _wallet, bytes32 _signHash) external view returns (bool executed) {
        return relayer[_wallet].executedTx[_signHash];
    }

    /**
    * @notice Gets the last stored session for a wallet.
    * @param _wallet The target wallet.
    */
    function getSession(address _wallet) external view returns (address key, uint64 expires) {
        return (sessions[_wallet].key, sessions[_wallet].expires);
    }

    /* ***************** Internal & Private methods ************************* */

    /**
    * @notice Generates the signed hash of a relayed transaction according to ERC 1077.
    * @param _from The starting address for the relayed transaction (should be the relayer module)
    * @param _value The value for the relayed transaction.
    * @param _data The data for the relayed transaction which includes the wallet address.
    * @param _nonce The nonce used to prevent replay attacks.
    * @param _gasPrice The max gas price (in token) to use for the gas refund.
    * @param _gasLimit The max gas limit to use for the gas refund.
    * @param _refundToken The token to use for the gas refund.
    * @param _refundAddress The address refunded to prevent front-running.
    */
    function getSignHash(
        address _from,
        uint256 _value,
        bytes memory _data,
        uint256 _nonce,
        uint256 _gasPrice,
        uint256 _gasLimit,
        address _refundToken,
        address _refundAddress
    )
        internal
        view
        returns (bytes32)
    {
        return keccak256(
            abi.encodePacked(
                "\x19Ethereum Signed Message:\n32",
                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;
    }
}

File 7 of 18 : SecurityManager.sol
// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.3;

import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "./common/Utils.sol";
import "./common/BaseModule.sol";
import "../wallet/IWallet.sol";

/**
 * @title SecurityManager
 * @notice Abstract module implementing the key security features of the wallet: guardians, lock and recovery.
 * @author Julien Niset - <[email protected]>
 * @author Olivier Van Den Biggelaar - <[email protected]>
 */
abstract contract SecurityManager is BaseModule {

    struct RecoveryConfig {
        address recovery;
        uint64 executeAfter;
        uint32 guardianCount;
    }

    struct GuardianManagerConfig {
        // The time at which a guardian addition or revokation will be confirmable by the owner
        mapping (bytes32 => uint256) pending;
    }

    // Wallet specific storage for recovery
    mapping (address => RecoveryConfig) internal recoveryConfigs;
    // Wallet specific storage for pending guardian addition/revokation
    mapping (address => GuardianManagerConfig) internal guardianConfigs;


    // Recovery period
    uint256 internal immutable recoveryPeriod;
    // Lock period
    uint256 internal immutable lockPeriod;
    // The security period to add/remove guardians
    uint256 internal immutable securityPeriod;
    // The security window
    uint256 internal immutable securityWindow;

    // *************** Events *************************** //

    event RecoveryExecuted(address indexed wallet, address indexed _recovery, uint64 executeAfter);
    event RecoveryFinalized(address indexed wallet, address indexed _recovery);
    event RecoveryCanceled(address indexed wallet, address indexed _recovery);
    event OwnershipTransfered(address indexed wallet, address indexed _newOwner);
    event Locked(address indexed wallet, uint64 releaseAfter);
    event Unlocked(address indexed wallet);
    event GuardianAdditionRequested(address indexed wallet, address indexed guardian, uint256 executeAfter);
    event GuardianRevokationRequested(address indexed wallet, address indexed guardian, uint256 executeAfter);
    event GuardianAdditionCancelled(address indexed wallet, address indexed guardian);
    event GuardianRevokationCancelled(address indexed wallet, address indexed guardian);
    event GuardianAdded(address indexed wallet, address indexed guardian);
    event GuardianRevoked(address indexed wallet, address indexed guardian);
    // *************** Modifiers ************************ //

    /**
     * @notice Throws if there is no ongoing recovery procedure.
     */
    modifier onlyWhenRecovery(address _wallet) {
        require(recoveryConfigs[_wallet].executeAfter > 0, "SM: no ongoing recovery");
        _;
    }

    /**
     * @notice Throws if there is an ongoing recovery procedure.
     */
    modifier notWhenRecovery(address _wallet) {
        require(recoveryConfigs[_wallet].executeAfter == 0, "SM: ongoing recovery");
        _;
    }

    /**
     * @notice Throws if the caller is not a guardian for the wallet or the module itself.
     */
    modifier onlyGuardianOrSelf(address _wallet) {
        require(_isSelf(msg.sender) || isGuardian(_wallet, msg.sender), "SM: must be guardian/self");
        _;
    }

    // *************** Constructor ************************ //

    constructor(
        uint256 _recoveryPeriod,
        uint256 _securityPeriod,
        uint256 _securityWindow,
        uint256 _lockPeriod
    ) {
        // For the wallet to be secure we must have recoveryPeriod >= securityPeriod + securityWindow
        // where securityPeriod and securityWindow are the security parameters of adding/removing guardians.
        require(_lockPeriod >= _recoveryPeriod, "SM: insecure lock period");
        require(_recoveryPeriod >= _securityPeriod + _securityWindow, "SM: insecure security periods");
        recoveryPeriod = _recoveryPeriod;
        lockPeriod = _lockPeriod;
        securityWindow = _securityWindow;
        securityPeriod = _securityPeriod;
    }

    // *************** External functions ************************ //

    // *************** Recovery functions ************************ //

    /**
     * @notice Lets the guardians start the execution of the recovery procedure.
     * Once triggered the recovery is pending for the security period before it can be finalised.
     * Must be confirmed by N guardians, where N = ceil(Nb Guardians / 2).
     * @param _wallet The target wallet.
     * @param _recovery The address to which ownership should be transferred.
     */
    function executeRecovery(address _wallet, address _recovery) external onlySelf() notWhenRecovery(_wallet) {
        validateNewOwner(_wallet, _recovery);
        uint64 executeAfter = uint64(block.timestamp + recoveryPeriod);
        recoveryConfigs[_wallet] = RecoveryConfig(_recovery, executeAfter, uint32(guardianStorage.guardianCount(_wallet)));
        _setLock(_wallet, block.timestamp + lockPeriod, SecurityManager.executeRecovery.selector);
        emit RecoveryExecuted(_wallet, _recovery, executeAfter);
    }

    /**
     * @notice Finalizes an ongoing recovery procedure if the security period is over.
     * The method is public and callable by anyone to enable orchestration.
     * @param _wallet The target wallet.
     */
    function finalizeRecovery(address _wallet) external onlyWhenRecovery(_wallet) {
        RecoveryConfig storage config = recoveryConfigs[_wallet];
        require(uint64(block.timestamp) > config.executeAfter, "SM: ongoing recovery period");
        address recoveryOwner = config.recovery;
        delete recoveryConfigs[_wallet];

        _clearSession(_wallet);

        IWallet(_wallet).setOwner(recoveryOwner);
        _setLock(_wallet, 0, bytes4(0));

        emit RecoveryFinalized(_wallet, recoveryOwner);
    }

    /**
     * @notice Lets the owner cancel an ongoing recovery procedure.
     * Must be confirmed by N guardians, where N = ceil(Nb Guardian at executeRecovery + 1) / 2) - 1.
     * @param _wallet The target wallet.
     */
    function cancelRecovery(address _wallet) external onlySelf() onlyWhenRecovery(_wallet) {
        address recoveryOwner = recoveryConfigs[_wallet].recovery;
        delete recoveryConfigs[_wallet];
        _setLock(_wallet, 0, bytes4(0));

        emit RecoveryCanceled(_wallet, recoveryOwner);
    }

    /**
     * @notice Lets the owner transfer the wallet ownership. This is executed immediately.
     * @param _wallet The target wallet.
     * @param _newOwner The address to which ownership should be transferred.
     */
    function transferOwnership(address _wallet, address _newOwner) external onlySelf() onlyWhenUnlocked(_wallet) {
        validateNewOwner(_wallet, _newOwner);
        IWallet(_wallet).setOwner(_newOwner);

        emit OwnershipTransfered(_wallet, _newOwner);
    }

    /**
    * @notice Gets the details of the ongoing recovery procedure if any.
    * @param _wallet The target wallet.
    */
    function getRecovery(address _wallet) external view returns(address _address, uint64 _executeAfter, uint32 _guardianCount) {
        RecoveryConfig storage config = recoveryConfigs[_wallet];
        return (config.recovery, config.executeAfter, config.guardianCount);
    }

    // *************** Lock functions ************************ //

    /**
     * @notice Lets a guardian lock a wallet.
     * @param _wallet The target wallet.
     */
    function lock(address _wallet) external onlyGuardianOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
        _setLock(_wallet, block.timestamp + lockPeriod, SecurityManager.lock.selector);
        emit Locked(_wallet, uint64(block.timestamp + lockPeriod));
    }

    /**
     * @notice Lets a guardian unlock a locked wallet.
     * @param _wallet The target wallet.
     */
    function unlock(address _wallet) external onlyGuardianOrSelf(_wallet) onlyWhenLocked(_wallet) {
        require(locks[_wallet].locker == SecurityManager.lock.selector, "SM: cannot unlock");
        _setLock(_wallet, 0, bytes4(0));
        emit Unlocked(_wallet);
    }

    /**
     * @notice Returns the release time of a wallet lock or 0 if the wallet is unlocked.
     * @param _wallet The target wallet.
     * @return _releaseAfter The epoch time at which the lock will release (in seconds).
     */
    function getLock(address _wallet) external view returns(uint64 _releaseAfter) {
        return _isLocked(_wallet) ? locks[_wallet].release : 0;
    }

    /**
     * @notice Checks if a wallet is locked.
     * @param _wallet The target wallet.
     * @return _isLocked `true` if the wallet is locked otherwise `false`.
     */
    function isLocked(address _wallet) external view returns (bool) {
        return _isLocked(_wallet);
    }

    // *************** Guardian functions ************************ //

    /**
     * @notice Lets the owner add a guardian to its wallet.
     * The first guardian is added immediately. All following additions must be confirmed
     * by calling the confirmGuardianAddition() method.
     * @param _wallet The target wallet.
     * @param _guardian The guardian to add.
     */
    function addGuardian(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
        require(!_isOwner(_wallet, _guardian), "SM: guardian cannot be owner");
        require(!isGuardian(_wallet, _guardian), "SM: duplicate guardian");
        // Guardians must either be an EOA or a contract with an owner()
        // method that returns an address with a 25000 gas stipend.
        // Note that this test is not meant to be strict and can be bypassed by custom malicious contracts.
        (bool success,) = _guardian.call{gas: 25000}(abi.encodeWithSignature("owner()"));
        require(success, "SM: must be EOA/Argent wallet");

        bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "addition"));
        GuardianManagerConfig storage config = guardianConfigs[_wallet];
        require(
            config.pending[id] == 0 || block.timestamp > config.pending[id] + securityWindow,
            "SM: duplicate pending addition");
        config.pending[id] = block.timestamp + securityPeriod;
        emit GuardianAdditionRequested(_wallet, _guardian, block.timestamp + securityPeriod);
    }

    /**
     * @notice Confirms the pending addition of a guardian to a wallet.
     * The method must be called during the confirmation window and can be called by anyone to enable orchestration.
     * @param _wallet The target wallet.
     * @param _guardian The guardian.
     */
    function confirmGuardianAddition(address _wallet, address _guardian) external onlyWhenUnlocked(_wallet) {
        bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "addition"));
        GuardianManagerConfig storage config = guardianConfigs[_wallet];
        require(config.pending[id] > 0, "SM: unknown pending addition");
        require(config.pending[id] < block.timestamp, "SM: pending addition not over");
        require(block.timestamp < config.pending[id] + securityWindow, "SM: pending addition expired");
        guardianStorage.addGuardian(_wallet, _guardian);
        emit GuardianAdded(_wallet, _guardian);
        delete config.pending[id];
    }

    /**
     * @notice Lets the owner cancel a pending guardian addition.
     * @param _wallet The target wallet.
     * @param _guardian The guardian.
     */
    function cancelGuardianAddition(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
        bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "addition"));
        GuardianManagerConfig storage config = guardianConfigs[_wallet];
        require(config.pending[id] > 0, "SM: unknown pending addition");
        delete config.pending[id];
        emit GuardianAdditionCancelled(_wallet, _guardian);
    }

    /**
     * @notice Lets the owner revoke a guardian from its wallet.
     * @dev Revokation must be confirmed by calling the confirmGuardianRevokation() method.
     * @param _wallet The target wallet.
     * @param _guardian The guardian to revoke.
     */
    function revokeGuardian(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) {
        require(isGuardian(_wallet, _guardian), "SM: must be existing guardian");
        bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "revokation"));
        GuardianManagerConfig storage config = guardianConfigs[_wallet];
        require(
            config.pending[id] == 0 || block.timestamp > config.pending[id] + securityWindow,
            "SM: duplicate pending revoke"); // TODO need to allow if confirmation window passed
        config.pending[id] = block.timestamp + securityPeriod;
        emit GuardianRevokationRequested(_wallet, _guardian, block.timestamp + securityPeriod);
    }

    /**
     * @notice Confirms the pending revokation of a guardian to a wallet.
     * The method must be called during the confirmation window and can be called by anyone to enable orchestration.
     * @param _wallet The target wallet.
     * @param _guardian The guardian.
     */
    function confirmGuardianRevokation(address _wallet, address _guardian) external {
        bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "revokation"));
        GuardianManagerConfig storage config = guardianConfigs[_wallet];
        require(config.pending[id] > 0, "SM: unknown pending revoke");
        require(config.pending[id] < block.timestamp, "SM: pending revoke not over");
        require(block.timestamp < config.pending[id] + securityWindow, "SM: pending revoke expired");
        guardianStorage.revokeGuardian(_wallet, _guardian);
        emit GuardianRevoked(_wallet, _guardian);
        delete config.pending[id];
    }

    /**
     * @notice Lets the owner cancel a pending guardian revokation.
     * @param _wallet The target wallet.
     * @param _guardian The guardian.
     */
    function cancelGuardianRevokation(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
        bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "revokation"));
        GuardianManagerConfig storage config = guardianConfigs[_wallet];
        require(config.pending[id] > 0, "SM: unknown pending revoke");
        delete config.pending[id];
        emit GuardianRevokationCancelled(_wallet, _guardian);
    }

    /**
     * @notice Checks if an address is a guardian for a wallet.
     * @param _wallet The target wallet.
     * @param _guardian The address to check.
     * @return _isGuardian `true` if the address is a guardian for the wallet otherwise `false`.
     */
    function isGuardian(address _wallet, address _guardian) public view returns (bool _isGuardian) {
        return guardianStorage.isGuardian(_wallet, _guardian);
    }

    /**
    * @notice Checks if an address is a guardian or an account authorised to sign on behalf of a smart-contract guardian.
    * @param _wallet The target wallet.
    * @param _guardian the address to test
    * @return _isGuardian `true` if the address is a guardian for the wallet otherwise `false`.
    */
    function isGuardianOrGuardianSigner(address _wallet, address _guardian) external view returns (bool _isGuardian) {
        (_isGuardian, ) = Utils.isGuardianOrGuardianSigner(guardianStorage.getGuardians(_wallet), _guardian);
    }

    /**
     * @notice Counts the number of active guardians for a wallet.
     * @param _wallet The target wallet.
     * @return _count The number of active guardians for a wallet.
     */
    function guardianCount(address _wallet) external view returns (uint256 _count) {
        return guardianStorage.guardianCount(_wallet);
    }

    /**
     * @notice Get the active guardians for a wallet.
     * @param _wallet The target wallet.
     * @return _guardians the active guardians for a wallet.
     */
    function getGuardians(address _wallet) external view returns (address[] memory _guardians) {
        return guardianStorage.getGuardians(_wallet);
    }

    // *************** Internal Functions ********************* //

    function validateNewOwner(address _wallet, address _newOwner) internal view {
        require(_newOwner != address(0), "SM: new owner cannot be null");
        require(!isGuardian(_wallet, _newOwner), "SM: new owner cannot be guardian");
    }

    function _setLock(address _wallet, uint256 _releaseAfter, bytes4 _locker) internal {
        locks[_wallet] = Lock(SafeCast.toUint64(_releaseAfter), _locker);
    }
}

File 8 of 18 : TransactionManager.sol
// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.3;

import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "./common/Utils.sol";
import "./common/BaseModule.sol";
import "../../lib_0.5/other/ERC20.sol";

/**
 * @title TransactionManager
 * @notice Module to execute transactions in sequence to e.g. transfer tokens (ETH, ERC20, ERC721, ERC1155) or call third-party contracts.
 * @author Julien Niset - <[email protected]>
 */
abstract contract TransactionManager is BaseModule {

    // Static calls
    bytes4 private constant ERC1271_IS_VALID_SIGNATURE = bytes4(keccak256("isValidSignature(bytes32,bytes)"));
    bytes4 private constant ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
    bytes4 private constant ERC1155_RECEIVED = bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"));
    bytes4 private constant ERC1155_BATCH_RECEIVED = bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"));
    bytes4 private constant ERC165_INTERFACE = bytes4(keccak256("supportsInterface(bytes4)"));

    struct Call {
        address to;
        uint256 value;
        bytes data;
    }

    // The time delay for adding a trusted contact
    uint256 internal immutable whitelistPeriod;

    // *************** Events *************************** //

    event AddedToWhitelist(address indexed wallet, address indexed target, uint64 whitelistAfter);
    event RemovedFromWhitelist(address indexed wallet, address indexed target);
    event SessionCreated(address indexed wallet, address sessionKey, uint64 expires);
    event SessionCleared(address indexed wallet, address sessionKey);
    // *************** Constructor ************************ //

    constructor(uint256 _whitelistPeriod) {
        whitelistPeriod = _whitelistPeriod;
    }

    // *************** External functions ************************ //

    /**
     * @notice Makes the target wallet execute a sequence of transactions authorised by the wallet owner.
     * The method reverts if any of the inner transactions reverts.
     * The method reverts if any of the inner transaction is not to a trusted contact or an authorised dapp.
     * @param _wallet The target wallet.
     * @param _transactions The sequence of transactions.
     */
    function multiCall(
        address _wallet,
        Call[] calldata _transactions
    )
        external
        onlySelf()
        onlyWhenUnlocked(_wallet)
        returns (bytes[] memory)
    {
        bytes[] memory results = new bytes[](_transactions.length);
        for(uint i = 0; i < _transactions.length; i++) {
            address spender = Utils.recoverSpender(_transactions[i].to, _transactions[i].data);
            require(
                (_transactions[i].value == 0 || spender == _transactions[i].to) &&
                (isWhitelisted(_wallet, spender) || authoriser.isAuthorised(_wallet, spender, _transactions[i].to, _transactions[i].data)),
                "TM: call not authorised");
            results[i] = invokeWallet(_wallet, _transactions[i].to, _transactions[i].value, _transactions[i].data);
        }
        return results;
    }

    /**
     * @notice Makes the target wallet execute a sequence of transactions authorised by a session key.
     * The method reverts if any of the inner transactions reverts.
     * @param _wallet The target wallet.
     * @param _transactions The sequence of transactions.
     */
    function multiCallWithSession(
        address _wallet,
        Call[] calldata _transactions
    )
        external
        onlySelf()
        onlyWhenUnlocked(_wallet)
        returns (bytes[] memory)
    {
        return multiCallWithApproval(_wallet, _transactions);
    }

    /**
     * @notice Makes the target wallet execute a sequence of transactions approved by a majority of guardians.
     * The method reverts if any of the inner transactions reverts.
     * @param _wallet The target wallet.
     * @param _transactions The sequence of transactions.
     */
    function multiCallWithGuardians(
        address _wallet,
        Call[] calldata _transactions
    )
        external 
        onlySelf()
        onlyWhenUnlocked(_wallet)
        returns (bytes[] memory)
    {
        return multiCallWithApproval(_wallet, _transactions);
    }

    /**
     * @notice Makes the target wallet execute a sequence of transactions approved by a majority of guardians.
     * The method reverts if any of the inner transactions reverts.
     * Upon success a new session is started.
     * @param _wallet The target wallet.
     * @param _transactions The sequence of transactions.
     */
    function multiCallWithGuardiansAndStartSession(
        address _wallet,
        Call[] calldata _transactions,
        address _sessionUser,
        uint64 _duration
    )
        external 
        onlySelf()
        onlyWhenUnlocked(_wallet)
        returns (bytes[] memory)
    {
        startSession(_wallet, _sessionUser, _duration);
        return multiCallWithApproval(_wallet, _transactions);
    }

    /**
    * @notice Clears the active session of a wallet if any.
    * @param _wallet The target wallet.
    */
    function clearSession(address _wallet) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
        emit SessionCleared(_wallet, sessions[_wallet].key);
        _clearSession(_wallet);
    }

    /**
     * @notice Adds an address to the list of trusted contacts.
     * @param _wallet The target wallet.
     * @param _target The address to add.
     */
    function addToWhitelist(address _wallet, address _target) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
        require(_target != _wallet, "TM: Cannot whitelist wallet");
        require(!registry.isRegisteredModule(_target), "TM: Cannot whitelist module");
        require(!isWhitelisted(_wallet, _target), "TM: target already whitelisted");

        uint256 whitelistAfter = block.timestamp + whitelistPeriod;
        setWhitelist(_wallet, _target, whitelistAfter);
        emit AddedToWhitelist(_wallet, _target, uint64(whitelistAfter));
    }

    /**
     * @notice Removes an address from the list of trusted contacts.
     * @param _wallet The target wallet.
     * @param _target The address to remove.
     */
    function removeFromWhitelist(address _wallet, address _target) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
        setWhitelist(_wallet, _target, 0);
        emit RemovedFromWhitelist(_wallet, _target);
    }

    /**
    * @notice Checks if an address is a trusted contact for a wallet.
    * @param _wallet The target wallet.
    * @param _target The address.
    * @return _isWhitelisted true if the address is a trusted contact.
    */
    function isWhitelisted(address _wallet, address _target) public view returns (bool _isWhitelisted) {
        uint whitelistAfter = userWhitelist.getWhitelist(_wallet, _target);
        return whitelistAfter > 0 && whitelistAfter < block.timestamp;
    }
    
    /*
    * @notice Enable the static calls required to make the wallet compatible with the ERC1155TokenReceiver 
    * interface (see https://eips.ethereum.org/EIPS/eip-1155#erc-1155-token-receiver). This method only 
    * needs to be called for wallets deployed in version lower or equal to 2.4.0 as the ERC1155 static calls
    * are not available by default for these versions of BaseWallet
    * @param _wallet The target wallet.
    */
    function enableERC1155TokenReceiver(address _wallet) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) {
        IWallet(_wallet).enableStaticCall(address(this), ERC165_INTERFACE);
        IWallet(_wallet).enableStaticCall(address(this), ERC1155_RECEIVED);
        IWallet(_wallet).enableStaticCall(address(this), ERC1155_BATCH_RECEIVED);
    }

    /**
     * @inheritdoc IModule
     */
    function supportsStaticCall(bytes4 _methodId) external pure override returns (bool _isSupported) {
        return _methodId == ERC1271_IS_VALID_SIGNATURE ||
               _methodId == ERC721_RECEIVED ||
               _methodId == ERC165_INTERFACE ||
               _methodId == ERC1155_RECEIVED ||
               _methodId == ERC1155_BATCH_RECEIVED;
    }

    /** ******************* Callbacks ************************** */

    /**
     * @notice Returns true if this contract implements the interface defined by
     * `interfaceId` (see https://eips.ethereum.org/EIPS/eip-165).
     */
    function supportsInterface(bytes4 _interfaceID) external pure returns (bool) {
        return  _interfaceID == ERC165_INTERFACE || _interfaceID == (ERC1155_RECEIVED ^ ERC1155_BATCH_RECEIVED);          
    }

    /**
    * @notice Implementation of EIP 1271.
    * Should return whether the signature provided is valid for the provided data.
    * @param _msgHash Hash of a message signed on the behalf of address(this)
    * @param _signature Signature byte array associated with _msgHash
    */
    function isValidSignature(bytes32 _msgHash, bytes memory _signature) external view returns (bytes4) {
        require(_signature.length == 65, "TM: invalid signature length");
        address signer = Utils.recoverSigner(_msgHash, _signature, 0);
        require(_isOwner(msg.sender, signer), "TM: Invalid signer");
        return ERC1271_IS_VALID_SIGNATURE;
    }


    fallback() external {
        bytes4 methodId = Utils.functionPrefix(msg.data);
        if(methodId == ERC721_RECEIVED || methodId == ERC1155_RECEIVED || methodId == ERC1155_BATCH_RECEIVED) {
            // solhint-disable-next-line no-inline-assembly
            assembly {                
                calldatacopy(0, 0, 0x04)
                return (0, 0x20)
            }
        }
    }

    // *************** Internal Functions ********************* //

    function enableDefaultStaticCalls(address _wallet) internal {
        // setup the static calls that are available for free for all wallets
        IWallet(_wallet).enableStaticCall(address(this), ERC1271_IS_VALID_SIGNATURE);
        IWallet(_wallet).enableStaticCall(address(this), ERC721_RECEIVED);
    }

    function multiCallWithApproval(address _wallet, Call[] calldata _transactions) internal returns (bytes[] memory) {
        bytes[] memory results = new bytes[](_transactions.length);
        for(uint i = 0; i < _transactions.length; i++) {
            results[i] = invokeWallet(_wallet, _transactions[i].to, _transactions[i].value, _transactions[i].data);
        }
        return results;
    }

    function startSession(address _wallet, address _sessionUser, uint64 _duration) internal {
        require(_sessionUser != address(0), "TM: Invalid session user");
        require(_duration > 0, "TM: Invalid session duration");

        uint64 expiry = SafeCast.toUint64(block.timestamp + _duration);
        sessions[_wallet] = Session(_sessionUser, expiry);
        emit SessionCreated(_wallet, _sessionUser, expiry);
    }

    function setWhitelist(address _wallet, address _target, uint256 _whitelistAfter) internal {
        userWhitelist.setWhitelist(_wallet, _target, _whitelistAfter);
    }
}

File 9 of 18 : BaseModule.sol
// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.3;

import "../../wallet/IWallet.sol";
import "../../infrastructure/IModuleRegistry.sol";
import "../../infrastructure/storage/IGuardianStorage.sol";
import "../../infrastructure/IAuthoriser.sol";
import "../../infrastructure/storage/ITransferStorage.sol";
import "./IModule.sol";
import "../../../lib_0.5/other/ERC20.sol";

/**
 * @title BaseModule
 * @notice Base Module contract that contains methods common to all Modules.
 * @author Julien Niset - <[email protected]>, Olivier VDB - <[email protected]>
 */
abstract contract BaseModule is IModule {

    // Empty calldata
    bytes constant internal EMPTY_BYTES = "";
    // Mock token address for ETH
    address constant internal ETH_TOKEN = address(0);

    // The module registry
    IModuleRegistry internal immutable registry;
    // The guardians storage
    IGuardianStorage internal immutable guardianStorage;
    // The trusted contacts storage
    ITransferStorage internal immutable userWhitelist;
    // The authoriser
    IAuthoriser internal immutable authoriser;

    event ModuleCreated(bytes32 name);

    enum OwnerSignature {
        Anyone,             // Anyone
        Required,           // Owner required
        Optional,           // Owner and/or guardians
        Disallowed,         // Guardians only
        Session             // Session only
    }

    struct Session {
        address key;
        uint64 expires;
    }

    // Maps wallet to session
    mapping (address => Session) internal sessions;

    struct Lock {
        // the lock's release timestamp
        uint64 release;
        // the signature of the method that set the last lock
        bytes4 locker;
    }
    
    // Wallet specific lock storage
    mapping (address => Lock) internal locks;

    /**
     * @notice Throws if the wallet is not locked.
     */
    modifier onlyWhenLocked(address _wallet) {
        require(_isLocked(_wallet), "BM: wallet must be locked");
        _;
    }

    /**
     * @notice Throws if the wallet is locked.
     */
    modifier onlyWhenUnlocked(address _wallet) {
        require(!_isLocked(_wallet), "BM: wallet locked");
        _;
    }

    /**
     * @notice Throws if the sender is not the module itself.
     */
    modifier onlySelf() {
        require(_isSelf(msg.sender), "BM: must be module");
        _;
    }

    /**
     * @notice Throws if the sender is not the module itself or the owner of the target wallet.
     */
    modifier onlyWalletOwnerOrSelf(address _wallet) {
        require(_isSelf(msg.sender) || _isOwner(_wallet, msg.sender), "BM: must be wallet owner/self");
        _;
    }

    /**
     * @dev Throws if the sender is not the target wallet of the call.
     */
    modifier onlyWallet(address _wallet) {
        require(msg.sender == _wallet, "BM: caller must be wallet");
        _;
    }

    constructor(
        IModuleRegistry _registry,
        IGuardianStorage _guardianStorage,
        ITransferStorage _userWhitelist,
        IAuthoriser _authoriser,
        bytes32 _name
    ) {
        registry = _registry;
        guardianStorage = _guardianStorage;
        userWhitelist = _userWhitelist;
        authoriser = _authoriser;
        emit ModuleCreated(_name);
    }

    /**
     * @notice Moves tokens that have been sent to the module by mistake.
     * @param _token The target token.
     */
    function recoverToken(address _token) external {
        uint total = ERC20(_token).balanceOf(address(this));
        ERC20(_token).transfer(address(registry), total);
    }

    function _clearSession(address _wallet) internal {
        delete sessions[_wallet];
    }
    
    /**
     * @notice Helper method to check if an address is the owner of a target wallet.
     * @param _wallet The target wallet.
     * @param _addr The address.
     */
    function _isOwner(address _wallet, address _addr) internal view returns (bool) {
        return IWallet(_wallet).owner() == _addr;
    }

    /**
     * @notice Helper method to check if a wallet is locked.
     * @param _wallet The target wallet.
     */
    function _isLocked(address _wallet) internal view returns (bool) {
        return locks[_wallet].release > uint64(block.timestamp);
    }

    /**
     * @notice Helper method to check if an address is the module itself.
     * @param _addr The target address.
     */
    function _isSelf(address _addr) internal view returns (bool) {
        return _addr == address(this);
    }

    /**
     * @notice Helper method to invoke a wallet.
     * @param _wallet The target wallet.
     * @param _to The target address for the transaction.
     * @param _value The value of the transaction.
     * @param _data The data of the transaction.
     */
    function invokeWallet(address _wallet, address _to, uint256 _value, bytes memory _data) internal returns (bytes memory _res) {
        bool success;
        (success, _res) = _wallet.call(abi.encodeWithSignature("invoke(address,uint256,bytes)", _to, _value, _data));
        if (success && _res.length > 0) { //_res is empty if _wallet is an "old" BaseWallet that can't return output values
            (_res) = abi.decode(_res, (bytes));
        } else if (_res.length > 0) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        } else if (!success) {
            revert("BM: wallet invoke reverted");
        }
    }
}

File 10 of 18 : IModule.sol
// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.3;

/**
 * @title IModule
 * @notice Interface for a Module.
 * @author Julien Niset - <[email protected]>, Olivier VDB - <[email protected]>
 */
interface IModule {

    /**	
     * @notice Adds a module to a wallet. Cannot execute when wallet is locked (or under recovery)	
     * @param _wallet The target wallet.	
     * @param _module The modules to authorise.	
     */	
    function addModule(address _wallet, address _module) external;

    /**
     * @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);
}

File 11 of 18 : SimpleOracle.sol
// 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'
            )))));
    }
}

File 12 of 18 : Utils.sol
// Copyright (C) 2020  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// SPDX-License-Identifier: GPL-3.0-only
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;
        }
    }
}

File 13 of 18 : IWallet.sol
// Copyright (C) 2018  Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// 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;
}

File 14 of 18 : ERC20.sol
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);
}

File 15 of 18 : Math.sol
// 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);
    }
}

File 16 of 18 : SafeCast.sol
// 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);
    }
}

File 17 of 18 : IUniswapV2Pair.sol
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;
}

File 18 of 18 : IUniswapV2Router01.sol
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);
}

Settings
{
  "remappings": [],
  "optimizer": {
    "enabled": true,
    "runs": 300
  },
  "evmVersion": "istanbul",
  "libraries": {
    "": {}
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IModuleRegistry","name":"_registry","type":"address"},{"internalType":"contract IGuardianStorage","name":"_guardianStorage","type":"address"},{"internalType":"contract ITransferStorage","name":"_userWhitelist","type":"address"},{"internalType":"contract IAuthoriser","name":"_authoriser","type":"address"},{"internalType":"address","name":"_uniswapRouter","type":"address"},{"internalType":"uint256","name":"_securityPeriod","type":"uint256"},{"internalType":"uint256","name":"_securityWindow","type":"uint256"},{"internalType":"uint256","name":"_recoveryPeriod","type":"uint256"},{"internalType":"uint256","name":"_lockPeriod","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint64","name":"whitelistAfter","type":"uint64"}],"name":"AddedToWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"guardian","type":"address"}],"name":"GuardianAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"guardian","type":"address"}],"name":"GuardianAdditionCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"guardian","type":"address"},{"indexed":false,"internalType":"uint256","name":"executeAfter","type":"uint256"}],"name":"GuardianAdditionRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"guardian","type":"address"}],"name":"GuardianRevokationCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"guardian","type":"address"},{"indexed":false,"internalType":"uint256","name":"executeAfter","type":"uint256"}],"name":"GuardianRevokationRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"guardian","type":"address"}],"name":"GuardianRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint64","name":"releaseAfter","type":"uint64"}],"name":"Locked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"name","type":"bytes32"}],"name":"ModuleCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransfered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"_recovery","type":"address"}],"name":"RecoveryCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"_recovery","type":"address"},{"indexed":false,"internalType":"uint64","name":"executeAfter","type":"uint64"}],"name":"RecoveryExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"_recovery","type":"address"}],"name":"RecoveryFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"refundAddress","type":"address"},{"indexed":false,"internalType":"address","name":"refundToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"refundAmount","type":"uint256"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"target","type":"address"}],"name":"RemovedFromWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"sessionKey","type":"address"}],"name":"SessionCleared","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"address","name":"sessionKey","type":"address"},{"indexed":false,"internalType":"uint64","name":"expires","type":"uint64"}],"name":"SessionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"bytes","name":"returnData","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"signedHash","type":"bytes32"}],"name":"TransactionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"}],"name":"Unlocked","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_guardian","type":"address"}],"name":"addGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_module","type":"address"}],"name":"addModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_target","type":"address"}],"name":"addToWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_guardian","type":"address"}],"name":"cancelGuardianAddition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_guardian","type":"address"}],"name":"cancelGuardianRevokation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"cancelRecovery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"clearSession","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_guardian","type":"address"}],"name":"confirmGuardianAddition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_guardian","type":"address"}],"name":"confirmGuardianRevokation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"enableERC1155TokenReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"},{"internalType":"uint256","name":"_nonce","type":"uint256"},{"internalType":"bytes","name":"_signatures","type":"bytes"},{"internalType":"uint256","name":"_gasPrice","type":"uint256"},{"internalType":"uint256","name":"_gasLimit","type":"uint256"},{"internalType":"address","name":"_refundToken","type":"address"},{"internalType":"address","name":"_refundAddress","type":"address"}],"name":"execute","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_recovery","type":"address"}],"name":"executeRecovery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"finalizeRecovery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"getGuardians","outputs":[{"internalType":"address[]","name":"_guardians","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"getLock","outputs":[{"internalType":"uint64","name":"_releaseAfter","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"getRecovery","outputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"uint64","name":"_executeAfter","type":"uint64"},{"internalType":"uint32","name":"_guardianCount","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"getRequiredSignatures","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"enum BaseModule.OwnerSignature","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"getSession","outputs":[{"internalType":"address","name":"key","type":"address"},{"internalType":"uint64","name":"expires","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"guardianCount","outputs":[{"internalType":"uint256","name":"_count","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"bytes32","name":"_signHash","type":"bytes32"}],"name":"isExecutedTx","outputs":[{"internalType":"bool","name":"executed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_guardian","type":"address"}],"name":"isGuardian","outputs":[{"internalType":"bool","name":"_isGuardian","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_guardian","type":"address"}],"name":"isGuardianOrGuardianSigner","outputs":[{"internalType":"bool","name":"_isGuardian","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"isLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_msgHash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_target","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"_isWhitelisted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct TransactionManager.Call[]","name":"_transactions","type":"tuple[]"}],"name":"multiCall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct TransactionManager.Call[]","name":"_transactions","type":"tuple[]"}],"name":"multiCallWithGuardians","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct TransactionManager.Call[]","name":"_transactions","type":"tuple[]"},{"internalType":"address","name":"_sessionUser","type":"address"},{"internalType":"uint64","name":"_duration","type":"uint64"}],"name":"multiCallWithGuardiansAndStartSession","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct TransactionManager.Call[]","name":"_transactions","type":"tuple[]"}],"name":"multiCallWithSession","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"recoverToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_target","type":"address"}],"name":"removeFromWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_guardian","type":"address"}],"name":"revokeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_methodId","type":"bytes4"}],"name":"supportsStaticCall","outputs":[{"internalType":"bool","name":"_isSupported","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101e06040523480156200001257600080fd5b50604051620064fa380380620064fa833981016040819052620000359162000323565b838285858489808f8f8f8f6b417267656e744d6f64756c6560a01b846001600160a01b03166080816001600160a01b031660601b81525050836001600160a01b031660a0816001600160a01b031660601b81525050826001600160a01b031660c0816001600160a01b031660601b81525050816001600160a01b031660e0816001600160a01b031660601b815250507f3019c8fc80239e3dff8f781212ae2004839c2cb61d6c70acd279ac65392145df81604051620000f691815260200190565b60405180910390a15050505050806001600160a01b031663ad5c46486040518163ffffffff1660e01b815260040160206040518083038186803b1580156200013d57600080fd5b505afa15801562000152573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001789190620002fd565b6001600160a01b0316610100816001600160a01b031660601b81525050806001600160a01b031663c45a01556040518163ffffffff1660e01b815260040160206040518083038186803b158015620001cf57600080fd5b505afa158015620001e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200020a9190620002fd565b60601b6001600160601b03191661012052505083811015620002735760405162461bcd60e51b815260206004820152601860248201527f534d3a20696e736563757265206c6f636b20706572696f64000000000000000060448201526064015b60405180910390fd5b6200027f8284620003c9565b841015620002d05760405162461bcd60e51b815260206004820152601d60248201527f534d3a20696e73656375726520736563757269747920706572696f647300000060448201526064016200026a565b61014093909352610160929092526101a091909152610180526101c0525062000407975050505050505050565b6000602082840312156200030f578081fd5b81516200031c81620003ee565b9392505050565b60008060008060008060008060006101208a8c03121562000342578485fd5b89516200034f81620003ee565b60208b01519099506200036281620003ee565b60408b01519098506200037581620003ee565b60608b01519097506200038881620003ee565b60808b01519096506200039b81620003ee565b8095505060a08a0151935060c08a0151925060e08a015191506101008a015190509295985092959850929598565b60008219821115620003e957634e487b7160e01b81526011600452602481fd5b500190565b6001600160a01b03811681146200040457600080fd5b50565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c6101405161016051610180516101a0516101c051615fb962000541600039600061196d0152600081816109f101528181611593015281816120680152612fc3015260008181610a6a01528181610ad20152818161303c01526130a4015260008181612b61015281816139ea0152613a4c015260006129fc015260006153cd0152600081816151c3015281816151f801526152b00152600081816125430152614b57015260008181612c11015281816142650152614c0e01526000818161162d015281816116f40152818161210201528181612a66015281816132360152818161382c015281816138da0152818161418301526148e201526000818161184801528181611c3a015261226d0152615fb96000f3fe608060405234801561001057600080fd5b50600436106102485760003560e01c80636ff6ec7c1161013b578063ba821088116100b8578063eac01e471161007c578063eac01e47146106cb578063f143ddba14610517578063f18858ab146106de578063f435f5a7146106fe578063f8d3277d1461071157610248565b8063ba8210881461066c578063c68452101461067f578063c90db44714610692578063d4ee9734146106a5578063e0724b6e146106b857610248565b8063a3f4df7e116100ff578063a3f4df7e1461060a578063a5efb23514610620578063a6eb069014610633578063b0ba4da014610646578063b6b352721461065957610248565b80636ff6ec7c1461051757806370135f521461052a5780638c8e13b91461053d5780639769c3fe146105775780639be65a60146105f757610248565b80633b73d67f116101c957806359b4958a1161018d57806359b4958a146104765780635a1db8c41461048957806360c0fdc01461049c5780636b9db4e6146104d95780636d4354211461050457610248565b80633b73d67f146104095780634a4fbeec1461042a5780634b3ef0541461043d5780635040fb7614610450578063575182431461046357610248565b806325b509341161021057806325b50934146103865780632960739b146103995780632d0335ab146103ac5780632f6c493c146103e3578063315a7af3146103f657610248565b806301ffc9a7146102ea5780631626ba7e1461031257806319ab453c1461033e5780631d97d8cc146103535780632437b75c14610366575b600061028a6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061072492505050565b90506001600160e01b03198116630a85bd0160e11b14806102bb57506001600160e01b0319811663f23a6e6160e01b145b806102d657506001600160e01b0319811663bc197c8160e01b145b156102e75760046000803760206000f35b50005b6102fd6102f83660046158e7565b610785565b60405190151581526020015b60405180910390f35b610325610320366004615864565b6107be565b6040516001600160e01b03199091168152602001610309565b61035161034c36600461551a565b610897565b005b610351610361366004615552565b6108fd565b6103796103743660046155dc565b610b10565b6040516103099190615c51565b6102fd6103943660046158e7565b610b9e565b6103516103a7366004615552565b610c22565b6103d56103ba36600461551a565b6001600160a01b031660009081526002602052604090205490565b604051908152602001610309565b6103516103f136600461551a565b610d66565b61035161040436600461551a565b610ed7565b61041c61041736600461568a565b6110ab565b604051610309929190615d36565b6102fd61043836600461551a565b611478565b61035161044b366004615552565b611483565b6103d561045e36600461551a565b6116d2565b610351610471366004615552565b611770565b61035161048436600461551a565b6119e8565b610351610497366004615552565b611bc4565b6102fd6104aa36600461565f565b6001600160a01b0391909116600090815260026020908152604080832093835260019093019052205460ff1690565b6104ec6104e736600461551a565b611d68565b6040516001600160401b039091168152602001610309565b610351610512366004615552565b611da3565b61037961052536600461558a565b611eb0565b610351610538366004615552565b611f31565b61055061054b36600461551a565b6121a8565b604080516001600160a01b0390931683526001600160401b03909116602083015201610309565b6105c461058536600461551a565b6001600160a01b0390811660009081526003602052604090205490811691600160a01b82046001600160401b031691600160e01b900463ffffffff1690565b604080516001600160a01b0390941684526001600160401b03909216602084015263ffffffff1690820152606001610309565b61035161060536600461551a565b6121dc565b6103d56b417267656e744d6f64756c6560a01b81565b61037961062e36600461558a565b612301565b610351610641366004615552565b6127eb565b610351610654366004615552565b61292f565b6102fd610667366004615552565b612be5565b61035161067a36600461551a565b612ca4565b61035161068d366004615552565b612d77565b6103516106a036600461551a565b6130e3565b6102fd6106b3366004615552565b61320c565b6102fd6106c63660046156cf565b6132b9565b6102fd6106d9366004615552565b613806565b6106f16106ec36600461551a565b6138b8565b6040516103099190615c04565b61035161070c36600461551a565b61395a565b61035161071f366004615552565b613a91565b600060048251101561077d5760405162461bcd60e51b815260206004820152601d60248201527f5574696c733a20496e76616c69642066756e6374696f6e50726566697800000060448201526064015b60405180910390fd5b506020015190565b60006001600160e01b031982166301ffc9a760e01b14806107b657506001600160e01b03198216630271189760e51b145b90505b919050565b600081516041146108115760405162461bcd60e51b815260206004820152601c60248201527f544d3a20696e76616c6964207369676e6174757265206c656e677468000000006044820152606401610774565b600061081f84846000613b3a565b905061082b3382613c6f565b61086c5760405162461bcd60e51b81526020600482015260126024820152712a269d1024b73b30b634b21039b4b3b732b960711b6044820152606401610774565b7f1626ba7e356f5979dd355a3d2bfb43e80420a480c3b854edce286a82d74968699150505b92915050565b80336001600160a01b038216146108f05760405162461bcd60e51b815260206004820152601960248201527f424d3a2063616c6c6572206d7573742062652077616c6c6574000000000000006044820152606401610774565b6108f982613cfd565b5050565b813033148061091157506109118133613c6f565b61092d5760405162461bcd60e51b815260040161077490615cd4565b610937838361320c565b6109835760405162461bcd60e51b815260206004820152601d60248201527f534d3a206d757374206265206578697374696e6720677561726469616e0000006044820152606401610774565b60008383604051602001610998929190615a0a565b60408051601f1981840301815291815281516020928301206001600160a01b0387166000908152600484528281208282529384905291909120549092501580610a195750600082815260208290526040902054610a16907f000000000000000000000000000000000000000000000000000000000000000090615e40565b42115b610a655760405162461bcd60e51b815260206004820152601c60248201527f534d3a206475706c69636174652070656e64696e67207265766f6b65000000006044820152606401610774565b610a8f7f000000000000000000000000000000000000000000000000000000000000000042615e40565b6000838152602083905260409020556001600160a01b038085169086167f9746f6868f544595794833da53250bd19e72334733336cfd5dd6fbc5f6a6ac42610af77f000000000000000000000000000000000000000000000000000000000000000042615e40565b6040519081526020015b60405180910390a35050505050565b6060303314610b565760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b85610b6081613e00565b15610b7d5760405162461bcd60e51b815260040161077490615d0b565b610b88878585613e29565b610b93878787613f96565b979650505050505050565b60006001600160e01b03198216630b135d3f60e11b1480610bcf57506001600160e01b03198216630a85bd0160e11b145b80610bea57506001600160e01b031982166301ffc9a760e01b145b80610c0557506001600160e01b0319821663f23a6e6160e01b145b806107b65750506001600160e01b03191663bc197c8160e01b1490565b8130331480610c365750610c368133613c6f565b610c525760405162461bcd60e51b815260040161077490615cd4565b82610c5c81613e00565b15610c795760405162461bcd60e51b815260040161077490615d0b565b60008484604051602001610c8e929190615a0a565b60408051601f1981840301815291815281516020928301206001600160a01b038816600090815260048452828120828252938490529190912054909250610d175760405162461bcd60e51b815260206004820152601a60248201527f534d3a20756e6b6e6f776e2070656e64696e67207265766f6b650000000000006044820152606401610774565b600082815260208290526040808220829055516001600160a01b0380881692908916917fc0b205956d5e27c296695de329b5a014584a4f51824b1725a0eefc1174d6dbd59190a3505050505050565b8030331480610d7a5750610d7a813361320c565b610dc65760405162461bcd60e51b815260206004820152601960248201527f534d3a206d75737420626520677561726469616e2f73656c66000000000000006044820152606401610774565b81610dd081613e00565b610e1c5760405162461bcd60e51b815260206004820152601960248201527f424d3a2077616c6c6574206d757374206265206c6f636b6564000000000000006044820152606401610774565b6001600160a01b038316600090815260016020526040902054600160401b900460e01b6001600160e01b03191663f435f5a760e01b14610e925760405162461bcd60e51b8152602060048201526011602482015270534d3a2063616e6e6f7420756e6c6f636b60781b6044820152606401610774565b610e9e836000806140e2565b6040516001600160a01b038416907f7e6adfec7e3f286831a0200a754127c171a2da564078722cb97704741bbdb0ea90600090a2505050565b6001600160a01b0381166000908152600360205260409020548190600160a01b90046001600160401b0316610f485760405162461bcd60e51b8152602060048201526017602482015276534d3a206e6f206f6e676f696e67207265636f7665727960481b6044820152606401610774565b6001600160a01b038216600090815260036020526040902080546001600160401b03600160a01b90910481164290911611610fc55760405162461bcd60e51b815260206004820152601b60248201527f534d3a206f6e676f696e67207265636f7665727920706572696f6400000000006044820152606401610774565b80546001600160a01b03848116600090815260036020908152604080832083905590829052902080546001600160e01b0319169055166040516313af403560e01b81526001600160a01b0382811660048301528516906313af403590602401600060405180830381600087803b15801561103e57600080fd5b505af1158015611052573d6000803e3d6000fd5b505050506110658460008060e01b6140e2565b806001600160a01b0316846001600160a01b03167fd8667de85dae2d56d76e700d16de53d21ac2ce4d5549cb0bf51c55fdc37f0bc160405160405180910390a350505050565b60008060006110ef85858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061072492505050565b90506001600160e01b0319811663a5efb23560e01b148061112057506001600160e01b03198116635751824360e01b145b8061113b57506001600160e01b0319811663f8d3277d60e01b145b8061115657506001600160e01b03198116632cda4ac560e11b145b8061117157506001600160e01b03198116631750421160e31b145b8061118c57506001600160e01b031981166316876e3160e21b145b806111a757506001600160e01b03198116630c68452160e41b145b806111c257506001600160e01b03198116630765f63360e21b145b806111dd57506001600160e01b03198116630a6eb06960e41b145b806111f857506001600160e01b03198116632960739b60e01b145b1561120a576001809250925050611470565b6001600160e01b03198116631bfdbb1f60e21b141561123157600160049250925050611470565b6001600160e01b03198116630585d26d60e51b14156112b45760006112558761415d565b9050600081116112a75760405162461bcd60e51b815260206004820152601e60248201527f414d3a206e6f20677561726469616e7320736574206f6e2077616c6c657400006044820152606401610774565b9250600391506114709050565b6001600160e01b0319811663c90db44760e01b141561131d576001600160a01b03861660009081526003602052604081205461130f9061130290600160e01b900463ffffffff166001615e58565b63ffffffff166002614200565b935060029250611470915050565b6001600160e01b031981166378a1eedd60e11b148061134c57506001600160e01b0319811663090dedd760e21b145b8061136757506001600160e01b03198116636d43542160e01b145b156113955760006113778761415d565b90506000611386826001615e40565b94506001935061147092505050565b6001600160e01b0319811663315a7af360e01b14806113c457506001600160e01b03198116633809afa960e11b145b806113df57506001600160e01b031981166312cfbc1560e21b145b156113f1576000809250925050611470565b6001600160e01b0319811663f435f5a760e01b148061142057506001600160e01b03198116630bdb124f60e21b145b1561143357600160039250925050611470565b60405162461bcd60e51b815260206004820152601260248201527114d34e881d5b9adb9bdddb881b595d1a1bd960721b6044820152606401610774565b935093915050565b60006107b682613e00565b60008282604051602001611498929190615a0a565b60408051601f1981840301815291815281516020928301206001600160a01b0386166000908152600484528281208282529384905291909120549092506115215760405162461bcd60e51b815260206004820152601a60248201527f534d3a20756e6b6e6f776e2070656e64696e67207265766f6b650000000000006044820152606401610774565b600082815260208290526040902054421161157e5760405162461bcd60e51b815260206004820152601b60248201527f534d3a2070656e64696e67207265766f6b65206e6f74206f76657200000000006044820152606401610774565b6000828152602082905260409020546115b8907f000000000000000000000000000000000000000000000000000000000000000090615e40565b42106116065760405162461bcd60e51b815260206004820152601a60248201527f534d3a2070656e64696e67207265766f6b6520657870697265640000000000006044820152606401610774565b604051630765f63360e21b81526001600160a01b03858116600483015284811660248301527f00000000000000000000000000000000000000000000000000000000000000001690631d97d8cc90604401600060405180830381600087803b15801561167157600080fd5b505af1158015611685573d6000803e3d6000fd5b50506040516001600160a01b038087169350871691507f548f10dcba266544123ad8cf8284f25c4baa659cba25dbdf16a06ea11235de9b90600090a3600091825260205260408120555050565b6040516328207dbb60e11b81526001600160a01b0382811660048301526000917f000000000000000000000000000000000000000000000000000000000000000090911690635040fb769060240160206040518083038186803b15801561173857600080fd5b505afa15801561174c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b691906159c6565b813033148061178457506117848133613c6f565b6117a05760405162461bcd60e51b815260040161077490615cd4565b826117aa81613e00565b156117c75760405162461bcd60e51b815260040161077490615d0b565b836001600160a01b0316836001600160a01b031614156118295760405162461bcd60e51b815260206004820152601b60248201527f544d3a2043616e6e6f742077686974656c6973742077616c6c657400000000006044820152606401610774565b604051630bcd4ebb60e01b81526001600160a01b0384811660048301527f00000000000000000000000000000000000000000000000000000000000000001690630bcd4ebb9060240160206040518083038186803b15801561188a57600080fd5b505afa15801561189e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c29190615844565b1561190f5760405162461bcd60e51b815260206004820152601b60248201527f544d3a2043616e6e6f742077686974656c697374206d6f64756c6500000000006044820152606401610774565b6119198484612be5565b156119665760405162461bcd60e51b815260206004820152601e60248201527f544d3a2074617267657420616c72656164792077686974656c697374656400006044820152606401610774565b60006119927f000000000000000000000000000000000000000000000000000000000000000042615e40565b905061199f858583614237565b6040516001600160401b03821681526001600160a01b0380861691908716907f1f57f9641d3e8733ed672fef5ac85464bd7215ef2f21e83428e8408248b13dcd90602001610b01565b80303314806119fc57506119fc8133613c6f565b611a185760405162461bcd60e51b815260040161077490615cd4565b81611a2281613e00565b15611a3f5760405162461bcd60e51b815260040161077490615d0b565b6040516309ed185960e11b81526001600160a01b038416906313da30b290611a8d9030907f01ffc9a7a5cef8baa21ed3c5c0d7e23accb804b619e9333b597f47a0d84076e290600401615bb9565b600060405180830381600087803b158015611aa757600080fd5b505af1158015611abb573d6000803e3d6000fd5b50506040516309ed185960e11b81526001600160a01b03861692506313da30b29150611b0d9030907ff23a6e612e1ff4830e658fe43f4e3cb4a5f8170bd5d9e69fb5d7a7fa9e4fdf9790600401615bb9565b600060405180830381600087803b158015611b2757600080fd5b505af1158015611b3b573d6000803e3d6000fd5b50506040516309ed185960e11b81526001600160a01b03861692506313da30b29150611b8d9030907fbc197c819b3e337a6f9652dd10becd7eef83032af3b9d958d3d42f669414662190600401615bb9565b600060405180830381600087803b158015611ba757600080fd5b505af1158015611bbb573d6000803e3d6000fd5b50505050505050565b8130331480611bd85750611bd88133613c6f565b611bf45760405162461bcd60e51b815260040161077490615cd4565b82611bfe81613e00565b15611c1b5760405162461bcd60e51b815260040161077490615d0b565b604051630bcd4ebb60e01b81526001600160a01b0384811660048301527f00000000000000000000000000000000000000000000000000000000000000001690630bcd4ebb9060240160206040518083038186803b158015611c7c57600080fd5b505afa158015611c90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb49190615844565b611d005760405162461bcd60e51b815260206004820152601c60248201527f414d3a206d6f64756c65206973206e6f742072656769737465726564000000006044820152606401610774565b604051631f17732d60e01b81526001600160a01b03848116600483015260016024830152851690631f17732d90604401600060405180830381600087803b158015611d4a57600080fd5b505af1158015611d5e573d6000803e3d6000fd5b5050505050505050565b6000611d7382613e00565b611d7e5760006107b6565b506001600160a01b03166000908152600160205260409020546001600160401b031690565b303314611de75760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b81611df181613e00565b15611e0e5760405162461bcd60e51b815260040161077490615d0b565b611e188383614294565b6040516313af403560e01b81526001600160a01b0383811660048301528416906313af403590602401600060405180830381600087803b158015611e5b57600080fd5b505af1158015611e6f573d6000803e3d6000fd5b50506040516001600160a01b038086169350861691507f0d18b5fd22306e373229b9439188228edca81207d1667f604daf6cef8aa3ee6790600090a3505050565b6060303314611ef65760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b83611f0081613e00565b15611f1d5760405162461bcd60e51b815260040161077490615d0b565b611f28858585613f96565b95945050505050565b81611f3b81613e00565b15611f585760405162461bcd60e51b815260040161077490615d0b565b60008383604051602001611f6d929190615a3f565b60408051601f1981840301815291815281516020928301206001600160a01b038716600090815260048452828120828252938490529190912054909250611ff65760405162461bcd60e51b815260206004820152601c60248201527f534d3a20756e6b6e6f776e2070656e64696e67206164646974696f6e000000006044820152606401610774565b60008281526020829052604090205442116120535760405162461bcd60e51b815260206004820152601d60248201527f534d3a2070656e64696e67206164646974696f6e206e6f74206f7665720000006044820152606401610774565b60008281526020829052604090205461208d907f000000000000000000000000000000000000000000000000000000000000000090615e40565b42106120db5760405162461bcd60e51b815260206004820152601c60248201527f534d3a2070656e64696e67206164646974696f6e2065787069726564000000006044820152606401610774565b604051630c68452160e41b81526001600160a01b03868116600483015285811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063c684521090604401600060405180830381600087803b15801561214657600080fd5b505af115801561215a573d6000803e3d6000fd5b50506040516001600160a01b038088169350881691507fbc3292102fa77e083913064b282926717cdfaede4d35f553d66366c0a3da755a90600090a360009182526020526040812055505050565b6001600160a01b0381811660009081526020819052604090205490811690600160a01b90046001600160401b03165b915091565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a082319060240160206040518083038186803b15801561221e57600080fd5b505afa158015612232573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225691906159c6565b60405163a9059cbb60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018390529192509083169063a9059cbb90604401602060405180830381600087803b1580156122c457600080fd5b505af11580156122d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122fc9190615844565b505050565b60603033146123475760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b8361235181613e00565b1561236e5760405162461bcd60e51b815260040161077490615d0b565b6000836001600160401b0381111561239657634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156123c957816020015b60608152602001906001900390816123b45790505b50905060005b848110156127e15760006124968787848181106123fc57634e487b7160e01b600052603260045260246000fd5b905060200281019061240e9190615dac565b61241c90602081019061551a565b88888581811061243c57634e487b7160e01b600052603260045260246000fd5b905060200281019061244e9190615dac565b61245c906040810190615d68565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061434192505050565b90508686838181106124b857634e487b7160e01b600052603260045260246000fd5b90506020028101906124ca9190615dac565b60200135158061252a57508686838181106124f557634e487b7160e01b600052603260045260246000fd5b90506020028101906125079190615dac565b61251590602081019061551a565b6001600160a01b0316816001600160a01b0316145b8015612662575061253b8882612be5565b8061266257507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635cfdc4d089838a8a8781811061259257634e487b7160e01b600052603260045260246000fd5b90506020028101906125a49190615dac565b6125b290602081019061551a565b8b8b888181106125d257634e487b7160e01b600052603260045260246000fd5b90506020028101906125e49190615dac565b6125f2906040810190615d68565b6040518663ffffffff1660e01b8152600401612612959493929190615b28565b60206040518083038186803b15801561262a57600080fd5b505afa15801561263e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126629190615844565b6126ae5760405162461bcd60e51b815260206004820152601760248201527f544d3a2063616c6c206e6f7420617574686f72697365640000000000000000006044820152606401610774565b6127a2888888858181106126d257634e487b7160e01b600052603260045260246000fd5b90506020028101906126e49190615dac565b6126f290602081019061551a565b89898681811061271257634e487b7160e01b600052603260045260246000fd5b90506020028101906127249190615dac565b602001358a8a8781811061274857634e487b7160e01b600052603260045260246000fd5b905060200281019061275a9190615dac565b612768906040810190615d68565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061442b92505050565b8383815181106127c257634e487b7160e01b600052603260045260246000fd5b60200260200101819052505080806127d990615efa565b9150506123cf565b5095945050505050565b81303314806127ff57506127ff8133613c6f565b61281b5760405162461bcd60e51b815260040161077490615cd4565b8261282581613e00565b156128425760405162461bcd60e51b815260040161077490615d0b565b60008484604051602001612857929190615a3f565b60408051601f1981840301815291815281516020928301206001600160a01b0388166000908152600484528281208282529384905291909120549092506128e05760405162461bcd60e51b815260206004820152601c60248201527f534d3a20756e6b6e6f776e2070656e64696e67206164646974696f6e000000006044820152606401610774565b600082815260208290526040808220829055516001600160a01b0380881692908916917faa13b27c23e9e3f3d5f3861a53b7a2931e019170a6a19ed64942e26a1dd5987a9190a3505050505050565b3033146129735760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b6001600160a01b0382166000908152600360205260409020548290600160a01b90046001600160401b0316156129eb5760405162461bcd60e51b815260206004820152601460248201527f534d3a206f6e676f696e67207265636f766572790000000000000000000000006044820152606401610774565b6129f58383614294565b6000612a217f000000000000000000000000000000000000000000000000000000000000000042615e40565b604080516060810182526001600160a01b0380871682526001600160401b038416602083015282516328207dbb60e11b815288821660048201529394509092918301917f000000000000000000000000000000000000000000000000000000000000000090911690635040fb769060240160206040518083038186803b158015612aaa57600080fd5b505afa158015612abe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae291906159c6565b63ffffffff9081169091526001600160a01b0380871660009081526003602090815260409182902085518154928701519690930151909416600160e01b026001600160e01b036001600160401b03909616600160a01b026001600160e01b031990921692909316919091171792909216919091179055612b9384612b867f000000000000000000000000000000000000000000000000000000000000000042615e40565b630585d26d60e51b6140e2565b6040516001600160401b03821681526001600160a01b0380851691908616907f5f59bfd9baba55ae30bb440923cbbe30987d50e12a4e9134ffac3fd9afc3526d9060200160405180910390a350505050565b6040516309fa507560e11b81526001600160a01b038381166004830152828116602483015260009182917f000000000000000000000000000000000000000000000000000000000000000016906313f4a0ea9060440160206040518083038186803b158015612c5357600080fd5b505afa158015612c67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c8b91906159c6565b9050600081118015612c9c57504281105b949350505050565b8030331480612cb85750612cb88133613c6f565b612cd45760405162461bcd60e51b815260040161077490615cd4565b81612cde81613e00565b15612cfb5760405162461bcd60e51b815260040161077490615d0b565b6001600160a01b038381166000818152602081815260409182902054915191909316815290917feb290a597820eccc6b8b31f942bd97c633d5138f4d849751f770f3cb3900e57a910160405180910390a26122fc836001600160a01b0316600090815260208190526040902080546001600160e01b0319169055565b8130331480612d8b5750612d8b8133613c6f565b612da75760405162461bcd60e51b815260040161077490615cd4565b82612db181613e00565b15612dce5760405162461bcd60e51b815260040161077490615d0b565b612dd88484613c6f565b15612e255760405162461bcd60e51b815260206004820152601c60248201527f534d3a20677561726469616e2063616e6e6f74206265206f776e6572000000006044820152606401610774565b612e2f848461320c565b15612e7c5760405162461bcd60e51b815260206004820152601660248201527f534d3a206475706c696361746520677561726469616e000000000000000000006044820152606401610774565b60408051600481526024810182526020810180516001600160e01b0316638da5cb5b60e01b17905290516000916001600160a01b038616916161a891612ec191615b0c565b60006040518083038160008787f1925050503d8060008114612eff576040519150601f19603f3d011682016040523d82523d6000602084013e612f04565b606091505b5050905080612f555760405162461bcd60e51b815260206004820152601d60248201527f534d3a206d75737420626520454f412f417267656e742077616c6c65740000006044820152606401610774565b60008585604051602001612f6a929190615a3f565b60408051601f1981840301815291815281516020928301206001600160a01b0389166000908152600484528281208282529384905291909120549092501580612feb5750600082815260208290526040902054612fe8907f000000000000000000000000000000000000000000000000000000000000000090615e40565b42115b6130375760405162461bcd60e51b815260206004820152601e60248201527f534d3a206475706c69636174652070656e64696e67206164646974696f6e00006044820152606401610774565b6130617f000000000000000000000000000000000000000000000000000000000000000042615e40565b6000838152602083905260409020556001600160a01b038087169088167fe4166e4bc55a182bd13d933553241bb3441b91d15fbc74c5c752f96965563bde6130c97f000000000000000000000000000000000000000000000000000000000000000042615e40565b60405190815260200160405180910390a350505050505050565b3033146131275760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b6001600160a01b0381166000908152600360205260409020548190600160a01b90046001600160401b03166131985760405162461bcd60e51b8152602060048201526017602482015276534d3a206e6f206f6e676f696e67207265636f7665727960481b6044820152606401610774565b6001600160a01b038083166000908152600360205260408120805490829055909116906131c7908490806140e2565b806001600160a01b0316836001600160a01b03167fc45926607303da71dbeffd2ed5c6b00f581982586b697655d19ae4c4d558f25960405160405180910390a3505050565b60405163353ba5cd60e21b81526001600160a01b03838116600483015282811660248301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063d4ee97349060440160206040518083038186803b15801561327a57600080fd5b505afa15801561328e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132b29190615844565b9392505050565b6000806132c7366008615e94565b5a6132d490615208615e40565b6132de9190615e40565b9050848110156133305760405162461bcd60e51b815260206004820152601b60248201527f524d3a206e6f7420656e6f756768206761732070726f766964656400000000006044820152606401610774565b61333b8c8c8c614556565b6133875760405162461bcd60e51b815260206004820152601e60248201527f524d3a20546172676574206f66205f6461746120213d205f77616c6c657400006044820152606401610774565b6133908c613e00565b158061339a575085155b6133e65760405162461bcd60e51b815260206004820152601860248201527f524d3a204c6f636b65642077616c6c657420726566756e6400000000000000006044820152606401610774565b6134176040805160a08101909152600080825260208201908152600060208201819052604082015260609081015290565b6134228d8d8d6110ab565b826020810182600481111561344757634e487b7160e01b600052602160045260246000fd5b600481111561346657634e487b7160e01b600052602160045260246000fd5b90529190915250805115158061349f575060008160200151600481111561349d57634e487b7160e01b600052602160045260246000fd5b145b6134eb5760405162461bcd60e51b815260206004820152601f60248201527f524d3a2057726f6e67207369676e617475726520726571756972656d656e74006044820152606401610774565b805188906134fa906041615e94565b146135475760405162461bcd60e51b815260206004820152601e60248201527f524d3a2057726f6e67206e756d626572206f66207369676e61747572657300006044820152606401610774565b61359c3060008e8e8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508d8b8b8b8b6145de565b8160400181815250506135be8d8b836040015184600001518560200151614677565b61360a5760405162461bcd60e51b815260206004820152601560248201527f524d3a204475706c6963617465207265717565737400000000000000000000006044820152606401610774565b60048160200151600481111561363057634e487b7160e01b600052602160045260246000fd5b141561368d576136468d82604001518b8b6147bb565b6136885760405162461bcd60e51b815260206004820152601360248201527229269d1024b73b30b634b21039b2b9b9b4b7b760691b6044820152606401610774565b613722565b6136d68d82604001518b8b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050506020850151614876565b6137225760405162461bcd60e51b815260206004820152601660248201527f524d3a20496e76616c6964207369676e617475726573000000000000000000006044820152606401610774565b6040513090613734908e908e90615afc565b6000604051808303816000865af19150503d8060008114613771576040519150601f19603f3d011682016040523d82523d6000602084013e613776565b606091505b50608083015215156060820152805160208201516137a0918f9185918b918b918b918b9190614a86565b806060015115158d6001600160a01b03167f7da4525a280527268ba2e963ee6c1b18f43c9507bcb1d2560f652ab17c76e90a836080015184604001516040516137ea929190615cb2565b60405180910390a3606001519c9b505050505050505050505050565b60405163f18858ab60e01b81526001600160a01b0383811660048301526000916138b0917f0000000000000000000000000000000000000000000000000000000000000000169063f18858ab9060240160006040518083038186803b15801561386e57600080fd5b505afa158015613882573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526138aa9190810190615791565b83614ebb565b509392505050565b60405163f18858ab60e01b81526001600160a01b0382811660048301526060917f00000000000000000000000000000000000000000000000000000000000000009091169063f18858ab9060240160006040518083038186803b15801561391e57600080fd5b505afa158015613932573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107b69190810190615791565b803033148061396e575061396e813361320c565b6139ba5760405162461bcd60e51b815260206004820152601960248201527f534d3a206d75737420626520677561726469616e2f73656c66000000000000006044820152606401610774565b816139c481613e00565b156139e15760405162461bcd60e51b815260040161077490615d0b565b613a1c83613a0f7f000000000000000000000000000000000000000000000000000000000000000042615e40565b63f435f5a760e01b6140e2565b6001600160a01b0383167f6395bace6e0acbe4f22761b149d3cc2e88c7dde6bf4d8481825eef404cf989a1613a717f000000000000000000000000000000000000000000000000000000000000000042615e40565b6040516001600160401b03909116815260200160405180910390a2505050565b8130331480613aa55750613aa58133613c6f565b613ac15760405162461bcd60e51b815260040161077490615cd4565b82613acb81613e00565b15613ae85760405162461bcd60e51b815260040161077490615d0b565b613af484846000614237565b826001600160a01b0316846001600160a01b03167fd288ab5da2e1f37cf384a1565a3f905ad289b092fbdd31950dbbfef148c04f8860405160405180910390a350505050565b6041808202830160208101516040820151919092015160009260ff9190911691601b831480613b6c57508260ff16601c145b613bb85760405162461bcd60e51b815260206004820152601f60248201527f5574696c733a2062616420762076616c756520696e207369676e6174757265006044820152606401610774565b604080516000808252602082018084528a905260ff861692820192909252606081018490526080810183905260019060a0016020604051602081039080840390855afa158015613c0c573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610b935760405162461bcd60e51b815260206004820152601b60248201527f5574696c733a2065637265636f7665722072657475726e6564203000000000006044820152606401610774565b6000816001600160a01b0316836001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015613cb457600080fd5b505afa158015613cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cec9190615536565b6001600160a01b0316149392505050565b6040516309ed185960e11b81526001600160a01b038216906313da30b290613d4b9030907f1626ba7e356f5979dd355a3d2bfb43e80420a480c3b854edce286a82d749686990600401615bb9565b600060405180830381600087803b158015613d6557600080fd5b505af1158015613d79573d6000803e3d6000fd5b50506040516309ed185960e11b81526001600160a01b03841692506313da30b29150613dcb9030907f150b7a023d4804d13e8c85fb27262cb750cf6ba9f9dd3bb30d90f482ceeb4b1f90600401615bb9565b600060405180830381600087803b158015613de557600080fd5b505af1158015613df9573d6000803e3d6000fd5b5050505050565b6001600160a01b03166000908152600160205260409020546001600160401b0342811691161190565b6001600160a01b038216613e7f5760405162461bcd60e51b815260206004820152601860248201527f544d3a20496e76616c69642073657373696f6e207573657200000000000000006044820152606401610774565b6000816001600160401b031611613ed85760405162461bcd60e51b815260206004820152601c60248201527f544d3a20496e76616c69642073657373696f6e206475726174696f6e000000006044820152606401610774565b6000613ef5613ef06001600160401b03841642615e40565b6150cb565b6040805180820182526001600160a01b038681168083526001600160401b0385811660208086018281528c86166000818152808452899020975188549251909516600160a01b026001600160e01b031990921694909616939093179290921790945584519182528101929092529293507f2ecea11087d1dc1431b517cbb5a559a9e33e58a1afeaac288f782c1c8bed8b8a910160405180910390a250505050565b60606000826001600160401b03811115613fc057634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015613ff357816020015b6060815260200190600190039081613fde5790505b50905060005b838110156140d95761409b8686868481811061402557634e487b7160e01b600052603260045260246000fd5b90506020028101906140379190615dac565b61404590602081019061551a565b87878581811061406557634e487b7160e01b600052603260045260246000fd5b90506020028101906140779190615dac565b6020013588888681811061274857634e487b7160e01b600052603260045260246000fd5b8282815181106140bb57634e487b7160e01b600052603260045260246000fd5b602002602001018190525080806140d190615efa565b915050613ff9565b50949350505050565b60405180604001604052806140f6846150cb565b6001600160401b0390811682526001600160e01b03199093166020918201526001600160a01b039094166000908152600185526040902081518154929095015160e01c600160401b026001600160601b031990921694909216939093179290921790915550565b6040516328207dbb60e11b81526001600160a01b0382811660048301526000916107b6917f00000000000000000000000000000000000000000000000000000000000000001690635040fb769060240160206040518083038186803b1580156141c557600080fd5b505afa1580156141d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141fd91906159c6565b60025b60008061420d8385615e80565b90506142198385615f15565b614224579050610891565b61422f816001615e40565b915050610891565b604051631017f7cd60e31b81526001600160a01b0384811660048301528381166024830152604482018390527f000000000000000000000000000000000000000000000000000000000000000016906380bfbe6890606401611b8d565b6001600160a01b0381166142ea5760405162461bcd60e51b815260206004820152601c60248201527f534d3a206e6577206f776e65722063616e6e6f74206265206e756c6c000000006044820152606401610774565b6142f4828261320c565b156108f95760405162461bcd60e51b815260206004820181905260248201527f534d3a206e6577206f776e65722063616e6e6f7420626520677561726469616e6044820152606401610774565b600060448251106144245760208201516001600160e01b0319811663a9059cbb60e01b148061438057506001600160e01b0319811663095ea7b360e01b145b8061439b57506001600160e01b0319811663a22cb46560e01b145b156143ac5750506024810151610891565b6001600160e01b031981166323b872dd60e01b14806143db57506001600160e01b03198116632142170760e11b145b806143f657506001600160e01b03198116635c46a7ef60e11b145b8061441157506001600160e01b03198116637921219560e11b145b156144225750506044810151610891565b505b5090919050565b60606000856001600160a01b031685858560405160240161444e93929190615bdc565b60408051601f198184030181529181526020820180516001600160e01b03166347b7819960e11b179052516144839190615b0c565b6000604051808303816000865af19150503d80600081146144c0576040519150601f19603f3d011682016040523d82523d6000602084013e6144c5565b606091505b50925090508080156144d8575060008251115b156144f857818060200190518101906144f1919061590f565b91506140d9565b815115614509573d6000803e3d6000fd5b806140d95760405162461bcd60e51b815260206004820152601a60248201527f424d3a2077616c6c657420696e766f6b652072657665727465640000000000006044820152606401610774565b600060248210156145a95760405162461bcd60e51b815260206004820152601660248201527f524d3a20496e76616c6964206461746157616c6c6574000000000000000000006044820152606401610774565b60006145b88360048187615e18565b8101906145c5919061551a565b6001600160a01b03908116908616149150509392505050565b60405160009061460a90601960f81b9083908c908c908c9046908d908d908d908d908d90602001615a72565b60408051601f198184030181529082905280516020918201207f19457468657265756d205369676e6564204d6573736167653a0a33320000000091830191909152603c820152605c0160405160208183030381529060405280519060200120905098975050505050505050565b60008260011480156146d1575060018260048111156146a657634e487b7160e01b600052602160045260246000fd5b14806146d1575060048260048111156146cf57634e487b7160e01b600052602160045260246000fd5b145b15614743576001600160a01b03861660009081526002602052604090205485116146fd57506000611f28565b608085901c61470e61271043615e40565b81111561471f576000915050611f28565b50506001600160a01b03851660009081526002602052604090208490556001611f28565b6001600160a01b038616600090815260026020908152604080832087845260019081019092529091205460ff161515141561478057506000611f28565b5050506001600160a01b0392909216600090815260026020908152604080832094835260019485019091529020805460ff1916831790555090565b6001600160a01b038481166000908152602081815260408083208151808301835290549485168152600160a01b9094046001600160401b0316848301528051601f86018390048302810183019091528481529192918391614838918891889088908190840183828082843760009201829052509250613b3a915050565b905081600001516001600160a01b0316816001600160a01b0316148015610b9357504282602001516001600160401b03161015979650505050505050565b600082516000141561488a57506001612c9c565b6000606060018460048111156148b057634e487b7160e01b600052602160045260246000fd5b1415806148be575060418551115b156149635760405163f18858ab60e01b81526001600160a01b0388811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063f18858ab9060240160006040518083038186803b15801561492457600080fd5b505afa158015614938573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526149609190810190615791565b90505b6000805b604187516149759190615e80565b811015614a77576000614989898984613b3a565b905081614a185760018760048111156149b257634e487b7160e01b600052602160045260246000fd5b14156149db576149c28a82613c6f565b156149cd5750614a65565b600095505050505050612c9c565b60028760048111156149fd57634e487b7160e01b600052602160045260246000fd5b1415614a1857614a0d8a82613c6f565b15614a185750614a65565b846001600160a01b0316816001600160a01b031611614a3f57600095505050505050612c9c565b809450614a4c8482614ebb565b9450925082614a6357600095505050505050612c9c565b505b80614a6f81615efa565b915050614967565b50600198975050505050505050565b600086118015614ade57506001816004811115614ab357634e487b7160e01b600052602160045260246000fd5b1480614ade57506004816004811115614adc57634e487b7160e01b600052602160045260246000fd5b145b15611d5e5760006001600160a01b03841615614afa5783614afc565b335b9050826001148015614b2d57506001826004811115614b2b57634e487b7160e01b600052602160045260246000fd5b145b15614ce95760408051602081018252600080825291516305cfdc4d60e41b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001692635cfdc4d092614b90928e9287929091600401615b7b565b60206040518083038186803b158015614ba857600080fd5b505afa158015614bbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614be09190615844565b614ce9576040516309fa507560e11b81526001600160a01b038a8116600483015282811660248301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906313f4a0ea9060440160206040518083038186803b158015614c5257600080fd5b505afa158015614c66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c8a91906159c6565b9050600081118015614c9b57504281105b614ce75760405162461bcd60e51b815260206004820152601960248201527f524d3a20726566756e64206e6f7420617574686f7269736564000000000000006044820152606401610774565b505b60006001600160a01b038616614d565760005a614d06908b615eb3565b614d12906159d8615e40565b9050614d1e893a615133565b614d28828a615133565b614d329190615e94565b9150614d4f8b84846040518060200160405280600081525061442b565b5050614e64565b60005a614d63908b615eb3565b614d6f9061927c615e40565b90506000614d7d883a615142565b9050614d898a82615133565b614d93838b615133565b614d9d9190615e94565b604080516001600160a01b038716602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790529093506000614df58e8b838561442b565b805190915015614e5f5780806020019051810190614e139190615844565b614e5f5760405162461bcd60e51b815260206004820152601a60248201527f524d3a20526566756e64207472616e73666572206661696c65640000000000006044820152606401610774565b505050505b604080516001600160a01b0388811682526020820184905280851692908d16917f22edd2bbb0b0afbdcf90d91da8a5e2100f8d8f67cdc766dee1742e9a36d6add3910160405180910390a350505050505050505050565b60006060835160001480614ed657506001600160a01b038316155b15614ee6575060009050826150c4565b60008060018651614ef79190615eb3565b6001600160401b03811115614f1c57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015614f45578160200160208202803683370190505b5090506000805b87518110156150aa578361502157878181518110614f7a57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316876001600160a01b03161415614fa45760019350615098565b614fdb888281518110614fc757634e487b7160e01b600052603260045260246000fd5b60200260200101513b63ffffffff16151590565b8015615013575061501388828151811061500557634e487b7160e01b600052603260045260246000fd5b60200260200101518861516a565b156150215760019350615098565b82518210156150985787818151811061504a57634e487b7160e01b600052603260045260246000fd5b602002602001015183838151811061507257634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101528161509481615efa565b9250505b806150a281615efa565b915050614f4c565b50826150b8576000876150bc565b6001825b945094505050505b9250929050565b6000600160401b821061512f5760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610774565b5090565b600081831061442457816132b2565b6000806000615150856151b4565b9092509050816151608286615e94565b611f289190615e80565b604051638da5cb5b60e01b815260009081906020818181886161a8fa600181141561519457815192505b5050826001600160a01b0316816001600160a01b03161491505092915050565b600080826001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031610156152a857600061521d7f0000000000000000000000000000000000000000000000000000000000000000856153a7565b9050806001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561525857600080fd5b505afa15801561526c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906152909190615978565b506001600160701b03918216945016915061535a9050565b60006152d4847f00000000000000000000000000000000000000000000000000000000000000006153a7565b9050806001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561530f57600080fd5b505afa158015615323573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906153479190615978565b506001600160701b039081169450169150505b811580159061536857508015155b6121d75760405162461bcd60e51b815260206004820152601060248201526f534f3a206e6f206c697175696469747960801b6044820152606401610774565b6040516001600160601b0319606084811b8216602084015283901b1660348201526000907f000000000000000000000000000000000000000000000000000000000000000090604801604051602081830303815290604052805190602001206040516020016154639291906001600160f81b0319815260609290921b6001600160601b031916600183015260158201527f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f603582015260550190565b60408051601f1981840301815291905280516020909101209392505050565b60008083601f840112615493578182fd5b5081356001600160401b038111156154a9578182fd5b6020830191508360208260051b85010111156150c457600080fd5b60008083601f8401126154d5578182fd5b5081356001600160401b038111156154eb578182fd5b6020830191508360208285010111156150c457600080fd5b80516001600160701b03811681146107b957600080fd5b60006020828403121561552b578081fd5b81356132b281615f6b565b600060208284031215615547578081fd5b81516132b281615f6b565b60008060408385031215615564578081fd5b823561556f81615f6b565b9150602083013561557f81615f6b565b809150509250929050565b60008060006040848603121561559e578081fd5b83356155a981615f6b565b925060208401356001600160401b038111156155c3578182fd5b6155cf86828701615482565b9497909650939450505050565b6000806000806000608086880312156155f3578081fd5b85356155fe81615f6b565b945060208601356001600160401b0380821115615619578283fd5b61562589838a01615482565b90965094506040880135915061563a82615f6b565b9092506060870135908082168214615650578283fd5b50809150509295509295909350565b60008060408385031215615671578182fd5b823561567c81615f6b565b946020939093013593505050565b60008060006040848603121561569e578081fd5b83356156a981615f6b565b925060208401356001600160401b038111156156c3578182fd5b6155cf868287016154c4565b6000806000806000806000806000806101008b8d0312156156ee578788fd5b8a356156f981615f6b565b995060208b01356001600160401b038082111561571457898afd5b6157208e838f016154c4565b909b50995060408d0135985060608d013591508082111561573f578687fd5b5061574c8d828e016154c4565b90975095505060808b0135935060a08b0135925060c08b013561576e81615f6b565b915060e08b013561577e81615f6b565b809150509295989b9194979a5092959850565b600060208083850312156157a3578182fd5b82516001600160401b03808211156157b9578384fd5b818501915085601f8301126157cc578384fd5b8151818111156157de576157de615f55565b8060051b91506157ef848301615dc1565b8181528481019084860184860187018a1015615809578788fd5b8795505b83861015615837578051945061582285615f6b565b8483526001959095019491860191860161580d565b5098975050505050505050565b600060208284031215615855578081fd5b815180151581146132b2578182fd5b60008060408385031215615876578182fd5b8235915060208301356001600160401b03811115615892578182fd5b8301601f810185136158a2578182fd5b80356158b56158b082615df1565b615dc1565b8181528660208385010111156158c9578384fd5b81602084016020830137908101602001929092525090939092509050565b6000602082840312156158f8578081fd5b81356001600160e01b0319811681146132b2578182fd5b600060208284031215615920578081fd5b81516001600160401b03811115615935578182fd5b8201601f81018413615945578182fd5b80516159536158b082615df1565b818152856020838501011115615967578384fd5b611f28826020830160208601615eca565b60008060006060848603121561598c578081fd5b61599584615503565b92506159a360208501615503565b9150604084015163ffffffff811681146159bb578182fd5b809150509250925092565b6000602082840312156159d7578081fd5b5051919050565b600081518084526159f6816020860160208601615eca565b601f01601f19169290920160200192915050565b6001600160601b0319606093841b811682529190921b166014820152693932bb37b5b0ba34b7b760b11b602882015260320190565b6001600160601b0319606093841b811682529190921b1660148201526730b23234ba34b7b760c11b602882015260300190565b600060ff60f81b808e168352808d166001840152506bffffffffffffffffffffffff19808c60601b1660028401528a60168401528951615ab9816036860160208e01615eca565b909201603681019890985250605687019590955260768601939093526096850191909152606090811b831660b68501521b1660ca82015260de0195945050505050565b6000828483379101908152919050565b60008251615b1e818460208701615eca565b9190910192915050565b60006001600160a01b038088168352808716602084015280861660408401525060806060830152826080830152828460a084013781830160a090810191909152601f909201601f19160101949350505050565b60006001600160a01b038087168352808616602084015280851660408401525060806060830152615baf60808301846159de565b9695505050505050565b6001600160a01b039290921682526001600160e01b031916602082015260400190565b60006001600160a01b038516825283602083015260606040830152611f2860608301846159de565b6020808252825182820181905260009190848201906040850190845b81811015615c455783516001600160a01b031683529284019291840191600101615c20565b50909695505050505050565b6000602080830181845280855180835260408601915060408160051b8701019250838701855b82811015615ca557603f19888603018452615c938583516159de565b94509285019290850190600101615c77565b5092979650505050505050565b600060408252615cc560408301856159de565b90508260208301529392505050565b6020808252601d908201527f424d3a206d7573742062652077616c6c6574206f776e65722f73656c66000000604082015260600190565b60208082526011908201527010934e881dd85b1b195d081b1bd8dad959607a1b604082015260600190565b8281526040810160058310615d5b57634e487b7160e01b600052602160045260246000fd5b8260208301529392505050565b6000808335601e19843603018112615d7e578283fd5b8301803591506001600160401b03821115615d97578283fd5b6020019150368190038213156150c457600080fd5b60008235605e19833603018112615b1e578182fd5b604051601f8201601f191681016001600160401b0381118282101715615de957615de9615f55565b604052919050565b60006001600160401b03821115615e0a57615e0a615f55565b50601f01601f191660200190565b60008085851115615e27578182fd5b83861115615e33578182fd5b5050820193919092039150565b60008219821115615e5357615e53615f29565b500190565b600063ffffffff808316818516808303821115615e7757615e77615f29565b01949350505050565b600082615e8f57615e8f615f3f565b500490565b6000816000190483118215151615615eae57615eae615f29565b500290565b600082821015615ec557615ec5615f29565b500390565b60005b83811015615ee5578181015183820152602001615ecd565b83811115615ef4576000848401525b50505050565b6000600019821415615f0e57615f0e615f29565b5060010190565b600082615f2457615f24615f3f565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114615f8057600080fd5b5056fea264697066735822122081d4fd652d7092f6bc4fbd840c7ca9e79421eccf5ef61d2be2b36c17356df9f564736f6c634300080300330000000000000000000000008ff41919435d50f113afd5bc25b88acf4cc3d8cc0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b000000000000000000000000577e0b01a8538e4cb36e5f202528157f65cdf08a000000000000000000000000b5ecc8ab46e2e20573c2e57c865f7c97f58c27980000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d0000000000000000000000000000000000000000000000000000000000000e100000000000000000000000000000000000000000000000000000000000000e100000000000000000000000000000000000000000000000000000000000001c200000000000000000000000000000000000000000000000000000000000003840

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102485760003560e01c80636ff6ec7c1161013b578063ba821088116100b8578063eac01e471161007c578063eac01e47146106cb578063f143ddba14610517578063f18858ab146106de578063f435f5a7146106fe578063f8d3277d1461071157610248565b8063ba8210881461066c578063c68452101461067f578063c90db44714610692578063d4ee9734146106a5578063e0724b6e146106b857610248565b8063a3f4df7e116100ff578063a3f4df7e1461060a578063a5efb23514610620578063a6eb069014610633578063b0ba4da014610646578063b6b352721461065957610248565b80636ff6ec7c1461051757806370135f521461052a5780638c8e13b91461053d5780639769c3fe146105775780639be65a60146105f757610248565b80633b73d67f116101c957806359b4958a1161018d57806359b4958a146104765780635a1db8c41461048957806360c0fdc01461049c5780636b9db4e6146104d95780636d4354211461050457610248565b80633b73d67f146104095780634a4fbeec1461042a5780634b3ef0541461043d5780635040fb7614610450578063575182431461046357610248565b806325b509341161021057806325b50934146103865780632960739b146103995780632d0335ab146103ac5780632f6c493c146103e3578063315a7af3146103f657610248565b806301ffc9a7146102ea5780631626ba7e1461031257806319ab453c1461033e5780631d97d8cc146103535780632437b75c14610366575b600061028a6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061072492505050565b90506001600160e01b03198116630a85bd0160e11b14806102bb57506001600160e01b0319811663f23a6e6160e01b145b806102d657506001600160e01b0319811663bc197c8160e01b145b156102e75760046000803760206000f35b50005b6102fd6102f83660046158e7565b610785565b60405190151581526020015b60405180910390f35b610325610320366004615864565b6107be565b6040516001600160e01b03199091168152602001610309565b61035161034c36600461551a565b610897565b005b610351610361366004615552565b6108fd565b6103796103743660046155dc565b610b10565b6040516103099190615c51565b6102fd6103943660046158e7565b610b9e565b6103516103a7366004615552565b610c22565b6103d56103ba36600461551a565b6001600160a01b031660009081526002602052604090205490565b604051908152602001610309565b6103516103f136600461551a565b610d66565b61035161040436600461551a565b610ed7565b61041c61041736600461568a565b6110ab565b604051610309929190615d36565b6102fd61043836600461551a565b611478565b61035161044b366004615552565b611483565b6103d561045e36600461551a565b6116d2565b610351610471366004615552565b611770565b61035161048436600461551a565b6119e8565b610351610497366004615552565b611bc4565b6102fd6104aa36600461565f565b6001600160a01b0391909116600090815260026020908152604080832093835260019093019052205460ff1690565b6104ec6104e736600461551a565b611d68565b6040516001600160401b039091168152602001610309565b610351610512366004615552565b611da3565b61037961052536600461558a565b611eb0565b610351610538366004615552565b611f31565b61055061054b36600461551a565b6121a8565b604080516001600160a01b0390931683526001600160401b03909116602083015201610309565b6105c461058536600461551a565b6001600160a01b0390811660009081526003602052604090205490811691600160a01b82046001600160401b031691600160e01b900463ffffffff1690565b604080516001600160a01b0390941684526001600160401b03909216602084015263ffffffff1690820152606001610309565b61035161060536600461551a565b6121dc565b6103d56b417267656e744d6f64756c6560a01b81565b61037961062e36600461558a565b612301565b610351610641366004615552565b6127eb565b610351610654366004615552565b61292f565b6102fd610667366004615552565b612be5565b61035161067a36600461551a565b612ca4565b61035161068d366004615552565b612d77565b6103516106a036600461551a565b6130e3565b6102fd6106b3366004615552565b61320c565b6102fd6106c63660046156cf565b6132b9565b6102fd6106d9366004615552565b613806565b6106f16106ec36600461551a565b6138b8565b6040516103099190615c04565b61035161070c36600461551a565b61395a565b61035161071f366004615552565b613a91565b600060048251101561077d5760405162461bcd60e51b815260206004820152601d60248201527f5574696c733a20496e76616c69642066756e6374696f6e50726566697800000060448201526064015b60405180910390fd5b506020015190565b60006001600160e01b031982166301ffc9a760e01b14806107b657506001600160e01b03198216630271189760e51b145b90505b919050565b600081516041146108115760405162461bcd60e51b815260206004820152601c60248201527f544d3a20696e76616c6964207369676e6174757265206c656e677468000000006044820152606401610774565b600061081f84846000613b3a565b905061082b3382613c6f565b61086c5760405162461bcd60e51b81526020600482015260126024820152712a269d1024b73b30b634b21039b4b3b732b960711b6044820152606401610774565b7f1626ba7e356f5979dd355a3d2bfb43e80420a480c3b854edce286a82d74968699150505b92915050565b80336001600160a01b038216146108f05760405162461bcd60e51b815260206004820152601960248201527f424d3a2063616c6c6572206d7573742062652077616c6c6574000000000000006044820152606401610774565b6108f982613cfd565b5050565b813033148061091157506109118133613c6f565b61092d5760405162461bcd60e51b815260040161077490615cd4565b610937838361320c565b6109835760405162461bcd60e51b815260206004820152601d60248201527f534d3a206d757374206265206578697374696e6720677561726469616e0000006044820152606401610774565b60008383604051602001610998929190615a0a565b60408051601f1981840301815291815281516020928301206001600160a01b0387166000908152600484528281208282529384905291909120549092501580610a195750600082815260208290526040902054610a16907f0000000000000000000000000000000000000000000000000000000000000e1090615e40565b42115b610a655760405162461bcd60e51b815260206004820152601c60248201527f534d3a206475706c69636174652070656e64696e67207265766f6b65000000006044820152606401610774565b610a8f7f0000000000000000000000000000000000000000000000000000000000000e1042615e40565b6000838152602083905260409020556001600160a01b038085169086167f9746f6868f544595794833da53250bd19e72334733336cfd5dd6fbc5f6a6ac42610af77f0000000000000000000000000000000000000000000000000000000000000e1042615e40565b6040519081526020015b60405180910390a35050505050565b6060303314610b565760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b85610b6081613e00565b15610b7d5760405162461bcd60e51b815260040161077490615d0b565b610b88878585613e29565b610b93878787613f96565b979650505050505050565b60006001600160e01b03198216630b135d3f60e11b1480610bcf57506001600160e01b03198216630a85bd0160e11b145b80610bea57506001600160e01b031982166301ffc9a760e01b145b80610c0557506001600160e01b0319821663f23a6e6160e01b145b806107b65750506001600160e01b03191663bc197c8160e01b1490565b8130331480610c365750610c368133613c6f565b610c525760405162461bcd60e51b815260040161077490615cd4565b82610c5c81613e00565b15610c795760405162461bcd60e51b815260040161077490615d0b565b60008484604051602001610c8e929190615a0a565b60408051601f1981840301815291815281516020928301206001600160a01b038816600090815260048452828120828252938490529190912054909250610d175760405162461bcd60e51b815260206004820152601a60248201527f534d3a20756e6b6e6f776e2070656e64696e67207265766f6b650000000000006044820152606401610774565b600082815260208290526040808220829055516001600160a01b0380881692908916917fc0b205956d5e27c296695de329b5a014584a4f51824b1725a0eefc1174d6dbd59190a3505050505050565b8030331480610d7a5750610d7a813361320c565b610dc65760405162461bcd60e51b815260206004820152601960248201527f534d3a206d75737420626520677561726469616e2f73656c66000000000000006044820152606401610774565b81610dd081613e00565b610e1c5760405162461bcd60e51b815260206004820152601960248201527f424d3a2077616c6c6574206d757374206265206c6f636b6564000000000000006044820152606401610774565b6001600160a01b038316600090815260016020526040902054600160401b900460e01b6001600160e01b03191663f435f5a760e01b14610e925760405162461bcd60e51b8152602060048201526011602482015270534d3a2063616e6e6f7420756e6c6f636b60781b6044820152606401610774565b610e9e836000806140e2565b6040516001600160a01b038416907f7e6adfec7e3f286831a0200a754127c171a2da564078722cb97704741bbdb0ea90600090a2505050565b6001600160a01b0381166000908152600360205260409020548190600160a01b90046001600160401b0316610f485760405162461bcd60e51b8152602060048201526017602482015276534d3a206e6f206f6e676f696e67207265636f7665727960481b6044820152606401610774565b6001600160a01b038216600090815260036020526040902080546001600160401b03600160a01b90910481164290911611610fc55760405162461bcd60e51b815260206004820152601b60248201527f534d3a206f6e676f696e67207265636f7665727920706572696f6400000000006044820152606401610774565b80546001600160a01b03848116600090815260036020908152604080832083905590829052902080546001600160e01b0319169055166040516313af403560e01b81526001600160a01b0382811660048301528516906313af403590602401600060405180830381600087803b15801561103e57600080fd5b505af1158015611052573d6000803e3d6000fd5b505050506110658460008060e01b6140e2565b806001600160a01b0316846001600160a01b03167fd8667de85dae2d56d76e700d16de53d21ac2ce4d5549cb0bf51c55fdc37f0bc160405160405180910390a350505050565b60008060006110ef85858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061072492505050565b90506001600160e01b0319811663a5efb23560e01b148061112057506001600160e01b03198116635751824360e01b145b8061113b57506001600160e01b0319811663f8d3277d60e01b145b8061115657506001600160e01b03198116632cda4ac560e11b145b8061117157506001600160e01b03198116631750421160e31b145b8061118c57506001600160e01b031981166316876e3160e21b145b806111a757506001600160e01b03198116630c68452160e41b145b806111c257506001600160e01b03198116630765f63360e21b145b806111dd57506001600160e01b03198116630a6eb06960e41b145b806111f857506001600160e01b03198116632960739b60e01b145b1561120a576001809250925050611470565b6001600160e01b03198116631bfdbb1f60e21b141561123157600160049250925050611470565b6001600160e01b03198116630585d26d60e51b14156112b45760006112558761415d565b9050600081116112a75760405162461bcd60e51b815260206004820152601e60248201527f414d3a206e6f20677561726469616e7320736574206f6e2077616c6c657400006044820152606401610774565b9250600391506114709050565b6001600160e01b0319811663c90db44760e01b141561131d576001600160a01b03861660009081526003602052604081205461130f9061130290600160e01b900463ffffffff166001615e58565b63ffffffff166002614200565b935060029250611470915050565b6001600160e01b031981166378a1eedd60e11b148061134c57506001600160e01b0319811663090dedd760e21b145b8061136757506001600160e01b03198116636d43542160e01b145b156113955760006113778761415d565b90506000611386826001615e40565b94506001935061147092505050565b6001600160e01b0319811663315a7af360e01b14806113c457506001600160e01b03198116633809afa960e11b145b806113df57506001600160e01b031981166312cfbc1560e21b145b156113f1576000809250925050611470565b6001600160e01b0319811663f435f5a760e01b148061142057506001600160e01b03198116630bdb124f60e21b145b1561143357600160039250925050611470565b60405162461bcd60e51b815260206004820152601260248201527114d34e881d5b9adb9bdddb881b595d1a1bd960721b6044820152606401610774565b935093915050565b60006107b682613e00565b60008282604051602001611498929190615a0a565b60408051601f1981840301815291815281516020928301206001600160a01b0386166000908152600484528281208282529384905291909120549092506115215760405162461bcd60e51b815260206004820152601a60248201527f534d3a20756e6b6e6f776e2070656e64696e67207265766f6b650000000000006044820152606401610774565b600082815260208290526040902054421161157e5760405162461bcd60e51b815260206004820152601b60248201527f534d3a2070656e64696e67207265766f6b65206e6f74206f76657200000000006044820152606401610774565b6000828152602082905260409020546115b8907f0000000000000000000000000000000000000000000000000000000000000e1090615e40565b42106116065760405162461bcd60e51b815260206004820152601a60248201527f534d3a2070656e64696e67207265766f6b6520657870697265640000000000006044820152606401610774565b604051630765f63360e21b81526001600160a01b03858116600483015284811660248301527f0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b1690631d97d8cc90604401600060405180830381600087803b15801561167157600080fd5b505af1158015611685573d6000803e3d6000fd5b50506040516001600160a01b038087169350871691507f548f10dcba266544123ad8cf8284f25c4baa659cba25dbdf16a06ea11235de9b90600090a3600091825260205260408120555050565b6040516328207dbb60e11b81526001600160a01b0382811660048301526000917f0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b90911690635040fb769060240160206040518083038186803b15801561173857600080fd5b505afa15801561174c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b691906159c6565b813033148061178457506117848133613c6f565b6117a05760405162461bcd60e51b815260040161077490615cd4565b826117aa81613e00565b156117c75760405162461bcd60e51b815260040161077490615d0b565b836001600160a01b0316836001600160a01b031614156118295760405162461bcd60e51b815260206004820152601b60248201527f544d3a2043616e6e6f742077686974656c6973742077616c6c657400000000006044820152606401610774565b604051630bcd4ebb60e01b81526001600160a01b0384811660048301527f0000000000000000000000008ff41919435d50f113afd5bc25b88acf4cc3d8cc1690630bcd4ebb9060240160206040518083038186803b15801561188a57600080fd5b505afa15801561189e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c29190615844565b1561190f5760405162461bcd60e51b815260206004820152601b60248201527f544d3a2043616e6e6f742077686974656c697374206d6f64756c6500000000006044820152606401610774565b6119198484612be5565b156119665760405162461bcd60e51b815260206004820152601e60248201527f544d3a2074617267657420616c72656164792077686974656c697374656400006044820152606401610774565b60006119927f0000000000000000000000000000000000000000000000000000000000000e1042615e40565b905061199f858583614237565b6040516001600160401b03821681526001600160a01b0380861691908716907f1f57f9641d3e8733ed672fef5ac85464bd7215ef2f21e83428e8408248b13dcd90602001610b01565b80303314806119fc57506119fc8133613c6f565b611a185760405162461bcd60e51b815260040161077490615cd4565b81611a2281613e00565b15611a3f5760405162461bcd60e51b815260040161077490615d0b565b6040516309ed185960e11b81526001600160a01b038416906313da30b290611a8d9030907f01ffc9a7a5cef8baa21ed3c5c0d7e23accb804b619e9333b597f47a0d84076e290600401615bb9565b600060405180830381600087803b158015611aa757600080fd5b505af1158015611abb573d6000803e3d6000fd5b50506040516309ed185960e11b81526001600160a01b03861692506313da30b29150611b0d9030907ff23a6e612e1ff4830e658fe43f4e3cb4a5f8170bd5d9e69fb5d7a7fa9e4fdf9790600401615bb9565b600060405180830381600087803b158015611b2757600080fd5b505af1158015611b3b573d6000803e3d6000fd5b50506040516309ed185960e11b81526001600160a01b03861692506313da30b29150611b8d9030907fbc197c819b3e337a6f9652dd10becd7eef83032af3b9d958d3d42f669414662190600401615bb9565b600060405180830381600087803b158015611ba757600080fd5b505af1158015611bbb573d6000803e3d6000fd5b50505050505050565b8130331480611bd85750611bd88133613c6f565b611bf45760405162461bcd60e51b815260040161077490615cd4565b82611bfe81613e00565b15611c1b5760405162461bcd60e51b815260040161077490615d0b565b604051630bcd4ebb60e01b81526001600160a01b0384811660048301527f0000000000000000000000008ff41919435d50f113afd5bc25b88acf4cc3d8cc1690630bcd4ebb9060240160206040518083038186803b158015611c7c57600080fd5b505afa158015611c90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb49190615844565b611d005760405162461bcd60e51b815260206004820152601c60248201527f414d3a206d6f64756c65206973206e6f742072656769737465726564000000006044820152606401610774565b604051631f17732d60e01b81526001600160a01b03848116600483015260016024830152851690631f17732d90604401600060405180830381600087803b158015611d4a57600080fd5b505af1158015611d5e573d6000803e3d6000fd5b5050505050505050565b6000611d7382613e00565b611d7e5760006107b6565b506001600160a01b03166000908152600160205260409020546001600160401b031690565b303314611de75760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b81611df181613e00565b15611e0e5760405162461bcd60e51b815260040161077490615d0b565b611e188383614294565b6040516313af403560e01b81526001600160a01b0383811660048301528416906313af403590602401600060405180830381600087803b158015611e5b57600080fd5b505af1158015611e6f573d6000803e3d6000fd5b50506040516001600160a01b038086169350861691507f0d18b5fd22306e373229b9439188228edca81207d1667f604daf6cef8aa3ee6790600090a3505050565b6060303314611ef65760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b83611f0081613e00565b15611f1d5760405162461bcd60e51b815260040161077490615d0b565b611f28858585613f96565b95945050505050565b81611f3b81613e00565b15611f585760405162461bcd60e51b815260040161077490615d0b565b60008383604051602001611f6d929190615a3f565b60408051601f1981840301815291815281516020928301206001600160a01b038716600090815260048452828120828252938490529190912054909250611ff65760405162461bcd60e51b815260206004820152601c60248201527f534d3a20756e6b6e6f776e2070656e64696e67206164646974696f6e000000006044820152606401610774565b60008281526020829052604090205442116120535760405162461bcd60e51b815260206004820152601d60248201527f534d3a2070656e64696e67206164646974696f6e206e6f74206f7665720000006044820152606401610774565b60008281526020829052604090205461208d907f0000000000000000000000000000000000000000000000000000000000000e1090615e40565b42106120db5760405162461bcd60e51b815260206004820152601c60248201527f534d3a2070656e64696e67206164646974696f6e2065787069726564000000006044820152606401610774565b604051630c68452160e41b81526001600160a01b03868116600483015285811660248301527f0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b169063c684521090604401600060405180830381600087803b15801561214657600080fd5b505af115801561215a573d6000803e3d6000fd5b50506040516001600160a01b038088169350881691507fbc3292102fa77e083913064b282926717cdfaede4d35f553d66366c0a3da755a90600090a360009182526020526040812055505050565b6001600160a01b0381811660009081526020819052604090205490811690600160a01b90046001600160401b03165b915091565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a082319060240160206040518083038186803b15801561221e57600080fd5b505afa158015612232573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061225691906159c6565b60405163a9059cbb60e01b81526001600160a01b037f0000000000000000000000008ff41919435d50f113afd5bc25b88acf4cc3d8cc81166004830152602482018390529192509083169063a9059cbb90604401602060405180830381600087803b1580156122c457600080fd5b505af11580156122d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122fc9190615844565b505050565b60603033146123475760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b8361235181613e00565b1561236e5760405162461bcd60e51b815260040161077490615d0b565b6000836001600160401b0381111561239657634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156123c957816020015b60608152602001906001900390816123b45790505b50905060005b848110156127e15760006124968787848181106123fc57634e487b7160e01b600052603260045260246000fd5b905060200281019061240e9190615dac565b61241c90602081019061551a565b88888581811061243c57634e487b7160e01b600052603260045260246000fd5b905060200281019061244e9190615dac565b61245c906040810190615d68565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061434192505050565b90508686838181106124b857634e487b7160e01b600052603260045260246000fd5b90506020028101906124ca9190615dac565b60200135158061252a57508686838181106124f557634e487b7160e01b600052603260045260246000fd5b90506020028101906125079190615dac565b61251590602081019061551a565b6001600160a01b0316816001600160a01b0316145b8015612662575061253b8882612be5565b8061266257507f000000000000000000000000b5ecc8ab46e2e20573c2e57c865f7c97f58c27986001600160a01b0316635cfdc4d089838a8a8781811061259257634e487b7160e01b600052603260045260246000fd5b90506020028101906125a49190615dac565b6125b290602081019061551a565b8b8b888181106125d257634e487b7160e01b600052603260045260246000fd5b90506020028101906125e49190615dac565b6125f2906040810190615d68565b6040518663ffffffff1660e01b8152600401612612959493929190615b28565b60206040518083038186803b15801561262a57600080fd5b505afa15801561263e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126629190615844565b6126ae5760405162461bcd60e51b815260206004820152601760248201527f544d3a2063616c6c206e6f7420617574686f72697365640000000000000000006044820152606401610774565b6127a2888888858181106126d257634e487b7160e01b600052603260045260246000fd5b90506020028101906126e49190615dac565b6126f290602081019061551a565b89898681811061271257634e487b7160e01b600052603260045260246000fd5b90506020028101906127249190615dac565b602001358a8a8781811061274857634e487b7160e01b600052603260045260246000fd5b905060200281019061275a9190615dac565b612768906040810190615d68565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061442b92505050565b8383815181106127c257634e487b7160e01b600052603260045260246000fd5b60200260200101819052505080806127d990615efa565b9150506123cf565b5095945050505050565b81303314806127ff57506127ff8133613c6f565b61281b5760405162461bcd60e51b815260040161077490615cd4565b8261282581613e00565b156128425760405162461bcd60e51b815260040161077490615d0b565b60008484604051602001612857929190615a3f565b60408051601f1981840301815291815281516020928301206001600160a01b0388166000908152600484528281208282529384905291909120549092506128e05760405162461bcd60e51b815260206004820152601c60248201527f534d3a20756e6b6e6f776e2070656e64696e67206164646974696f6e000000006044820152606401610774565b600082815260208290526040808220829055516001600160a01b0380881692908916917faa13b27c23e9e3f3d5f3861a53b7a2931e019170a6a19ed64942e26a1dd5987a9190a3505050505050565b3033146129735760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b6001600160a01b0382166000908152600360205260409020548290600160a01b90046001600160401b0316156129eb5760405162461bcd60e51b815260206004820152601460248201527f534d3a206f6e676f696e67207265636f766572790000000000000000000000006044820152606401610774565b6129f58383614294565b6000612a217f0000000000000000000000000000000000000000000000000000000000001c2042615e40565b604080516060810182526001600160a01b0380871682526001600160401b038416602083015282516328207dbb60e11b815288821660048201529394509092918301917f0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b90911690635040fb769060240160206040518083038186803b158015612aaa57600080fd5b505afa158015612abe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae291906159c6565b63ffffffff9081169091526001600160a01b0380871660009081526003602090815260409182902085518154928701519690930151909416600160e01b026001600160e01b036001600160401b03909616600160a01b026001600160e01b031990921692909316919091171792909216919091179055612b9384612b867f000000000000000000000000000000000000000000000000000000000000384042615e40565b630585d26d60e51b6140e2565b6040516001600160401b03821681526001600160a01b0380851691908616907f5f59bfd9baba55ae30bb440923cbbe30987d50e12a4e9134ffac3fd9afc3526d9060200160405180910390a350505050565b6040516309fa507560e11b81526001600160a01b038381166004830152828116602483015260009182917f000000000000000000000000577e0b01a8538e4cb36e5f202528157f65cdf08a16906313f4a0ea9060440160206040518083038186803b158015612c5357600080fd5b505afa158015612c67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c8b91906159c6565b9050600081118015612c9c57504281105b949350505050565b8030331480612cb85750612cb88133613c6f565b612cd45760405162461bcd60e51b815260040161077490615cd4565b81612cde81613e00565b15612cfb5760405162461bcd60e51b815260040161077490615d0b565b6001600160a01b038381166000818152602081815260409182902054915191909316815290917feb290a597820eccc6b8b31f942bd97c633d5138f4d849751f770f3cb3900e57a910160405180910390a26122fc836001600160a01b0316600090815260208190526040902080546001600160e01b0319169055565b8130331480612d8b5750612d8b8133613c6f565b612da75760405162461bcd60e51b815260040161077490615cd4565b82612db181613e00565b15612dce5760405162461bcd60e51b815260040161077490615d0b565b612dd88484613c6f565b15612e255760405162461bcd60e51b815260206004820152601c60248201527f534d3a20677561726469616e2063616e6e6f74206265206f776e6572000000006044820152606401610774565b612e2f848461320c565b15612e7c5760405162461bcd60e51b815260206004820152601660248201527f534d3a206475706c696361746520677561726469616e000000000000000000006044820152606401610774565b60408051600481526024810182526020810180516001600160e01b0316638da5cb5b60e01b17905290516000916001600160a01b038616916161a891612ec191615b0c565b60006040518083038160008787f1925050503d8060008114612eff576040519150601f19603f3d011682016040523d82523d6000602084013e612f04565b606091505b5050905080612f555760405162461bcd60e51b815260206004820152601d60248201527f534d3a206d75737420626520454f412f417267656e742077616c6c65740000006044820152606401610774565b60008585604051602001612f6a929190615a3f565b60408051601f1981840301815291815281516020928301206001600160a01b0389166000908152600484528281208282529384905291909120549092501580612feb5750600082815260208290526040902054612fe8907f0000000000000000000000000000000000000000000000000000000000000e1090615e40565b42115b6130375760405162461bcd60e51b815260206004820152601e60248201527f534d3a206475706c69636174652070656e64696e67206164646974696f6e00006044820152606401610774565b6130617f0000000000000000000000000000000000000000000000000000000000000e1042615e40565b6000838152602083905260409020556001600160a01b038087169088167fe4166e4bc55a182bd13d933553241bb3441b91d15fbc74c5c752f96965563bde6130c97f0000000000000000000000000000000000000000000000000000000000000e1042615e40565b60405190815260200160405180910390a350505050505050565b3033146131275760405162461bcd60e51b8152602060048201526012602482015271424d3a206d757374206265206d6f64756c6560701b6044820152606401610774565b6001600160a01b0381166000908152600360205260409020548190600160a01b90046001600160401b03166131985760405162461bcd60e51b8152602060048201526017602482015276534d3a206e6f206f6e676f696e67207265636f7665727960481b6044820152606401610774565b6001600160a01b038083166000908152600360205260408120805490829055909116906131c7908490806140e2565b806001600160a01b0316836001600160a01b03167fc45926607303da71dbeffd2ed5c6b00f581982586b697655d19ae4c4d558f25960405160405180910390a3505050565b60405163353ba5cd60e21b81526001600160a01b03838116600483015282811660248301526000917f0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b9091169063d4ee97349060440160206040518083038186803b15801561327a57600080fd5b505afa15801561328e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132b29190615844565b9392505050565b6000806132c7366008615e94565b5a6132d490615208615e40565b6132de9190615e40565b9050848110156133305760405162461bcd60e51b815260206004820152601b60248201527f524d3a206e6f7420656e6f756768206761732070726f766964656400000000006044820152606401610774565b61333b8c8c8c614556565b6133875760405162461bcd60e51b815260206004820152601e60248201527f524d3a20546172676574206f66205f6461746120213d205f77616c6c657400006044820152606401610774565b6133908c613e00565b158061339a575085155b6133e65760405162461bcd60e51b815260206004820152601860248201527f524d3a204c6f636b65642077616c6c657420726566756e6400000000000000006044820152606401610774565b6134176040805160a08101909152600080825260208201908152600060208201819052604082015260609081015290565b6134228d8d8d6110ab565b826020810182600481111561344757634e487b7160e01b600052602160045260246000fd5b600481111561346657634e487b7160e01b600052602160045260246000fd5b90529190915250805115158061349f575060008160200151600481111561349d57634e487b7160e01b600052602160045260246000fd5b145b6134eb5760405162461bcd60e51b815260206004820152601f60248201527f524d3a2057726f6e67207369676e617475726520726571756972656d656e74006044820152606401610774565b805188906134fa906041615e94565b146135475760405162461bcd60e51b815260206004820152601e60248201527f524d3a2057726f6e67206e756d626572206f66207369676e61747572657300006044820152606401610774565b61359c3060008e8e8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508d8b8b8b8b6145de565b8160400181815250506135be8d8b836040015184600001518560200151614677565b61360a5760405162461bcd60e51b815260206004820152601560248201527f524d3a204475706c6963617465207265717565737400000000000000000000006044820152606401610774565b60048160200151600481111561363057634e487b7160e01b600052602160045260246000fd5b141561368d576136468d82604001518b8b6147bb565b6136885760405162461bcd60e51b815260206004820152601360248201527229269d1024b73b30b634b21039b2b9b9b4b7b760691b6044820152606401610774565b613722565b6136d68d82604001518b8b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050506020850151614876565b6137225760405162461bcd60e51b815260206004820152601660248201527f524d3a20496e76616c6964207369676e617475726573000000000000000000006044820152606401610774565b6040513090613734908e908e90615afc565b6000604051808303816000865af19150503d8060008114613771576040519150601f19603f3d011682016040523d82523d6000602084013e613776565b606091505b50608083015215156060820152805160208201516137a0918f9185918b918b918b918b9190614a86565b806060015115158d6001600160a01b03167f7da4525a280527268ba2e963ee6c1b18f43c9507bcb1d2560f652ab17c76e90a836080015184604001516040516137ea929190615cb2565b60405180910390a3606001519c9b505050505050505050505050565b60405163f18858ab60e01b81526001600160a01b0383811660048301526000916138b0917f0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b169063f18858ab9060240160006040518083038186803b15801561386e57600080fd5b505afa158015613882573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526138aa9190810190615791565b83614ebb565b509392505050565b60405163f18858ab60e01b81526001600160a01b0382811660048301526060917f0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b9091169063f18858ab9060240160006040518083038186803b15801561391e57600080fd5b505afa158015613932573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107b69190810190615791565b803033148061396e575061396e813361320c565b6139ba5760405162461bcd60e51b815260206004820152601960248201527f534d3a206d75737420626520677561726469616e2f73656c66000000000000006044820152606401610774565b816139c481613e00565b156139e15760405162461bcd60e51b815260040161077490615d0b565b613a1c83613a0f7f000000000000000000000000000000000000000000000000000000000000384042615e40565b63f435f5a760e01b6140e2565b6001600160a01b0383167f6395bace6e0acbe4f22761b149d3cc2e88c7dde6bf4d8481825eef404cf989a1613a717f000000000000000000000000000000000000000000000000000000000000384042615e40565b6040516001600160401b03909116815260200160405180910390a2505050565b8130331480613aa55750613aa58133613c6f565b613ac15760405162461bcd60e51b815260040161077490615cd4565b82613acb81613e00565b15613ae85760405162461bcd60e51b815260040161077490615d0b565b613af484846000614237565b826001600160a01b0316846001600160a01b03167fd288ab5da2e1f37cf384a1565a3f905ad289b092fbdd31950dbbfef148c04f8860405160405180910390a350505050565b6041808202830160208101516040820151919092015160009260ff9190911691601b831480613b6c57508260ff16601c145b613bb85760405162461bcd60e51b815260206004820152601f60248201527f5574696c733a2062616420762076616c756520696e207369676e6174757265006044820152606401610774565b604080516000808252602082018084528a905260ff861692820192909252606081018490526080810183905260019060a0016020604051602081039080840390855afa158015613c0c573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610b935760405162461bcd60e51b815260206004820152601b60248201527f5574696c733a2065637265636f7665722072657475726e6564203000000000006044820152606401610774565b6000816001600160a01b0316836001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015613cb457600080fd5b505afa158015613cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cec9190615536565b6001600160a01b0316149392505050565b6040516309ed185960e11b81526001600160a01b038216906313da30b290613d4b9030907f1626ba7e356f5979dd355a3d2bfb43e80420a480c3b854edce286a82d749686990600401615bb9565b600060405180830381600087803b158015613d6557600080fd5b505af1158015613d79573d6000803e3d6000fd5b50506040516309ed185960e11b81526001600160a01b03841692506313da30b29150613dcb9030907f150b7a023d4804d13e8c85fb27262cb750cf6ba9f9dd3bb30d90f482ceeb4b1f90600401615bb9565b600060405180830381600087803b158015613de557600080fd5b505af1158015613df9573d6000803e3d6000fd5b5050505050565b6001600160a01b03166000908152600160205260409020546001600160401b0342811691161190565b6001600160a01b038216613e7f5760405162461bcd60e51b815260206004820152601860248201527f544d3a20496e76616c69642073657373696f6e207573657200000000000000006044820152606401610774565b6000816001600160401b031611613ed85760405162461bcd60e51b815260206004820152601c60248201527f544d3a20496e76616c69642073657373696f6e206475726174696f6e000000006044820152606401610774565b6000613ef5613ef06001600160401b03841642615e40565b6150cb565b6040805180820182526001600160a01b038681168083526001600160401b0385811660208086018281528c86166000818152808452899020975188549251909516600160a01b026001600160e01b031990921694909616939093179290921790945584519182528101929092529293507f2ecea11087d1dc1431b517cbb5a559a9e33e58a1afeaac288f782c1c8bed8b8a910160405180910390a250505050565b60606000826001600160401b03811115613fc057634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015613ff357816020015b6060815260200190600190039081613fde5790505b50905060005b838110156140d95761409b8686868481811061402557634e487b7160e01b600052603260045260246000fd5b90506020028101906140379190615dac565b61404590602081019061551a565b87878581811061406557634e487b7160e01b600052603260045260246000fd5b90506020028101906140779190615dac565b6020013588888681811061274857634e487b7160e01b600052603260045260246000fd5b8282815181106140bb57634e487b7160e01b600052603260045260246000fd5b602002602001018190525080806140d190615efa565b915050613ff9565b50949350505050565b60405180604001604052806140f6846150cb565b6001600160401b0390811682526001600160e01b03199093166020918201526001600160a01b039094166000908152600185526040902081518154929095015160e01c600160401b026001600160601b031990921694909216939093179290921790915550565b6040516328207dbb60e11b81526001600160a01b0382811660048301526000916107b6917f0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b1690635040fb769060240160206040518083038186803b1580156141c557600080fd5b505afa1580156141d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141fd91906159c6565b60025b60008061420d8385615e80565b90506142198385615f15565b614224579050610891565b61422f816001615e40565b915050610891565b604051631017f7cd60e31b81526001600160a01b0384811660048301528381166024830152604482018390527f000000000000000000000000577e0b01a8538e4cb36e5f202528157f65cdf08a16906380bfbe6890606401611b8d565b6001600160a01b0381166142ea5760405162461bcd60e51b815260206004820152601c60248201527f534d3a206e6577206f776e65722063616e6e6f74206265206e756c6c000000006044820152606401610774565b6142f4828261320c565b156108f95760405162461bcd60e51b815260206004820181905260248201527f534d3a206e6577206f776e65722063616e6e6f7420626520677561726469616e6044820152606401610774565b600060448251106144245760208201516001600160e01b0319811663a9059cbb60e01b148061438057506001600160e01b0319811663095ea7b360e01b145b8061439b57506001600160e01b0319811663a22cb46560e01b145b156143ac5750506024810151610891565b6001600160e01b031981166323b872dd60e01b14806143db57506001600160e01b03198116632142170760e11b145b806143f657506001600160e01b03198116635c46a7ef60e11b145b8061441157506001600160e01b03198116637921219560e11b145b156144225750506044810151610891565b505b5090919050565b60606000856001600160a01b031685858560405160240161444e93929190615bdc565b60408051601f198184030181529181526020820180516001600160e01b03166347b7819960e11b179052516144839190615b0c565b6000604051808303816000865af19150503d80600081146144c0576040519150601f19603f3d011682016040523d82523d6000602084013e6144c5565b606091505b50925090508080156144d8575060008251115b156144f857818060200190518101906144f1919061590f565b91506140d9565b815115614509573d6000803e3d6000fd5b806140d95760405162461bcd60e51b815260206004820152601a60248201527f424d3a2077616c6c657420696e766f6b652072657665727465640000000000006044820152606401610774565b600060248210156145a95760405162461bcd60e51b815260206004820152601660248201527f524d3a20496e76616c6964206461746157616c6c6574000000000000000000006044820152606401610774565b60006145b88360048187615e18565b8101906145c5919061551a565b6001600160a01b03908116908616149150509392505050565b60405160009061460a90601960f81b9083908c908c908c9046908d908d908d908d908d90602001615a72565b60408051601f198184030181529082905280516020918201207f19457468657265756d205369676e6564204d6573736167653a0a33320000000091830191909152603c820152605c0160405160208183030381529060405280519060200120905098975050505050505050565b60008260011480156146d1575060018260048111156146a657634e487b7160e01b600052602160045260246000fd5b14806146d1575060048260048111156146cf57634e487b7160e01b600052602160045260246000fd5b145b15614743576001600160a01b03861660009081526002602052604090205485116146fd57506000611f28565b608085901c61470e61271043615e40565b81111561471f576000915050611f28565b50506001600160a01b03851660009081526002602052604090208490556001611f28565b6001600160a01b038616600090815260026020908152604080832087845260019081019092529091205460ff161515141561478057506000611f28565b5050506001600160a01b0392909216600090815260026020908152604080832094835260019485019091529020805460ff1916831790555090565b6001600160a01b038481166000908152602081815260408083208151808301835290549485168152600160a01b9094046001600160401b0316848301528051601f86018390048302810183019091528481529192918391614838918891889088908190840183828082843760009201829052509250613b3a915050565b905081600001516001600160a01b0316816001600160a01b0316148015610b9357504282602001516001600160401b03161015979650505050505050565b600082516000141561488a57506001612c9c565b6000606060018460048111156148b057634e487b7160e01b600052602160045260246000fd5b1415806148be575060418551115b156149635760405163f18858ab60e01b81526001600160a01b0388811660048301527f0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b169063f18858ab9060240160006040518083038186803b15801561492457600080fd5b505afa158015614938573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526149609190810190615791565b90505b6000805b604187516149759190615e80565b811015614a77576000614989898984613b3a565b905081614a185760018760048111156149b257634e487b7160e01b600052602160045260246000fd5b14156149db576149c28a82613c6f565b156149cd5750614a65565b600095505050505050612c9c565b60028760048111156149fd57634e487b7160e01b600052602160045260246000fd5b1415614a1857614a0d8a82613c6f565b15614a185750614a65565b846001600160a01b0316816001600160a01b031611614a3f57600095505050505050612c9c565b809450614a4c8482614ebb565b9450925082614a6357600095505050505050612c9c565b505b80614a6f81615efa565b915050614967565b50600198975050505050505050565b600086118015614ade57506001816004811115614ab357634e487b7160e01b600052602160045260246000fd5b1480614ade57506004816004811115614adc57634e487b7160e01b600052602160045260246000fd5b145b15611d5e5760006001600160a01b03841615614afa5783614afc565b335b9050826001148015614b2d57506001826004811115614b2b57634e487b7160e01b600052602160045260246000fd5b145b15614ce95760408051602081018252600080825291516305cfdc4d60e41b81526001600160a01b037f000000000000000000000000b5ecc8ab46e2e20573c2e57c865f7c97f58c27981692635cfdc4d092614b90928e9287929091600401615b7b565b60206040518083038186803b158015614ba857600080fd5b505afa158015614bbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614be09190615844565b614ce9576040516309fa507560e11b81526001600160a01b038a8116600483015282811660248301526000917f000000000000000000000000577e0b01a8538e4cb36e5f202528157f65cdf08a909116906313f4a0ea9060440160206040518083038186803b158015614c5257600080fd5b505afa158015614c66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c8a91906159c6565b9050600081118015614c9b57504281105b614ce75760405162461bcd60e51b815260206004820152601960248201527f524d3a20726566756e64206e6f7420617574686f7269736564000000000000006044820152606401610774565b505b60006001600160a01b038616614d565760005a614d06908b615eb3565b614d12906159d8615e40565b9050614d1e893a615133565b614d28828a615133565b614d329190615e94565b9150614d4f8b84846040518060200160405280600081525061442b565b5050614e64565b60005a614d63908b615eb3565b614d6f9061927c615e40565b90506000614d7d883a615142565b9050614d898a82615133565b614d93838b615133565b614d9d9190615e94565b604080516001600160a01b038716602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790529093506000614df58e8b838561442b565b805190915015614e5f5780806020019051810190614e139190615844565b614e5f5760405162461bcd60e51b815260206004820152601a60248201527f524d3a20526566756e64207472616e73666572206661696c65640000000000006044820152606401610774565b505050505b604080516001600160a01b0388811682526020820184905280851692908d16917f22edd2bbb0b0afbdcf90d91da8a5e2100f8d8f67cdc766dee1742e9a36d6add3910160405180910390a350505050505050505050565b60006060835160001480614ed657506001600160a01b038316155b15614ee6575060009050826150c4565b60008060018651614ef79190615eb3565b6001600160401b03811115614f1c57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015614f45578160200160208202803683370190505b5090506000805b87518110156150aa578361502157878181518110614f7a57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316876001600160a01b03161415614fa45760019350615098565b614fdb888281518110614fc757634e487b7160e01b600052603260045260246000fd5b60200260200101513b63ffffffff16151590565b8015615013575061501388828151811061500557634e487b7160e01b600052603260045260246000fd5b60200260200101518861516a565b156150215760019350615098565b82518210156150985787818151811061504a57634e487b7160e01b600052603260045260246000fd5b602002602001015183838151811061507257634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101528161509481615efa565b9250505b806150a281615efa565b915050614f4c565b50826150b8576000876150bc565b6001825b945094505050505b9250929050565b6000600160401b821061512f5760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610774565b5090565b600081831061442457816132b2565b6000806000615150856151b4565b9092509050816151608286615e94565b611f289190615e80565b604051638da5cb5b60e01b815260009081906020818181886161a8fa600181141561519457815192505b5050826001600160a01b0316816001600160a01b03161491505092915050565b600080826001600160a01b03167f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031610156152a857600061521d7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2856153a7565b9050806001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561525857600080fd5b505afa15801561526c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906152909190615978565b506001600160701b03918216945016915061535a9050565b60006152d4847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26153a7565b9050806001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561530f57600080fd5b505afa158015615323573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906153479190615978565b506001600160701b039081169450169150505b811580159061536857508015155b6121d75760405162461bcd60e51b815260206004820152601060248201526f534f3a206e6f206c697175696469747960801b6044820152606401610774565b6040516001600160601b0319606084811b8216602084015283901b1660348201526000907f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f90604801604051602081830303815290604052805190602001206040516020016154639291906001600160f81b0319815260609290921b6001600160601b031916600183015260158201527f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f603582015260550190565b60408051601f1981840301815291905280516020909101209392505050565b60008083601f840112615493578182fd5b5081356001600160401b038111156154a9578182fd5b6020830191508360208260051b85010111156150c457600080fd5b60008083601f8401126154d5578182fd5b5081356001600160401b038111156154eb578182fd5b6020830191508360208285010111156150c457600080fd5b80516001600160701b03811681146107b957600080fd5b60006020828403121561552b578081fd5b81356132b281615f6b565b600060208284031215615547578081fd5b81516132b281615f6b565b60008060408385031215615564578081fd5b823561556f81615f6b565b9150602083013561557f81615f6b565b809150509250929050565b60008060006040848603121561559e578081fd5b83356155a981615f6b565b925060208401356001600160401b038111156155c3578182fd5b6155cf86828701615482565b9497909650939450505050565b6000806000806000608086880312156155f3578081fd5b85356155fe81615f6b565b945060208601356001600160401b0380821115615619578283fd5b61562589838a01615482565b90965094506040880135915061563a82615f6b565b9092506060870135908082168214615650578283fd5b50809150509295509295909350565b60008060408385031215615671578182fd5b823561567c81615f6b565b946020939093013593505050565b60008060006040848603121561569e578081fd5b83356156a981615f6b565b925060208401356001600160401b038111156156c3578182fd5b6155cf868287016154c4565b6000806000806000806000806000806101008b8d0312156156ee578788fd5b8a356156f981615f6b565b995060208b01356001600160401b038082111561571457898afd5b6157208e838f016154c4565b909b50995060408d0135985060608d013591508082111561573f578687fd5b5061574c8d828e016154c4565b90975095505060808b0135935060a08b0135925060c08b013561576e81615f6b565b915060e08b013561577e81615f6b565b809150509295989b9194979a5092959850565b600060208083850312156157a3578182fd5b82516001600160401b03808211156157b9578384fd5b818501915085601f8301126157cc578384fd5b8151818111156157de576157de615f55565b8060051b91506157ef848301615dc1565b8181528481019084860184860187018a1015615809578788fd5b8795505b83861015615837578051945061582285615f6b565b8483526001959095019491860191860161580d565b5098975050505050505050565b600060208284031215615855578081fd5b815180151581146132b2578182fd5b60008060408385031215615876578182fd5b8235915060208301356001600160401b03811115615892578182fd5b8301601f810185136158a2578182fd5b80356158b56158b082615df1565b615dc1565b8181528660208385010111156158c9578384fd5b81602084016020830137908101602001929092525090939092509050565b6000602082840312156158f8578081fd5b81356001600160e01b0319811681146132b2578182fd5b600060208284031215615920578081fd5b81516001600160401b03811115615935578182fd5b8201601f81018413615945578182fd5b80516159536158b082615df1565b818152856020838501011115615967578384fd5b611f28826020830160208601615eca565b60008060006060848603121561598c578081fd5b61599584615503565b92506159a360208501615503565b9150604084015163ffffffff811681146159bb578182fd5b809150509250925092565b6000602082840312156159d7578081fd5b5051919050565b600081518084526159f6816020860160208601615eca565b601f01601f19169290920160200192915050565b6001600160601b0319606093841b811682529190921b166014820152693932bb37b5b0ba34b7b760b11b602882015260320190565b6001600160601b0319606093841b811682529190921b1660148201526730b23234ba34b7b760c11b602882015260300190565b600060ff60f81b808e168352808d166001840152506bffffffffffffffffffffffff19808c60601b1660028401528a60168401528951615ab9816036860160208e01615eca565b909201603681019890985250605687019590955260768601939093526096850191909152606090811b831660b68501521b1660ca82015260de0195945050505050565b6000828483379101908152919050565b60008251615b1e818460208701615eca565b9190910192915050565b60006001600160a01b038088168352808716602084015280861660408401525060806060830152826080830152828460a084013781830160a090810191909152601f909201601f19160101949350505050565b60006001600160a01b038087168352808616602084015280851660408401525060806060830152615baf60808301846159de565b9695505050505050565b6001600160a01b039290921682526001600160e01b031916602082015260400190565b60006001600160a01b038516825283602083015260606040830152611f2860608301846159de565b6020808252825182820181905260009190848201906040850190845b81811015615c455783516001600160a01b031683529284019291840191600101615c20565b50909695505050505050565b6000602080830181845280855180835260408601915060408160051b8701019250838701855b82811015615ca557603f19888603018452615c938583516159de565b94509285019290850190600101615c77565b5092979650505050505050565b600060408252615cc560408301856159de565b90508260208301529392505050565b6020808252601d908201527f424d3a206d7573742062652077616c6c6574206f776e65722f73656c66000000604082015260600190565b60208082526011908201527010934e881dd85b1b195d081b1bd8dad959607a1b604082015260600190565b8281526040810160058310615d5b57634e487b7160e01b600052602160045260246000fd5b8260208301529392505050565b6000808335601e19843603018112615d7e578283fd5b8301803591506001600160401b03821115615d97578283fd5b6020019150368190038213156150c457600080fd5b60008235605e19833603018112615b1e578182fd5b604051601f8201601f191681016001600160401b0381118282101715615de957615de9615f55565b604052919050565b60006001600160401b03821115615e0a57615e0a615f55565b50601f01601f191660200190565b60008085851115615e27578182fd5b83861115615e33578182fd5b5050820193919092039150565b60008219821115615e5357615e53615f29565b500190565b600063ffffffff808316818516808303821115615e7757615e77615f29565b01949350505050565b600082615e8f57615e8f615f3f565b500490565b6000816000190483118215151615615eae57615eae615f29565b500290565b600082821015615ec557615ec5615f29565b500390565b60005b83811015615ee5578181015183820152602001615ecd565b83811115615ef4576000848401525b50505050565b6000600019821415615f0e57615f0e615f29565b5060010190565b600082615f2457615f24615f3f565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114615f8057600080fd5b5056fea264697066735822122081d4fd652d7092f6bc4fbd840c7ca9e79421eccf5ef61d2be2b36c17356df9f564736f6c63430008030033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000008ff41919435d50f113afd5bc25b88acf4cc3d8cc0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b000000000000000000000000577e0b01a8538e4cb36e5f202528157f65cdf08a000000000000000000000000b5ecc8ab46e2e20573c2e57c865f7c97f58c27980000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d0000000000000000000000000000000000000000000000000000000000000e100000000000000000000000000000000000000000000000000000000000000e100000000000000000000000000000000000000000000000000000000000001c200000000000000000000000000000000000000000000000000000000000003840

-----Decoded View---------------
Arg [0] : _registry (address): 0x8FF41919435D50f113aFd5bC25b88aCF4Cc3D8Cc
Arg [1] : _guardianStorage (address): 0x4cAC0996Ede3125A72bE96942D299B1b26e5381b
Arg [2] : _userWhitelist (address): 0x577e0B01A8538e4cb36E5f202528157f65cDF08A
Arg [3] : _authoriser (address): 0xB5ecC8ab46e2E20573C2e57C865F7c97f58c2798
Arg [4] : _uniswapRouter (address): 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
Arg [5] : _securityPeriod (uint256): 3600
Arg [6] : _securityWindow (uint256): 3600
Arg [7] : _recoveryPeriod (uint256): 7200
Arg [8] : _lockPeriod (uint256): 14400

-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 0000000000000000000000008ff41919435d50f113afd5bc25b88acf4cc3d8cc
Arg [1] : 0000000000000000000000004cac0996ede3125a72be96942d299b1b26e5381b
Arg [2] : 000000000000000000000000577e0b01a8538e4cb36e5f202528157f65cdf08a
Arg [3] : 000000000000000000000000b5ecc8ab46e2e20573c2e57c865f7c97f58c2798
Arg [4] : 0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000e10
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000e10
Arg [7] : 0000000000000000000000000000000000000000000000000000000000001c20
Arg [8] : 0000000000000000000000000000000000000000000000000000000000003840


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.