FRAX Price: $0.87 (+9.05%)

Contract

0xb28Dd740D27853A91639795223AB409088A73E23

Overview

FRAX Balance | FXTL Balance

0 FRAX | 91 FXTL

FRAX Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

1 Internal Transaction and 1 Token Transfer found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
284006722025-11-20 4:14:1571 days ago1763612055  Contract Creation0 FRAX

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
WhitelistManagerFacet

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
london EvmVersion
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { LibDiamond } from "../Libraries/LibDiamond.sol";
import { LibAccess } from "../Libraries/LibAccess.sol";
import { LibAllowList } from "../Libraries/LibAllowList.sol";
import { IWhitelistManagerFacet } from "../Interfaces/IWhitelistManagerFacet.sol";
import { CannotAuthoriseSelf, InvalidConfig } from "../Errors/GenericErrors.sol";

/// @title WhitelistManagerFacet
/// @author LI.FI (https://li.fi)
/// @notice Facet contract for managing whitelisted addresses for various protocol interactions.
/// @custom:version 1.0.0
contract WhitelistManagerFacet is IWhitelistManagerFacet {
    /// External Methods ///

    /// @inheritdoc IWhitelistManagerFacet
    function setContractSelectorWhitelist(
        address _contract,
        bytes4 _selector,
        bool _whitelisted
    ) external {
        if (msg.sender != LibDiamond.contractOwner()) {
            LibAccess.enforceAccessControl();
        }
        _setContractSelectorWhitelist(_contract, _selector, _whitelisted);
    }

    /// @inheritdoc IWhitelistManagerFacet
    function batchSetContractSelectorWhitelist(
        address[] calldata _contracts,
        bytes4[] calldata _selectors,
        bool _whitelisted
    ) external {
        if (msg.sender != LibDiamond.contractOwner()) {
            LibAccess.enforceAccessControl();
        }
        if (_contracts.length != _selectors.length) {
            revert InvalidConfig();
        }
        for (uint256 i = 0; i < _contracts.length; ) {
            _setContractSelectorWhitelist(
                _contracts[i],
                _selectors[i],
                _whitelisted
            );
            unchecked {
                ++i;
            }
        }
    }

    /// @inheritdoc IWhitelistManagerFacet
    function isContractSelectorWhitelisted(
        address _contract,
        bytes4 _selector
    ) external view returns (bool whitelisted) {
        return LibAllowList.contractSelectorIsAllowed(_contract, _selector);
    }

    /// @inheritdoc IWhitelistManagerFacet
    function isFunctionSelectorWhitelisted(
        bytes4 _selector
    ) external view returns (bool whitelisted) {
        return LibAllowList.selectorIsAllowed(_selector);
    }

    /// @inheritdoc IWhitelistManagerFacet
    function getWhitelistedAddresses()
        external
        view
        returns (address[] memory addresses)
    {
        return LibAllowList.getAllowedContracts();
    }

    /// @inheritdoc IWhitelistManagerFacet
    function isAddressWhitelisted(
        address _address
    ) external view returns (bool whitelisted) {
        return LibAllowList.contractIsAllowed(_address);
    }

    /// @inheritdoc IWhitelistManagerFacet
    function getWhitelistedFunctionSelectors()
        external
        view
        returns (bytes4[] memory selectors)
    {
        return LibAllowList.getAllowedSelectors();
    }

    /// @inheritdoc IWhitelistManagerFacet
    function getWhitelistedSelectorsForContract(
        address _contract
    ) external view returns (bytes4[] memory selectors) {
        return LibAllowList.getWhitelistedSelectorsForContract(_contract);
    }

    /// @inheritdoc IWhitelistManagerFacet
    function getAllContractSelectorPairs()
        external
        view
        returns (address[] memory contracts, bytes4[][] memory selectors)
    {
        // Get all whitelisted contracts
        contracts = LibAllowList.getAllowedContracts();

        // Initialize selectors array with same length as contracts
        selectors = new bytes4[][](contracts.length);

        // For each contract, get its whitelisted selectors
        for (uint256 i = 0; i < contracts.length; ) {
            selectors[i] = LibAllowList.getWhitelistedSelectorsForContract(
                contracts[i]
            );
            unchecked {
                ++i;
            }
        }
    }

    /// @dev Remove these methods after migration is complete in next facet upgrade.
    /// @inheritdoc IWhitelistManagerFacet
    function migrate(
        bytes4[] calldata _selectorsToRemove,
        address[] calldata _contracts,
        bytes4[][] calldata _selectors
    ) external {
        if (msg.sender != LibDiamond.contractOwner()) {
            LibAccess.enforceAccessControl();
        }

        LibAllowList.AllowListStorage storage als = _getAllowListStorage();

        // return early if already migrated
        if (als.migrated) return;

        // Validate input arrays have matching lengths
        if (_contracts.length != _selectors.length) {
            revert InvalidConfig();
        }

        // clear old state
        // reset contractAllowList
        uint256 i;
        uint256 length = als.contracts.length;
        for (; i < length; ) {
            delete als.contractAllowList[als.contracts[i]];
            unchecked {
                ++i;
            }
        }

        // reset selectorAllowList with external selectors array because new selectors array does not exist yet
        i = 0;
        length = _selectorsToRemove.length;
        for (; i < length; ) {
            delete als.selectorAllowList[_selectorsToRemove[i]];
            unchecked {
                ++i;
            }
        }

        // reset contract array
        delete als.contracts;
        // clearing selectors (als.selectors) is not needed as it's a new variable

        // whitelist contract-selector pairs
        i = 0;
        length = _contracts.length;
        for (; i < length; ) {
            address contractAddr = _contracts[i];
            bytes4[] calldata contractSelectors = _selectors[i];

            if (contractAddr == address(this)) {
                revert CannotAuthoriseSelf();
            }

            // whitelist each selector for this contract
            for (uint256 j = 0; j < contractSelectors.length; ) {
                bytes4 selector = contractSelectors[j];

                // check for duplicate contract-selector pairs
                if (
                    LibAllowList.contractSelectorIsAllowed(
                        contractAddr,
                        selector
                    )
                ) {
                    revert InvalidConfig();
                }

                LibAllowList.addAllowedContractSelector(
                    contractAddr,
                    selector
                );
                emit ContractSelectorWhitelistChanged(
                    contractAddr,
                    selector,
                    true
                );
                unchecked {
                    ++j;
                }
            }
            unchecked {
                ++i;
            }
        }

        // Mark as migrated
        als.migrated = true;
    }

    /// @dev Remove these methods after migration is complete in next facet upgrade.
    /// @inheritdoc IWhitelistManagerFacet
    function isMigrated() external view returns (bool) {
        LibAllowList.AllowListStorage storage als = _getAllowListStorage();
        return als.migrated;
    }

    /// Internal Logic ///

    /// @dev Remove these methods after migration is complete in next facet upgrade.
    /// @dev Fetch allow list storage struct
    function _getAllowListStorage()
        internal
        pure
        returns (LibAllowList.AllowListStorage storage als)
    {
        bytes32 position = LibAllowList.NAMESPACE;
        assembly {
            als.slot := position
        }
    }

    /// @dev The single internal function that all state changes must flow through.
    function _setContractSelectorWhitelist(
        address _contract,
        bytes4 _selector,
        bool _whitelisted
    ) internal {
        if (_contract == address(this)) {
            revert CannotAuthoriseSelf();
        }
        // Check current status to prevent redundant operations and emitting unnecessary events.
        bool isCurrentlyWhitelisted = LibAllowList.contractSelectorIsAllowed(
            _contract,
            _selector
        );
        if (isCurrentlyWhitelisted == _whitelisted) {
            return;
        }

        if (_whitelisted) {
            LibAllowList.addAllowedContractSelector(_contract, _selector);
        } else {
            LibAllowList.removeAllowedContractSelector(_contract, _selector);
        }
        emit ContractSelectorWhitelistChanged(
            _contract,
            _selector,
            _whitelisted
        );
    }
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { LibDiamond } from "../Libraries/LibDiamond.sol";
import { LibUtil } from "../Libraries/LibUtil.sol";
import { OnlyContractOwner } from "../Errors/GenericErrors.sol";

/// @title LibDiamond
/// @custom:version 1.0.0
/// @notice This library implements the EIP-2535 Diamond Standard
library LibDiamond {
    bytes32 internal constant DIAMOND_STORAGE_POSITION =
        keccak256("diamond.standard.diamond.storage");

    // Diamond specific errors
    error IncorrectFacetCutAction();
    error NoSelectorsInFace();
    error FunctionAlreadyExists();
    error FacetAddressIsZero();
    error FacetAddressIsNotZero();
    error FacetContainsNoCode();
    error FunctionDoesNotExist();
    error FunctionIsImmutable();
    error InitZeroButCalldataNotEmpty();
    error CalldataEmptyButInitNotZero();
    error InitReverted();
    // ----------------

    struct FacetAddressAndPosition {
        address facetAddress;
        uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
    }

    struct FacetFunctionSelectors {
        bytes4[] functionSelectors;
        uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
    }

    struct DiamondStorage {
        // maps function selector to the facet address and
        // the position of the selector in the facetFunctionSelectors.selectors array
        mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
        // maps facet addresses to function selectors
        mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
        // facet addresses
        address[] facetAddresses;
        // Used to query if a contract implements an interface.
        // Used to implement ERC-165.
        mapping(bytes4 => bool) supportedInterfaces;
        // owner of the contract
        address contractOwner;
    }

    enum FacetCutAction {
        Add,
        Replace,
        Remove
    }
    // Add=0, Replace=1, Remove=2

    struct FacetCut {
        address facetAddress;
        FacetCutAction action;
        bytes4[] functionSelectors;
    }

    function diamondStorage()
        internal
        pure
        returns (DiamondStorage storage ds)
    {
        bytes32 position = DIAMOND_STORAGE_POSITION;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            ds.slot := position
        }
    }

    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);

    function setContractOwner(address _newOwner) internal {
        DiamondStorage storage ds = diamondStorage();
        address previousOwner = ds.contractOwner;
        ds.contractOwner = _newOwner;
        emit OwnershipTransferred(previousOwner, _newOwner);
    }

    function contractOwner() internal view returns (address contractOwner_) {
        contractOwner_ = diamondStorage().contractOwner;
    }

    function enforceIsContractOwner() internal view {
        if (msg.sender != diamondStorage().contractOwner)
            revert OnlyContractOwner();
    }

    // Internal function version of diamondCut
    function diamondCut(
        FacetCut[] memory _diamondCut,
        address _init,
        bytes memory _calldata
    ) internal {
        for (uint256 facetIndex; facetIndex < _diamondCut.length; ) {
            LibDiamond.FacetCutAction action = _diamondCut[facetIndex].action;
            if (action == LibDiamond.FacetCutAction.Add) {
                addFunctions(
                    _diamondCut[facetIndex].facetAddress,
                    _diamondCut[facetIndex].functionSelectors
                );
            } else if (action == LibDiamond.FacetCutAction.Replace) {
                replaceFunctions(
                    _diamondCut[facetIndex].facetAddress,
                    _diamondCut[facetIndex].functionSelectors
                );
            } else if (action == LibDiamond.FacetCutAction.Remove) {
                removeFunctions(
                    _diamondCut[facetIndex].facetAddress,
                    _diamondCut[facetIndex].functionSelectors
                );
            } else {
                revert IncorrectFacetCutAction();
            }
            unchecked {
                ++facetIndex;
            }
        }
        emit DiamondCut(_diamondCut, _init, _calldata);
        initializeDiamondCut(_init, _calldata);
    }

    function addFunctions(
        address _facetAddress,
        bytes4[] memory _functionSelectors
    ) internal {
        if (_functionSelectors.length == 0) {
            revert NoSelectorsInFace();
        }
        DiamondStorage storage ds = diamondStorage();
        if (LibUtil.isZeroAddress(_facetAddress)) {
            revert FacetAddressIsZero();
        }
        uint96 selectorPosition = uint96(
            ds.facetFunctionSelectors[_facetAddress].functionSelectors.length
        );
        // add new facet address if it does not exist
        if (selectorPosition == 0) {
            addFacet(ds, _facetAddress);
        }
        for (
            uint256 selectorIndex;
            selectorIndex < _functionSelectors.length;

        ) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds
                .selectorToFacetAndPosition[selector]
                .facetAddress;
            if (!LibUtil.isZeroAddress(oldFacetAddress)) {
                revert FunctionAlreadyExists();
            }
            addFunction(ds, selector, selectorPosition, _facetAddress);
            unchecked {
                ++selectorPosition;
                ++selectorIndex;
            }
        }
    }

    function replaceFunctions(
        address _facetAddress,
        bytes4[] memory _functionSelectors
    ) internal {
        if (_functionSelectors.length == 0) {
            revert NoSelectorsInFace();
        }
        DiamondStorage storage ds = diamondStorage();
        if (LibUtil.isZeroAddress(_facetAddress)) {
            revert FacetAddressIsZero();
        }
        uint96 selectorPosition = uint96(
            ds.facetFunctionSelectors[_facetAddress].functionSelectors.length
        );
        // add new facet address if it does not exist
        if (selectorPosition == 0) {
            addFacet(ds, _facetAddress);
        }
        for (
            uint256 selectorIndex;
            selectorIndex < _functionSelectors.length;

        ) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds
                .selectorToFacetAndPosition[selector]
                .facetAddress;
            if (oldFacetAddress == _facetAddress) {
                revert FunctionAlreadyExists();
            }
            removeFunction(ds, oldFacetAddress, selector);
            addFunction(ds, selector, selectorPosition, _facetAddress);
            unchecked {
                ++selectorPosition;
                ++selectorIndex;
            }
        }
    }

    function removeFunctions(
        address _facetAddress,
        bytes4[] memory _functionSelectors
    ) internal {
        if (_functionSelectors.length == 0) {
            revert NoSelectorsInFace();
        }
        DiamondStorage storage ds = diamondStorage();
        // if function does not exist then do nothing and return
        if (!LibUtil.isZeroAddress(_facetAddress)) {
            revert FacetAddressIsNotZero();
        }
        for (
            uint256 selectorIndex;
            selectorIndex < _functionSelectors.length;

        ) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds
                .selectorToFacetAndPosition[selector]
                .facetAddress;
            removeFunction(ds, oldFacetAddress, selector);
            unchecked {
                ++selectorIndex;
            }
        }
    }

    function addFacet(
        DiamondStorage storage ds,
        address _facetAddress
    ) internal {
        enforceHasContractCode(_facetAddress);
        ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds
            .facetAddresses
            .length;
        ds.facetAddresses.push(_facetAddress);
    }

    function addFunction(
        DiamondStorage storage ds,
        bytes4 _selector,
        uint96 _selectorPosition,
        address _facetAddress
    ) internal {
        ds
            .selectorToFacetAndPosition[_selector]
            .functionSelectorPosition = _selectorPosition;
        ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(
            _selector
        );
        ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
    }

    function removeFunction(
        DiamondStorage storage ds,
        address _facetAddress,
        bytes4 _selector
    ) internal {
        if (LibUtil.isZeroAddress(_facetAddress)) {
            revert FunctionDoesNotExist();
        }
        // an immutable function is a function defined directly in a diamond
        if (_facetAddress == address(this)) {
            revert FunctionIsImmutable();
        }
        // replace selector with last selector, then delete last selector
        uint256 selectorPosition = ds
            .selectorToFacetAndPosition[_selector]
            .functionSelectorPosition;
        uint256 lastSelectorPosition = ds
            .facetFunctionSelectors[_facetAddress]
            .functionSelectors
            .length - 1;
        // if not the same then replace _selector with lastSelector
        if (selectorPosition != lastSelectorPosition) {
            bytes4 lastSelector = ds
                .facetFunctionSelectors[_facetAddress]
                .functionSelectors[lastSelectorPosition];
            ds.facetFunctionSelectors[_facetAddress].functionSelectors[
                selectorPosition
            ] = lastSelector;
            ds
                .selectorToFacetAndPosition[lastSelector]
                .functionSelectorPosition = uint96(selectorPosition);
        }
        // delete the last selector
        ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
        delete ds.selectorToFacetAndPosition[_selector];

        // if no more selectors for facet address then delete the facet address
        if (lastSelectorPosition == 0) {
            // replace facet address with last facet address and delete last facet address
            uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
            uint256 facetAddressPosition = ds
                .facetFunctionSelectors[_facetAddress]
                .facetAddressPosition;
            if (facetAddressPosition != lastFacetAddressPosition) {
                address lastFacetAddress = ds.facetAddresses[
                    lastFacetAddressPosition
                ];
                ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
                ds
                    .facetFunctionSelectors[lastFacetAddress]
                    .facetAddressPosition = facetAddressPosition;
            }
            ds.facetAddresses.pop();
            delete ds
                .facetFunctionSelectors[_facetAddress]
                .facetAddressPosition;
        }
    }

    function initializeDiamondCut(
        address _init,
        bytes memory _calldata
    ) internal {
        if (LibUtil.isZeroAddress(_init)) {
            if (_calldata.length != 0) {
                revert InitZeroButCalldataNotEmpty();
            }
        } else {
            if (_calldata.length == 0) {
                revert CalldataEmptyButInitNotZero();
            }
            if (_init != address(this)) {
                enforceHasContractCode(_init);
            }
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory error) = _init.delegatecall(_calldata);
            if (!success) {
                if (error.length > 0) {
                    // bubble up the error
                    revert(string(error));
                } else {
                    revert InitReverted();
                }
            }
        }
    }

    function enforceHasContractCode(address _contract) internal view {
        uint256 contractSize;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            contractSize := extcodesize(_contract)
        }
        if (contractSize == 0) {
            revert FacetContainsNoCode();
        }
    }
}

// SPDX-License-Identifier: LGPL-3.0-only
/// @custom:version 1.0.0
pragma solidity ^0.8.17;

import { CannotAuthoriseSelf, UnAuthorized } from "../Errors/GenericErrors.sol";

/// @title Access Library
/// @author LI.FI (https://li.fi)
/// @notice Provides functionality for managing method level access control
library LibAccess {
    /// Types ///
    bytes32 internal constant NAMESPACE =
        keccak256("com.lifi.library.access.management");

    /// Storage ///
    struct AccessStorage {
        mapping(bytes4 => mapping(address => bool)) execAccess;
    }

    /// Events ///
    event AccessGranted(address indexed account, bytes4 indexed method);
    event AccessRevoked(address indexed account, bytes4 indexed method);

    /// @dev Fetch local storage
    function accessStorage()
        internal
        pure
        returns (AccessStorage storage accStor)
    {
        bytes32 position = NAMESPACE;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            accStor.slot := position
        }
    }

    /// @notice Gives an address permission to execute a method
    /// @param selector The method selector to execute
    /// @param executor The address to grant permission to
    function addAccess(bytes4 selector, address executor) internal {
        if (executor == address(this)) {
            revert CannotAuthoriseSelf();
        }
        AccessStorage storage accStor = accessStorage();
        accStor.execAccess[selector][executor] = true;
        emit AccessGranted(executor, selector);
    }

    /// @notice Revokes permission to execute a method
    /// @param selector The method selector to execute
    /// @param executor The address to revoke permission from
    function removeAccess(bytes4 selector, address executor) internal {
        AccessStorage storage accStor = accessStorage();
        accStor.execAccess[selector][executor] = false;
        emit AccessRevoked(executor, selector);
    }

    /// @notice Enforces access control by reverting if `msg.sender`
    ///     has not been given permission to execute `msg.sig`
    function enforceAccessControl() internal view {
        AccessStorage storage accStor = accessStorage();
        if (accStor.execAccess[msg.sig][msg.sender] != true)
            revert UnAuthorized();
    }
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { InvalidContract, InvalidCallData } from "../Errors/GenericErrors.sol";
import { LibAsset } from "./LibAsset.sol";

/// @title LibAllowList
/// @author LI.FI (https://li.fi)
/// @notice Manages a dual-model allow list to support a secure, granular permissions system
/// while maintaining backward compatibility with a non-granular, global system.
/// @dev This library is the single source of truth for all whitelist state changes.
/// It ensures that both the new granular mapping and the global arrays used by older
/// contracts are kept perfectly synchronized. The long-term goal is to migrate all usage
/// to the new granular system. New development should exclusively use the "Primary Interface" functions.
///
/// Special ApproveTo-Only Selector:
/// - Use 0xffffffff to whitelist contracts that are used only as approveTo in
///   LibSwap.SwapData, without allowing function calls to them.
/// - Some DEXs have specific contracts that need to be approved to while another
///   (router) contract must be called to initiate the swap.
/// - This selector makes contractIsAllowed(_contract) return true for backward
///   compatibility, but does not authorize any granular calls. In the granular
///   system, real function selectors must be explicitly whitelisted to be callable.
/// @custom:version 2.0.0
library LibAllowList {
    /// Storage ///

    bytes32 internal constant NAMESPACE =
        keccak256("com.lifi.library.allow.list");

    struct AllowListStorage {
        // --- STORAGE FOR OLDER VERSIONS ---
        /// @dev [BACKWARD COMPATIBILITY] [V1 DATA] Boolean mapping actively maintained
        /// by functions to support older, deployed contracts that read from it.
        /// Also kept for storage layout compatibility.
        mapping(address => bool) contractAllowList;
        /// @dev [BACKWARD COMPATIBILITY] [V1 DATA] Boolean mapping actively maintained
        /// by functions to support older, deployed contracts that read from it.
        /// Also kept for storage layout compatibility.
        mapping(bytes4 => bool) selectorAllowList;
        /// @dev [BACKWARD COMPATIBILITY] The global list of all unique whitelisted contracts for older facets.
        address[] contracts;
        // --- NEW GRANULAR STORAGE & SYNCHRONIZATION ---
        // These variables form the new, secure, and preferred whitelist system.

        /// @dev [BACKWARD COMPATIBILITY] 1-based index for `contracts` array for efficient removal.
        mapping(address => uint256) contractToIndex;
        /// @dev [BACKWARD COMPATIBILITY] 1-based index for `selectors` array for efficient removal.
        mapping(bytes4 => uint256) selectorToIndex;
        /// @dev [BACKWARD COMPATIBILITY] The global list of all unique whitelisted selectors for older facets.
        bytes4[] selectors;
        /// @dev The SOURCE OF TRUTH for the new granular system.
        mapping(address => mapping(bytes4 => bool)) contractSelectorAllowList;
        /// @dev A global reference count for each selector to manage the `selectors` array for backward compatibility.
        mapping(bytes4 => uint256) selectorReferenceCount;
        /// @dev Iterable list of selectors for each contract, used by the backend getter.
        /// The length of this array also serves as the IMPLICIT contract reference count.
        mapping(address => bytes4[]) whitelistedSelectorsByContract;
        /// @dev 1-based index for `whitelistedSelectorsByContract` array for efficient removal.
        mapping(address => mapping(bytes4 => uint256)) selectorIndices;
        /// @dev Flag to indicate completion of a one-time data migration.
        bool migrated;
    }

    /// @notice Adds a specific contract-selector pair to the allow list.
    /// @dev This is the primary entry point for whitelisting. It updates the granular
    /// mapping and synchronizes the global arrays (for backward compatibility) via reference counting.
    /// @param _contract The contract address.
    /// @param _selector The function selector.
    function addAllowedContractSelector(
        address _contract,
        bytes4 _selector
    ) internal {
        if (_contract == address(0) || _selector == bytes4(0))
            revert InvalidCallData();
        AllowListStorage storage als = _getStorage();

        // Skip if the pair is already allowed.
        if (als.contractSelectorAllowList[_contract][_selector]) return;

        // 1. Update the source of truth for the new system.
        als.contractSelectorAllowList[_contract][_selector] = true;

        // 2. Update the `contracts` variables if this is the first selector for this contract.
        // We use the length of the iterable array as an implicit reference count.
        if (als.whitelistedSelectorsByContract[_contract].length == 0) {
            _addAllowedContract(_contract);
        }

        // 3. Update the `selectors` variables if this is the first time this selector is used globally.
        if (++als.selectorReferenceCount[_selector] == 1) {
            _addAllowedSelector(_selector);
        }

        // 4. Update the iterable list used by the on-chain getter.
        als.whitelistedSelectorsByContract[_contract].push(_selector);
        // Store 1-based index for efficient removal later.
        als.selectorIndices[_contract][_selector] = als
            .whitelistedSelectorsByContract[_contract]
            .length;
    }

    /// @notice Removes a specific contract-selector pair from the allow list.
    /// @dev This is the primary entry point for removal. It updates the granular
    /// mapping and synchronizes the global arrays (for backward compatibility) via reference counting.
    /// @param _contract The contract address.
    /// @param _selector The function selector.
    function removeAllowedContractSelector(
        address _contract,
        bytes4 _selector
    ) internal {
        AllowListStorage storage als = _getStorage();
        // Skip if the pair is not currently allowed.
        if (!als.contractSelectorAllowList[_contract][_selector]) return;

        // 1. Update the source of truth.
        delete als.contractSelectorAllowList[_contract][_selector];

        // 2. Update the iterable list FIRST to get the new length.
        _removeSelectorFromIterableList(_contract, _selector);

        // 3. If the iterable list's new length is 0, it was the last selector,
        // so remove the contract from the global list.
        if (als.whitelistedSelectorsByContract[_contract].length == 0) {
            _removeAllowedContract(_contract);
        }

        // 4. If the global reference count is now 0, it was the last usage of this
        // selector, so remove it from the global list.
        if (--als.selectorReferenceCount[_selector] == 0) {
            _removeAllowedSelector(_selector);
        }
    }

    /// @notice Checks if a specific contract-selector pair is allowed.
    /// @dev Preferred runtime check for all new contracts/facets.
    /// @param _contract The contract address.
    /// @param _selector The function selector.
    /// @return isAllowed True if the contract-selector pair is allowed, false otherwise.
    function contractSelectorIsAllowed(
        address _contract,
        bytes4 _selector
    ) internal view returns (bool) {
        return _getStorage().contractSelectorAllowList[_contract][_selector];
    }

    /// @notice Gets all approved selectors for a specific contract.
    /// @dev Used by the on-chain getter in the facet for backend synchronization.
    /// @param _contract The contract address.
    /// @return selectors The whitelisted selectors for the contract.
    function getWhitelistedSelectorsForContract(
        address _contract
    ) internal view returns (bytes4[] memory) {
        return _getStorage().whitelistedSelectorsByContract[_contract];
    }

    /// Backward Compatibility Interface (V1) ///

    // These functions read from the global arrays. They are required for existing,
    // deployed facets to continue functioning. They should be considered part of a
    // transitional phase and MUST NOT be used in new development.

    /// @notice [Backward Compatibility] Checks if a contract is on the global allow list.
    /// @dev This function reads from the global list and is NOT granular. It is required for
    /// older, deployed facets to function correctly. Avoid use in new code.
    /// @param _contract The contract address.
    /// @return isAllowed True if the contract is allowed, false otherwise.
    function contractIsAllowed(
        address _contract
    ) internal view returns (bool) {
        return _getStorage().contractAllowList[_contract];
    }

    /// @notice [Backward Compatibility] Checks if a selector is on the global allow list.
    /// @dev This function reads from the global list and is NOT granular. It is required for
    /// older, deployed facets to function correctly. Avoid use in new code.
    /// @param _selector The function selector.
    /// @return isAllowed True if the selector is allowed, false otherwise.
    function selectorIsAllowed(bytes4 _selector) internal view returns (bool) {
        return _getStorage().selectorAllowList[_selector];
    }

    /// @notice [Backward Compatibility] Gets the entire global list of whitelisted contracts.
    /// @dev Returns the `contracts` array, which is synchronized with the new granular system.
    /// @return contracts The global list of whitelisted contracts.
    function getAllowedContracts() internal view returns (address[] memory) {
        return _getStorage().contracts;
    }

    /// @notice [Backward Compatibility] Gets the entire global list of whitelisted selectors.
    /// @dev Returns the `selectors` array, which is synchronized with the new granular system.
    function getAllowedSelectors() internal view returns (bytes4[] memory) {
        return _getStorage().selectors;
    }

    /// Private Helpers (Internal Use Only) ///

    /// @dev Internal helper to add a contract to the `contracts` array.
    /// @param _contract The contract address.
    function _addAllowedContract(address _contract) private {
        // Ensure address is actually a contract.
        if (!LibAsset.isContract(_contract)) revert InvalidContract();
        AllowListStorage storage als = _getStorage();

        // Add contract to the old allow list for backward compatibility
        als.contractAllowList[_contract] = true;

        // Skip if contract is already in allow list (1-based index).
        if (als.contractToIndex[_contract] > 0) return;

        // Add contract to allow list array.
        als.contracts.push(_contract);
        // Store 1-based index for efficient removal later.
        als.contractToIndex[_contract] = als.contracts.length;
    }

    /// @dev Internal helper to remove a contract from the `contracts` array.
    /// @param _contract The contract address.
    function _removeAllowedContract(address _contract) private {
        AllowListStorage storage als = _getStorage();

        // The V1 boolean mapping must be cleared before any checks.
        // This delete operation is placed at the top to ensure V1/V2 sync
        // and primarily solves two issues of stale item where
        // V1 data - als.selectorAllowList[_selector]=true,
        // V2 data - als.selectorToIndex[_selector]=0.
        // This scenario is different from selectors; it's an unlikely
        // edge case for contracts because the migration iterates the
        // full on-chain `contracts` array for a "perfect" cleanup.
        // However, this defensive delete ensures the function is robust
        //against any state corruption.
        delete als.contractAllowList[_contract];

        // Get the 1-based index; return if not found.
        uint256 oneBasedIndex = als.contractToIndex[_contract];
        if (oneBasedIndex == 0) {
            return;
        }
        // Convert to 0-based index for array operations.
        uint256 index = oneBasedIndex - 1;
        uint256 lastIndex = als.contracts.length - 1;

        // If the contract to remove isn't the last one,
        // move the last contract to the removed contract's position.
        if (index != lastIndex) {
            address lastContract = als.contracts[lastIndex];
            als.contracts[index] = lastContract;
            als.contractToIndex[lastContract] = oneBasedIndex;
        }

        // Remove the last element and clean up mappings.
        als.contracts.pop();
        delete als.contractToIndex[_contract];
    }

    /// @dev Internal helper to add a selector to the `selectors` array.
    /// @param _selector The function selector.
    function _addAllowedSelector(bytes4 _selector) private {
        AllowListStorage storage als = _getStorage();

        // Add selector to the old allow list for backward compatibility
        als.selectorAllowList[_selector] = true;

        // Skip if selector is already in allow list (1-based index).
        if (als.selectorToIndex[_selector] > 0) return;

        // Add selector to the array.
        als.selectors.push(_selector);

        // Store 1-based index for efficient removal later.
        als.selectorToIndex[_selector] = als.selectors.length;
    }

    /// @dev Internal helper to remove a selector from the `selectors` array.
    /// @param _selector The function selector.
    function _removeAllowedSelector(bytes4 _selector) private {
        AllowListStorage storage als = _getStorage();

        // The V1 boolean mapping must be cleared before any checks.
        // The migration's selector cleanup is "imperfect" as it relies on an
        // off-chain list. A "stale selector" ( V1 data - als.selectorAllowList[_selector]=true, V2 data - als.selectorToIndex[_selector]=0) is possible.
        // Placing `delete` here allows an admin to fix this by
        // add-then-remove, as this line will clean the V1 bool even if
        // the V2 `oneBasedIndex` is 0.
        delete als.selectorAllowList[_selector];

        // Get the 1-based index; return if not found.
        uint256 oneBasedIndex = als.selectorToIndex[_selector];
        if (oneBasedIndex == 0) {
            return;
        }

        // Convert to 0-based index for array operations.
        uint256 index = oneBasedIndex - 1;
        uint256 lastIndex = als.selectors.length - 1;

        // If the selector to remove isn't the last one,
        // move the last selector to the removed selector's position.
        if (index != lastIndex) {
            bytes4 lastSelector = als.selectors[lastIndex];
            als.selectors[index] = lastSelector;
            als.selectorToIndex[lastSelector] = oneBasedIndex;
        }

        // Remove the last element and clean up mappings.
        als.selectors.pop();
        delete als.selectorToIndex[_selector];
    }

    /// @dev Internal helper to manage the iterable array for the getter function.
    /// @param _contract The contract address.
    /// @param _selector The function selector.
    function _removeSelectorFromIterableList(
        address _contract,
        bytes4 _selector
    ) private {
        AllowListStorage storage als = _getStorage();

        // Get the 1-based index; return if not found.
        uint256 oneBasedIndex = als.selectorIndices[_contract][_selector];
        if (oneBasedIndex == 0) return;

        // Convert to 0-based index for array operations.
        uint256 index = oneBasedIndex - 1;
        bytes4[] storage selectorsArray = als.whitelistedSelectorsByContract[
            _contract
        ];
        uint256 lastIndex = selectorsArray.length - 1;

        // If the selector to remove isn't the last one,
        // move the last selector to the removed selector's position.
        if (index != lastIndex) {
            bytes4 lastSelector = selectorsArray[lastIndex];
            selectorsArray[index] = lastSelector;
            als.selectorIndices[_contract][lastSelector] = oneBasedIndex;
        }

        // Remove the last element and clean up mappings.
        selectorsArray.pop();
        delete als.selectorIndices[_contract][_selector];
    }

    /// @dev Fetches the storage pointer for this library.
    /// @return als The storage pointer.
    function _getStorage()
        internal
        pure
        returns (AllowListStorage storage als)
    {
        bytes32 position = NAMESPACE;
        assembly {
            als.slot := position
        }
    }
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

/// @title IWhitelistManagerFacet
/// @author LI.FI (https://li.fi)
/// @notice Interface for WhitelistManagerFacet facet for managing approved contracts and function selectors.
/// @dev This interface supports a dual-model allow list system:
///      1. NEW GRANULAR SYSTEM: Contract-selector pairs for precise control
///      2. BACKWARD COMPATIBILITY: Global lists for existing deployed facets
///      New development should exclusively use the granular functions.
/// @custom:version 1.0.0
interface IWhitelistManagerFacet {
    /// Events ///

    /// @notice Emitted when a contract and selector pair is whitelisted or unwhitelisted.
    event ContractSelectorWhitelistChanged(
        address indexed contractAddress,
        bytes4 indexed selector,
        bool indexed whitelisted
    );

    // ============================================================================
    // NEW GRANULAR SYSTEM FUNCTIONS (PREFERRED)
    // ============================================================================
    // These functions operate on contract-selector pairs and provide precise control.
    // They are the source of truth and synchronize the global arrays automatically.
    // ============================================================================

    /// @notice Sets the whitelist status for a specific contract and selector pair.
    /// @param _contract The contract address to whitelist or unwhitelist.
    /// @param _selector The function selector to whitelist or unwhitelist.
    /// @param _whitelisted Whether the contract and selector pair should be whitelisted.
    /// @dev APPROVE TO-ONLY SELECTOR: Use 0xffffffff to whitelist contracts that are used
    ///      only as approveTo in LibSwap.SwapData, without allowing function calls to them.
    ///      Some DEXs have specific contracts that need to be approved to while another
    ///      (router) contract must be called to initiate the swap. This selector enables
    ///      whitelisting such approveTo-only contracts while preventing any function calls.
    ///      Note: This makes isAddressWhitelisted(_contract) return true for backward
    ///      compatibility, but does not allow any granular calls. Actual selectors must
    ///      still be explicitly whitelisted for real function call usage.
    function setContractSelectorWhitelist(
        address _contract,
        bytes4 _selector,
        bool _whitelisted
    ) external;

    /// @notice Sets the whitelist status for multiple contract and selector pairs.
    /// @param _contracts Array of contract addresses to whitelist or unwhitelist.
    /// @param _selectors Array of function selectors to whitelist or unwhitelist.
    /// @param _whitelisted Whether the contract and selector pairs should be whitelisted.
    /// @dev APPROVE TO-ONLY SELECTOR: Use 0xffffffff to whitelist contracts that are used
    ///      only as approveTo in LibSwap.SwapData, without allowing function calls to them.
    ///      Some DEXs have specific contracts that need to be approved to while another
    ///      (router) contract must be called to initiate the swap. This selector enables
    ///      whitelisting such approveTo-only contracts while preventing any function calls.
    ///      Note: This makes isAddressWhitelisted(_contract) return true for backward
    ///      compatibility, but does not allow any granular calls. Actual selectors must
    ///      still be explicitly whitelisted for real function call usage.
    function batchSetContractSelectorWhitelist(
        address[] calldata _contracts,
        bytes4[] calldata _selectors,
        bool _whitelisted
    ) external;

    /// @notice Returns whether a specific contract and selector pair is whitelisted.
    /// @param _contract The contract address to query.
    /// @param _selector The function selector to query.
    /// @return whitelisted Whether the pair is whitelisted.
    function isContractSelectorWhitelisted(
        address _contract,
        bytes4 _selector
    ) external view returns (bool whitelisted);

    /// @notice Returns a list of whitelisted selectors for a specific contract.
    /// @param _contract The contract address to query.
    /// @return selectors List of whitelisted selectors for the contract.
    function getWhitelistedSelectorsForContract(
        address _contract
    ) external view returns (bytes4[] memory selectors);

    /// @notice Returns all whitelisted contract-selector pairs in a single call.
    /// @dev This is more efficient than calling getWhitelistedAddresses() and then
    ///      getWhitelistedSelectorsForContract() for each address separately.
    /// @return contracts Array of whitelisted contract addresses.
    /// @return selectors Array of corresponding selector arrays for each contract.
    function getAllContractSelectorPairs()
        external
        view
        returns (address[] memory contracts, bytes4[][] memory selectors);

    /// @notice Check if the allow list has been migrated.
    /// @return True if the allow list has been migrated, false otherwise.
    function isMigrated() external view returns (bool);

    // ============================================================================
    // BACKWARD COMPATIBILITY FUNCTIONS
    // ============================================================================
    // These functions read from the global arrays. They are required for existing,
    // deployed facets to continue functioning. They should be considered part of a
    // transitional phase and MUST NOT be used in new development.
    // ============================================================================

    /// @notice [BACKWARD COMPATIBILITY] Returns a list of all whitelisted addresses.
    /// @dev WARNING: This function reads from the global list and is NOT granular.
    ///      It is required for older, deployed facets to function correctly.
    ///      Avoid use in new code. Use isContractSelectorWhitelisted() instead.
    ///      Reading ~10 000 entries is safe, but if the list grows toward ~45 000+,
    ///      the call may run out of gas. Do not rely on it for unbounded iteration.
    /// @return addresses List of whitelisted addresses.
    function getWhitelistedAddresses()
        external
        view
        returns (address[] memory addresses);

    /// @notice [BACKWARD COMPATIBILITY] Returns whether an address is whitelisted.
    /// @dev WARNING: This function reads from the global list and is NOT granular.
    ///      It is required for older, deployed facets to function correctly.
    ///      Avoid use in new code. Use isContractSelectorWhitelisted() instead.
    /// @param _address The address to query.
    /// @return whitelisted Whitelisted or not.
    function isAddressWhitelisted(
        address _address
    ) external view returns (bool whitelisted);

    /// @notice [BACKWARD COMPATIBILITY] Returns a list of all approved function selectors.
    /// @dev WARNING: This function reads from the global list and is NOT granular.
    ///      It is required for older, deployed facets to function correctly.
    ///      Avoid use in new code. Use isContractSelectorWhitelisted() instead.
    ///      Reading ~10 000 entries is safe, but if the list grows toward ~45 000+,
    ///      the call may run out of gas. Do not rely on it for unbounded iteration.
    /// @return selectors List of approved function selectors.
    function getWhitelistedFunctionSelectors()
        external
        view
        returns (bytes4[] memory selectors);

    /// @notice [BACKWARD COMPATIBILITY] Returns whether a function selector is whitelisted.
    /// @dev WARNING: This function reads from the global list and is NOT granular.
    ///      It is required for older, deployed facets to function correctly.
    ///      Avoid use in new code. Use isContractSelectorWhitelisted() instead.
    /// @param _selector The function selector to query.
    /// @return whitelisted Whitelisted or not.
    function isFunctionSelectorWhitelisted(
        bytes4 _selector
    ) external view returns (bool whitelisted);

    /// Temporary methods for migration ///

    /// @notice Temporary method to check if the allow list has been migrated.
    /// @dev Remove these methods after migration is complete in next facet upgrade.
    /// @dev This function can only be called by the diamond owner or authorized addresses.
    /// @param _selectorsToRemove Array of selectors to remove from the allow list.
    /// @param _contracts Array of contract addresses.
    /// @param _selectors Parallel array of selector arrays for each contract.
    function migrate(
        bytes4[] calldata _selectorsToRemove,
        address[] calldata _contracts,
        bytes4[][] calldata _selectors
    ) external;
}

File 6 of 12 : GenericErrors.sol
// SPDX-License-Identifier: LGPL-3.0-only
/// @custom:version 1.0.2
pragma solidity ^0.8.17;

error AlreadyInitialized();
error CannotAuthoriseSelf();
error CannotBridgeToSameNetwork();
error ContractCallNotAllowed();
error CumulativeSlippageTooHigh(uint256 minAmount, uint256 receivedAmount);
error DiamondIsPaused();
error ETHTransferFailed();
error ExternalCallFailed();
error FunctionDoesNotExist();
error InformationMismatch();
error InsufficientBalance(uint256 required, uint256 balance);
error InvalidAmount();
error InvalidCallData();
error InvalidConfig();
error InvalidContract();
error InvalidDestinationChain();
error InvalidFallbackAddress();
error InvalidNonEVMReceiver();
error InvalidReceiver();
error InvalidSendingToken();
error NativeAssetNotSupported();
error NativeAssetTransferFailed();
error NoSwapDataProvided();
error NoSwapFromZeroBalance();
error NotAContract();
error NotInitialized();
error NoTransferToNullAddress();
error NullAddrIsNotAnERC20Token();
error NullAddrIsNotAValidSpender();
error OnlyContractOwner();
error RecoveryAddressCannotBeZero();
error ReentrancyError();
error TokenNotSupported();
error TransferFromFailed();
error UnAuthorized();
error UnsupportedChainId(uint256 chainId);
error WithdrawFailed();
error ZeroAmount();

// SPDX-License-Identifier: LGPL-3.0-only
/// @custom:version 1.0.0
pragma solidity ^0.8.17;

// solhint-disable-next-line no-global-import
import "./LibBytes.sol";

library LibUtil {
    using LibBytes for bytes;

    function getRevertMsg(
        bytes memory _res
    ) internal pure returns (string memory) {
        // If the _res length is less than 68, then the transaction failed silently (without a revert message)
        if (_res.length < 68) return "Transaction reverted silently";
        bytes memory revertData = _res.slice(4, _res.length - 4); // Remove the selector which is the first 4 bytes
        return abi.decode(revertData, (string)); // All that remains is the revert string
    }

    /// @notice Determines whether the given address is the zero address
    /// @param addr The address to verify
    /// @return Boolean indicating if the address is the zero address
    function isZeroAddress(address addr) internal pure returns (bool) {
        return addr == address(0);
    }

    function revertWith(bytes memory data) internal pure {
        assembly {
            let dataSize := mload(data) // Load the size of the data
            let dataPtr := add(data, 0x20) // Advance data pointer to the next word
            revert(dataPtr, dataSize) // Revert with the given data
        }
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { LibSwap } from "./LibSwap.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";

// solhint-disable-next-line max-line-length
import { InvalidReceiver, NullAddrIsNotAValidSpender, InvalidAmount, NullAddrIsNotAnERC20Token } from "../Errors/GenericErrors.sol";

/// @title LibAsset
/// @author LI.FI (https://li.fi)
/// @custom:version 2.1.3
/// @notice This library contains helpers for dealing with onchain transfers
///         of assets, including accounting for the native asset `assetId`
///         conventions and any noncompliant ERC20 transfers
library LibAsset {
    using SafeTransferLib for address;
    using SafeTransferLib for address payable;

    /// @dev All native assets use the empty address for their asset id
    ///      by convention
    address internal constant NULL_ADDRESS = address(0);

    /// @dev EIP-7702 delegation designator prefix for Account Abstraction
    bytes3 internal constant DELEGATION_DESIGNATOR = 0xef0100;

    /// @notice Gets the balance of the inheriting contract for the given asset
    /// @param assetId The asset identifier to get the balance of
    /// @return Balance held by contracts using this library (returns 0 if assetId does not exist)
    function getOwnBalance(address assetId) internal view returns (uint256) {
        return
            isNativeAsset(assetId)
                ? address(this).balance
                : assetId.balanceOf(address(this));
    }

    /// @notice Wrapper function to transfer a given asset (native or erc20) to
    ///         some recipient. Should handle all non-compliant return value
    ///         tokens as well by using the SafeERC20 contract by open zeppelin.
    /// @param assetId Asset id for transfer (address(0) for native asset,
    ///                token address for erc20s)
    /// @param recipient Address to send asset to
    /// @param amount Amount to send to given recipient
    function transferAsset(
        address assetId,
        address payable recipient,
        uint256 amount
    ) internal {
        if (isNativeAsset(assetId)) {
            transferNativeAsset(recipient, amount);
        } else {
            transferERC20(assetId, recipient, amount);
        }
    }

    /// @notice Transfers ether from the inheriting contract to a given
    ///         recipient
    /// @param recipient Address to send ether to
    /// @param amount Amount to send to given recipient
    function transferNativeAsset(
        address payable recipient,
        uint256 amount
    ) internal {
        // make sure a meaningful receiver address was provided
        if (recipient == NULL_ADDRESS) revert InvalidReceiver();

        // transfer native asset (will revert if target reverts or contract has insufficient balance)
        recipient.safeTransferETH(amount);
    }

    /// @notice Transfers tokens from the inheriting contract to a given recipient
    /// @param assetId Token address to transfer
    /// @param recipient Address to send tokens to
    /// @param amount Amount to send to given recipient
    function transferERC20(
        address assetId,
        address recipient,
        uint256 amount
    ) internal {
        // make sure a meaningful receiver address was provided
        if (recipient == NULL_ADDRESS) {
            revert InvalidReceiver();
        }

        // transfer ERC20 assets (will revert if target reverts or contract has insufficient balance)
        assetId.safeTransfer(recipient, amount);
    }

    /// @notice Transfers tokens from a sender to a given recipient
    /// @param assetId Token address to transfer
    /// @param from Address of sender/owner
    /// @param recipient Address of recipient/spender
    /// @param amount Amount to transfer from owner to spender
    function transferFromERC20(
        address assetId,
        address from,
        address recipient,
        uint256 amount
    ) internal {
        // check if native asset
        if (isNativeAsset(assetId)) {
            revert NullAddrIsNotAnERC20Token();
        }

        // make sure a meaningful receiver address was provided
        if (recipient == NULL_ADDRESS) {
            revert InvalidReceiver();
        }

        // transfer ERC20 assets (will revert if target reverts or contract has insufficient balance)
        assetId.safeTransferFrom(from, recipient, amount);
    }

    /// @notice Pulls tokens from msg.sender
    /// @param assetId Token address to transfer
    /// @param amount Amount to transfer from owner
    function depositAsset(address assetId, uint256 amount) internal {
        // make sure a meaningful amount was provided
        if (amount == 0) revert InvalidAmount();

        // check if native asset
        if (isNativeAsset(assetId)) {
            // ensure msg.value is equal or greater than amount
            if (msg.value < amount) revert InvalidAmount();
        } else {
            // transfer ERC20 assets (will revert if target reverts or contract has insufficient balance)
            assetId.safeTransferFrom(msg.sender, address(this), amount);
        }
    }

    function depositAssets(LibSwap.SwapData[] calldata swaps) internal {
        for (uint256 i = 0; i < swaps.length; ) {
            LibSwap.SwapData calldata swap = swaps[i];
            if (swap.requiresDeposit) {
                depositAsset(swap.sendingAssetId, swap.fromAmount);
            }
            unchecked {
                i++;
            }
        }
    }

    /// @notice If the current allowance is insufficient, the allowance for a given spender
    ///         is set to MAX_UINT.
    /// @param assetId Token address to transfer
    /// @param spender Address to give spend approval to
    /// @param amount allowance amount required for current transaction
    function maxApproveERC20(
        IERC20 assetId,
        address spender,
        uint256 amount
    ) internal {
        approveERC20(assetId, spender, amount, type(uint256).max);
    }

    /// @notice If the current allowance is insufficient, the allowance for a given spender
    ///         is set to the amount provided
    /// @param assetId Token address to transfer
    /// @param spender Address to give spend approval to
    /// @param requiredAllowance Allowance required for current transaction
    /// @param setAllowanceTo The amount the allowance should be set to if current allowance is insufficient
    function approveERC20(
        IERC20 assetId,
        address spender,
        uint256 requiredAllowance,
        uint256 setAllowanceTo
    ) internal {
        if (isNativeAsset(address(assetId))) {
            return;
        }

        // make sure a meaningful spender address was provided
        if (spender == NULL_ADDRESS) {
            revert NullAddrIsNotAValidSpender();
        }

        // check if allowance is sufficient, otherwise set allowance to provided amount
        // If the initial attempt to approve fails, attempts to reset the approved amount to zero,
        // then retries the approval again (some tokens, e.g. USDT, requires this).
        // Reverts upon failure
        if (assetId.allowance(address(this), spender) < requiredAllowance) {
            address(assetId).safeApproveWithRetry(spender, setAllowanceTo);
        }
    }

    /// @notice Determines whether the given assetId is the native asset
    /// @param assetId The asset identifier to evaluate
    /// @return Boolean indicating if the asset is the native asset
    function isNativeAsset(address assetId) internal pure returns (bool) {
        return assetId == NULL_ADDRESS;
    }

    /// @notice Checks if the given address is a contract
    ///         Returns true for any account with runtime code (excluding EIP-7702 accounts).
    ///         For EIP-7702 accounts, checks if code size is exactly 23 bytes (delegation format).
    ///         Limitations:
    ///         - Cannot distinguish between EOA and self-destructed contract
    /// @param account The address to be checked
    function isContract(address account) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }

        // Return true only for regular contracts (size > 23)
        // EIP-7702 delegated accounts (size == 23) are still EOAs, not contracts
        return size > 23;
    }
}

// SPDX-License-Identifier: LGPL-3.0-only
/// @custom:version 1.0.0
pragma solidity ^0.8.17;

library LibBytes {
    // solhint-disable no-inline-assembly

    // LibBytes specific errors
    error SliceOverflow();
    error SliceOutOfBounds();
    error AddressOutOfBounds();

    bytes16 private constant _SYMBOLS = "0123456789abcdef";

    // -------------------------

    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    ) internal pure returns (bytes memory) {
        if (_length + 31 < _length) revert SliceOverflow();
        if (_bytes.length < _start + _length) revert SliceOutOfBounds();

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(
                    add(tempBytes, lengthmod),
                    mul(0x20, iszero(lengthmod))
                )
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(
                        add(
                            add(_bytes, lengthmod),
                            mul(0x20, iszero(lengthmod))
                        ),
                        _start
                    )
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(
        bytes memory _bytes,
        uint256 _start
    ) internal pure returns (address) {
        if (_bytes.length < _start + 20) {
            revert AddressOutOfBounds();
        }
        address tempAddress;

        assembly {
            tempAddress := div(
                mload(add(add(_bytes, 0x20), _start)),
                0x1000000000000000000000000
            )
        }

        return tempAddress;
    }

    /// Copied from OpenZeppelin's `Strings.sol` utility library.
    /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/8335676b0e99944eef6a742e16dcd9ff6e68e609
    /// /contracts/utils/Strings.sol
    function toHexString(
        uint256 value,
        uint256 length
    ) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        // solhint-disable-next-line gas-custom-errors
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.17;

import { LibAsset } from "./LibAsset.sol";
import { LibUtil } from "./LibUtil.sol";
import { InvalidContract, NoSwapFromZeroBalance } from "../Errors/GenericErrors.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title LibSwap
/// @custom:version 1.1.0
/// @notice This library contains functionality to execute mostly swaps but also
///         other calls such as fee collection, token wrapping/unwrapping or
///         sending gas to destination chain
library LibSwap {
    /// @notice Struct containing all necessary data to execute a swap or generic call
    /// @param callTo The address of the contract to call for executing the swap
    /// @param approveTo The address that will receive token approval (can be different than callTo for some DEXs)
    /// @param sendingAssetId The address of the token being sent
    /// @param receivingAssetId The address of the token expected to be received
    /// @param fromAmount The exact amount of the sending asset to be used in the call
    /// @param callData Encoded function call data to be sent to the `callTo` contract
    /// @param requiresDeposit A flag indicating whether the tokens must be deposited (pulled) before the call
    struct SwapData {
        address callTo;
        address approveTo;
        address sendingAssetId;
        address receivingAssetId;
        uint256 fromAmount;
        bytes callData;
        bool requiresDeposit;
    }

    /// @notice Emitted after a successful asset swap or related operation
    /// @param transactionId    The unique identifier associated with the swap operation
    /// @param dex              The address of the DEX or contract that handled the swap
    /// @param fromAssetId      The address of the token that was sent
    /// @param toAssetId        The address of the token that was received
    /// @param fromAmount       The amount of `fromAssetId` sent
    /// @param toAmount         The amount of `toAssetId` received
    /// @param timestamp        The timestamp when the swap was executed
    event AssetSwapped(
        bytes32 transactionId,
        address dex,
        address fromAssetId,
        address toAssetId,
        uint256 fromAmount,
        uint256 toAmount,
        uint256 timestamp
    );

    function swap(bytes32 transactionId, SwapData calldata _swap) internal {
        // make sure callTo is a contract
        if (!LibAsset.isContract(_swap.callTo)) revert InvalidContract();

        // make sure that fromAmount is not 0
        uint256 fromAmount = _swap.fromAmount;
        if (fromAmount == 0) revert NoSwapFromZeroBalance();

        // determine how much native value to send with the swap call
        uint256 nativeValue = LibAsset.isNativeAsset(_swap.sendingAssetId)
            ? _swap.fromAmount
            : 0;

        // store initial balance (required for event emission)
        uint256 initialReceivingAssetBalance = LibAsset.getOwnBalance(
            _swap.receivingAssetId
        );

        // max approve (if ERC20)
        if (nativeValue == 0) {
            LibAsset.maxApproveERC20(
                IERC20(_swap.sendingAssetId),
                _swap.approveTo,
                _swap.fromAmount
            );
        }

        // we used to have a sending asset balance check here (initialSendingAssetBalance >= _swap.fromAmount)
        // this check was removed to allow for more flexibility with rebasing/fee-taking tokens
        // the general assumption is that if not enough tokens are available to execute the calldata,
        // the transaction will fail anyway
        // the error message might not be as explicit though

        // execute the swap
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory res) = _swap.callTo.call{
            value: nativeValue
        }(_swap.callData);
        if (!success) {
            LibUtil.revertWith(res);
        }

        // get post-swap balance
        uint256 newBalance = LibAsset.getOwnBalance(_swap.receivingAssetId);

        // emit event
        emit AssetSwapped(
            transactionId,
            _swap.callTo,
            _swap.sendingAssetId,
            _swap.receivingAssetId,
            _swap.fromAmount,
            newBalance > initialReceivingAssetBalance
                ? newBalance - initialReceivingAssetBalance
                : newBalance,
            block.timestamp
        );
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success :=
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(and(call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00), exists)) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(add(m, 0x94), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `1` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero(call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00)) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

Settings
{
  "remappings": [
    "@eth-optimism/=node_modules/@hop-protocol/sdk/node_modules/@eth-optimism/",
    "@uniswap/=node_modules/@uniswap/",
    "eth-gas-reporter/=node_modules/eth-gas-reporter/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "celer-network/=lib/sgn-v2-contracts/",
    "create3-factory/=lib/create3-factory/src/",
    "solmate/=lib/solmate/src/",
    "solady/=lib/solady/src/",
    "permit2/=lib/Permit2/src/",
    "ds-test/=lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "lifi/=src/",
    "test/=test/",
    "@cowprotocol/=node_modules/@cowprotocol/",
    "Permit2/=lib/Permit2/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/Permit2/lib/forge-gas-snapshot/src/",
    "hardhat/=node_modules/hardhat/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "sgn-v2-contracts/=lib/sgn-v2-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"name":"CannotAuthoriseSelf","type":"error"},{"inputs":[],"name":"InvalidCallData","type":"error"},{"inputs":[],"name":"InvalidConfig","type":"error"},{"inputs":[],"name":"InvalidContract","type":"error"},{"inputs":[],"name":"UnAuthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"},{"indexed":true,"internalType":"bytes4","name":"selector","type":"bytes4"},{"indexed":true,"internalType":"bool","name":"whitelisted","type":"bool"}],"name":"ContractSelectorWhitelistChanged","type":"event"},{"inputs":[{"internalType":"address[]","name":"_contracts","type":"address[]"},{"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"},{"internalType":"bool","name":"_whitelisted","type":"bool"}],"name":"batchSetContractSelectorWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllContractSelectorPairs","outputs":[{"internalType":"address[]","name":"contracts","type":"address[]"},{"internalType":"bytes4[][]","name":"selectors","type":"bytes4[][]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWhitelistedAddresses","outputs":[{"internalType":"address[]","name":"addresses","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWhitelistedFunctionSelectors","outputs":[{"internalType":"bytes4[]","name":"selectors","type":"bytes4[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"getWhitelistedSelectorsForContract","outputs":[{"internalType":"bytes4[]","name":"selectors","type":"bytes4[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"isAddressWhitelisted","outputs":[{"internalType":"bool","name":"whitelisted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"isContractSelectorWhitelisted","outputs":[{"internalType":"bool","name":"whitelisted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"isFunctionSelectorWhitelisted","outputs":[{"internalType":"bool","name":"whitelisted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMigrated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4[]","name":"_selectorsToRemove","type":"bytes4[]"},{"internalType":"address[]","name":"_contracts","type":"address[]"},{"internalType":"bytes4[][]","name":"_selectors","type":"bytes4[][]"}],"name":"migrate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"internalType":"bytes4","name":"_selector","type":"bytes4"},{"internalType":"bool","name":"_whitelisted","type":"bool"}],"name":"setContractSelectorWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b506121c2806100206000396000f3fe608060405234801561001057600080fd5b50600436106100c85760003560e01c80636d028027116100815780639baf00f91161005b5780639baf00f914610187578063b06faf621461019a578063f08ad6eb146101c457600080fd5b80636d0280271461014a57806390f3d77b1461015f57806394ddf6631461017457600080fd5b806313f44d10116100b257806313f44d101461010157806351fed6481461012457806356977cc01461013757600080fd5b8062816c97146100cd5780631171c007146100ec575b600080fd5b6100d56101d7565b6040516100e3929190611c1a565b60405180910390f35b6100ff6100fa366004611d54565b61028c565b005b61011461010f366004611df9565b61037d565b60405190151581526020016100e3565b6100ff610132366004611e44565b6103cc565b610114610145366004611e87565b610422565b61015261047b565b6040516100e39190611ea2565b61016761048a565b6040516100e39190611eb5565b610167610182366004611df9565b610494565b610114610195366004611f1b565b61049f565b7f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f285460ff16610114565b6100ff6101d2366004611f4e565b61051c565b6060806101e2610982565b9150815167ffffffffffffffff8111156101fe576101fe611fe8565b60405190808252806020026020018201604052801561023157816020015b606081526020019060019003908161021c5790505b50905060005b82518110156102875761026283828151811061025557610255612017565b6020026020010151610a13565b82828151811061027457610274612017565b6020908102919091010152600101610237565b509091565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c13205473ffffffffffffffffffffffffffffffffffffffff1633146102d2576102d2610aed565b83821461030b576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b848110156103755761036d86868381811061032b5761032b612017565b90506020020160208101906103409190611df9565b85858481811061035257610352612017565b90506020020160208101906103679190611e87565b84610b8d565b60010161030e565b505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e602052604081205460ff165b92915050565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c13205473ffffffffffffffffffffffffffffffffffffffff16331461041257610412610aed565b61041d838383610b8d565b505050565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1f602052604081205460ff166103c6565b6060610485610982565b905090565b6060610485610cf0565b60606103c682610a13565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f24602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008516845290915281205460ff165b9392505050565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c13205473ffffffffffffffffffffffffffffffffffffffff16331461056257610562610aed565b7f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f28547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e9060ff16156105b45750610375565b8382146105ed576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028101546000905b8082101561067c5782600001600084600201848154811061061957610619612017565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055600191909101906105f6565b5060009050865b8082101561071c578260010160008a8a858181106106a3576106a3612017565b90506020020160208101906106b89190611e87565b7fffffffff00000000000000000000000000000000000000000000000000000000168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905560019190910190610683565b61072a600284016000611b97565b5060009050845b8082101561094c57600087878481811061074d5761074d612017565b90506020020160208101906107629190611df9565b905036600087878681811061077957610779612017565b905060200281019061078b9190612046565b90925090503073ffffffffffffffffffffffffffffffffffffffff8416036107df576040517fa9cefcae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8181101561093d5760008383838181106107fe576107fe612017565b90506020020160208101906108139190611e87565b73ffffffffffffffffffffffffffffffffffffffff861660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f24602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008516845290915290205490915060ff16156108c2576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108cc8582610dab565b6040516001907fffffffff0000000000000000000000000000000000000000000000000000000083169073ffffffffffffffffffffffffffffffffffffffff8816907f1026451bc49d23b939d8e3c16eb4fc3ea6dd8b0be5549aa0dae6675b0083a84090600090a4506001016107e2565b50846001019450505050610731565b5050600a0180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055505050505050565b60607f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e600201805480602002602001604051908101604052809291908181526020018280548015610a0957602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116109de575b5050505050905090565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f266020908152604091829020805483518184028101840190945280845260609392830182828015610ae157602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019060040190602082600301049283019260010382029150808411610a8e5790505b50505050509050919050565b600080357fffffffff000000000000000000000000000000000000000000000000000000001681527fdf05114fe8fad5d7cd2d71c5651effc2a4c21f13ee8b4a462e2a3bd4e140c73e6020818152604080842033855290915290912054600160ff909116151514610b8a576040517fbe24598300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b3073ffffffffffffffffffffffffffffffffffffffff841603610bdc576040517fa9cefcae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff831660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f24602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008616845290915290205460ff1681151581151503610c625750505050565b8115610c7757610c728484610dab565b610c81565b610c81848461105c565b604051821515907fffffffff0000000000000000000000000000000000000000000000000000000085169073ffffffffffffffffffffffffffffffffffffffff8716907f1026451bc49d23b939d8e3c16eb4fc3ea6dd8b0be5549aa0dae6675b0083a84090600090a450505050565b60607f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e600501805480602002602001604051908101604052809291908181526020018280548015610a0957602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019060040190602082600301049283019260010382029150808411610d4f5790505050505050905090565b73ffffffffffffffffffffffffffffffffffffffff82161580610dee57507fffffffff000000000000000000000000000000000000000000000000000000008116155b15610e25576040517f1c49f4d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f24602090815260408083207fffffffff00000000000000000000000000000000000000000000000000000000851684529091529020547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e9060ff1615610ec657505050565b73ffffffffffffffffffffffffffffffffffffffff8316600081815260068301602090815260408083207fffffffff0000000000000000000000000000000000000000000000000000000087168452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055928252600884019052908120549003610f5f57610f5f8361120d565b7fffffffff000000000000000000000000000000000000000000000000000000008216600090815260078201602052604081208054909190610fa0906120dd565b9182905550600103610fb557610fb582611358565b73ffffffffffffffffffffffffffffffffffffffff9290921660008181526008808501602090815260408084208054600181018255818652838620948104909401805463ffffffff60079096166004026101000a958602191660e089901c95909502949094179093559383529054600990950181528282207fffffffff00000000000000000000000000000000000000000000000000000000909416825292909252902055565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f24602090815260408083207fffffffff00000000000000000000000000000000000000000000000000000000851684529091529020547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e9060ff166110fc57505050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260068201602090815260408083207fffffffff0000000000000000000000000000000000000000000000000000000086168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055611180838361149a565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260088201602052604081205490036111b7576111b78361171a565b7fffffffff0000000000000000000000000000000000000000000000000000000082166000908152600782016020526040812080549091906111f890612115565b918290555060000361041d5761041d8261193c565b6017813b11611248576040517f6eefed2000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e6020818152604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f2190915290912054156112e9575050565b60028101805460018101825560008281526020808220909201805473ffffffffffffffffffffffffffffffffffffffff9096167fffffffffffffffffffffffff000000000000000000000000000000000000000090961686179055915493825260039092019091526040902055565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1f6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f229091529020547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e9015611426575050565b600581018054600181018255600082815260208082206008840401805460e088901c6004600790961686026101000a90810263ffffffff9091021990911617905592547fffffffff000000000000000000000000000000000000000000000000000000009095168152920190526040902055565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f27602090815260408083207fffffffff00000000000000000000000000000000000000000000000000000000851684529091528120547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e9181900361153b5750505050565b600061154860018361214a565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260088501602052604081208054929350916115819060019061214a565b905080831461166157600082828154811061159e5761159e612017565b90600052602060002090600891828204019190066004029054906101000a900460e01b9050808385815481106115d6576115d6612017565b600091825260208083206008830401805463ffffffff60079094166004026101000a938402191660e09590951c9290920293909317905573ffffffffffffffffffffffffffffffffffffffff8a16815260098801825260408082207fffffffff0000000000000000000000000000000000000000000000000000000094909416825292909152208490555b818054806116715761167161215d565b6000828152602080822060087fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90940193840401805463ffffffff600460078716026101000a02191690559190925573ffffffffffffffffffffffffffffffffffffffff909816815260099095018752505060408084207fffffffff00000000000000000000000000000000000000000000000000000000909516845293909452509081205550565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e6020818152604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f2190915282205490918190036117bc57505050565b60006117c960018361214a565b60028401549091506000906117e09060019061214a565b90508082146118a057600084600201828154811061180057611800612017565b60009182526020909120015460028601805473ffffffffffffffffffffffffffffffffffffffff909216925082918590811061183e5761183e612017565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff94851617905592909116815260038601909152604090208390555b836002018054806118b3576118b361215d565b6000828152602080822083017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905590920190925573ffffffffffffffffffffffffffffffffffffffff96909616815260039094019094525050604081205550565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1f6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f229091528120547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e91819003611a0a57505050565b6000611a1760018361214a565b6005840154909150600090611a2e9060019061214a565b9050808214611af6576000846005018281548110611a4e57611a4e612017565b90600052602060002090600891828204019190066004029054906101000a900460e01b905080856005018481548110611a8957611a89612017565b600091825260208083206008830401805460e09590951c6004600790941684026101000a90810263ffffffff90910219909516949094179093557fffffffff0000000000000000000000000000000000000000000000000000000090931681529186019052604090208390555b83600501805480611b0957611b0961215d565b60019003818190600052602060002090600891828204019190066004026101000a81549063ffffffff02191690559055836004016000867bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001908152602001600020600090555050505050565b5080546000825590600052602060002090810190610b8a91905b80821115611bc55760008155600101611bb1565b5090565b600081518084526020808501945080840160005b83811015611c0f57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101611bdd565b509495945050505050565b604081526000611c2d6040830185611bc9565b6020838203818501528185518084528284019150828160051b8501018388016000805b84811015611ce3578784037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018652825180518086529088019088860190845b81811015611cce5783517fffffffff00000000000000000000000000000000000000000000000000000000168352928a0192918a0191600101611c90565b50509688019694505091860191600101611c50565b50919a9950505050505050505050565b60008083601f840112611d0557600080fd5b50813567ffffffffffffffff811115611d1d57600080fd5b6020830191508360208260051b8501011115611d3857600080fd5b9250929050565b80358015158114611d4f57600080fd5b919050565b600080600080600060608688031215611d6c57600080fd5b853567ffffffffffffffff80821115611d8457600080fd5b611d9089838a01611cf3565b90975095506020880135915080821115611da957600080fd5b50611db688828901611cf3565b9094509250611dc9905060408701611d3f565b90509295509295909350565b803573ffffffffffffffffffffffffffffffffffffffff81168114611d4f57600080fd5b600060208284031215611e0b57600080fd5b61051582611dd5565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114611d4f57600080fd5b600080600060608486031215611e5957600080fd5b611e6284611dd5565b9250611e7060208501611e14565b9150611e7e60408501611d3f565b90509250925092565b600060208284031215611e9957600080fd5b61051582611e14565b6020815260006105156020830184611bc9565b6020808252825182820181905260009190848201906040850190845b81811015611f0f5783517fffffffff000000000000000000000000000000000000000000000000000000001683529284019291840191600101611ed1565b50909695505050505050565b60008060408385031215611f2e57600080fd5b611f3783611dd5565b9150611f4560208401611e14565b90509250929050565b60008060008060008060608789031215611f6757600080fd5b863567ffffffffffffffff80821115611f7f57600080fd5b611f8b8a838b01611cf3565b90985096506020890135915080821115611fa457600080fd5b611fb08a838b01611cf3565b90965094506040890135915080821115611fc957600080fd5b50611fd689828a01611cf3565b979a9699509497509295939492505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261207b57600080fd5b83018035915067ffffffffffffffff82111561209657600080fd5b6020019150600581901b3603821315611d3857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361210e5761210e6120ae565b5060010190565b600081612124576121246120ae565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b818103818111156103c6576103c66120ae565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea2646970667358221220ef50cc718134d3f54bddd8446f4617dc3cf0e2fefd4dd72daa5c2a6b970907be64736f6c63430008110033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100c85760003560e01c80636d028027116100815780639baf00f91161005b5780639baf00f914610187578063b06faf621461019a578063f08ad6eb146101c457600080fd5b80636d0280271461014a57806390f3d77b1461015f57806394ddf6631461017457600080fd5b806313f44d10116100b257806313f44d101461010157806351fed6481461012457806356977cc01461013757600080fd5b8062816c97146100cd5780631171c007146100ec575b600080fd5b6100d56101d7565b6040516100e3929190611c1a565b60405180910390f35b6100ff6100fa366004611d54565b61028c565b005b61011461010f366004611df9565b61037d565b60405190151581526020016100e3565b6100ff610132366004611e44565b6103cc565b610114610145366004611e87565b610422565b61015261047b565b6040516100e39190611ea2565b61016761048a565b6040516100e39190611eb5565b610167610182366004611df9565b610494565b610114610195366004611f1b565b61049f565b7f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f285460ff16610114565b6100ff6101d2366004611f4e565b61051c565b6060806101e2610982565b9150815167ffffffffffffffff8111156101fe576101fe611fe8565b60405190808252806020026020018201604052801561023157816020015b606081526020019060019003908161021c5790505b50905060005b82518110156102875761026283828151811061025557610255612017565b6020026020010151610a13565b82828151811061027457610274612017565b6020908102919091010152600101610237565b509091565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c13205473ffffffffffffffffffffffffffffffffffffffff1633146102d2576102d2610aed565b83821461030b576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b848110156103755761036d86868381811061032b5761032b612017565b90506020020160208101906103409190611df9565b85858481811061035257610352612017565b90506020020160208101906103679190611e87565b84610b8d565b60010161030e565b505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e602052604081205460ff165b92915050565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c13205473ffffffffffffffffffffffffffffffffffffffff16331461041257610412610aed565b61041d838383610b8d565b505050565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1f602052604081205460ff166103c6565b6060610485610982565b905090565b6060610485610cf0565b60606103c682610a13565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f24602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008516845290915281205460ff165b9392505050565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c13205473ffffffffffffffffffffffffffffffffffffffff16331461056257610562610aed565b7f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f28547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e9060ff16156105b45750610375565b8382146105ed576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028101546000905b8082101561067c5782600001600084600201848154811061061957610619612017565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055600191909101906105f6565b5060009050865b8082101561071c578260010160008a8a858181106106a3576106a3612017565b90506020020160208101906106b89190611e87565b7fffffffff00000000000000000000000000000000000000000000000000000000168152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905560019190910190610683565b61072a600284016000611b97565b5060009050845b8082101561094c57600087878481811061074d5761074d612017565b90506020020160208101906107629190611df9565b905036600087878681811061077957610779612017565b905060200281019061078b9190612046565b90925090503073ffffffffffffffffffffffffffffffffffffffff8416036107df576040517fa9cefcae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8181101561093d5760008383838181106107fe576107fe612017565b90506020020160208101906108139190611e87565b73ffffffffffffffffffffffffffffffffffffffff861660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f24602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008516845290915290205490915060ff16156108c2576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6108cc8582610dab565b6040516001907fffffffff0000000000000000000000000000000000000000000000000000000083169073ffffffffffffffffffffffffffffffffffffffff8816907f1026451bc49d23b939d8e3c16eb4fc3ea6dd8b0be5549aa0dae6675b0083a84090600090a4506001016107e2565b50846001019450505050610731565b5050600a0180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055505050505050565b60607f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e600201805480602002602001604051908101604052809291908181526020018280548015610a0957602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116109de575b5050505050905090565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f266020908152604091829020805483518184028101840190945280845260609392830182828015610ae157602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019060040190602082600301049283019260010382029150808411610a8e5790505b50505050509050919050565b600080357fffffffff000000000000000000000000000000000000000000000000000000001681527fdf05114fe8fad5d7cd2d71c5651effc2a4c21f13ee8b4a462e2a3bd4e140c73e6020818152604080842033855290915290912054600160ff909116151514610b8a576040517fbe24598300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b3073ffffffffffffffffffffffffffffffffffffffff841603610bdc576040517fa9cefcae00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff831660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f24602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008616845290915290205460ff1681151581151503610c625750505050565b8115610c7757610c728484610dab565b610c81565b610c81848461105c565b604051821515907fffffffff0000000000000000000000000000000000000000000000000000000085169073ffffffffffffffffffffffffffffffffffffffff8716907f1026451bc49d23b939d8e3c16eb4fc3ea6dd8b0be5549aa0dae6675b0083a84090600090a450505050565b60607f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e600501805480602002602001604051908101604052809291908181526020018280548015610a0957602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019060040190602082600301049283019260010382029150808411610d4f5790505050505050905090565b73ffffffffffffffffffffffffffffffffffffffff82161580610dee57507fffffffff000000000000000000000000000000000000000000000000000000008116155b15610e25576040517f1c49f4d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f24602090815260408083207fffffffff00000000000000000000000000000000000000000000000000000000851684529091529020547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e9060ff1615610ec657505050565b73ffffffffffffffffffffffffffffffffffffffff8316600081815260068301602090815260408083207fffffffff0000000000000000000000000000000000000000000000000000000087168452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055928252600884019052908120549003610f5f57610f5f8361120d565b7fffffffff000000000000000000000000000000000000000000000000000000008216600090815260078201602052604081208054909190610fa0906120dd565b9182905550600103610fb557610fb582611358565b73ffffffffffffffffffffffffffffffffffffffff9290921660008181526008808501602090815260408084208054600181018255818652838620948104909401805463ffffffff60079096166004026101000a958602191660e089901c95909502949094179093559383529054600990950181528282207fffffffff00000000000000000000000000000000000000000000000000000000909416825292909252902055565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f24602090815260408083207fffffffff00000000000000000000000000000000000000000000000000000000851684529091529020547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e9060ff166110fc57505050565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260068201602090815260408083207fffffffff0000000000000000000000000000000000000000000000000000000086168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055611180838361149a565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260088201602052604081205490036111b7576111b78361171a565b7fffffffff0000000000000000000000000000000000000000000000000000000082166000908152600782016020526040812080549091906111f890612115565b918290555060000361041d5761041d8261193c565b6017813b11611248576040517f6eefed2000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e6020818152604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f2190915290912054156112e9575050565b60028101805460018101825560008281526020808220909201805473ffffffffffffffffffffffffffffffffffffffff9096167fffffffffffffffffffffffff000000000000000000000000000000000000000090961686179055915493825260039092019091526040902055565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1f6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790557f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f229091529020547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e9015611426575050565b600581018054600181018255600082815260208082206008840401805460e088901c6004600790961686026101000a90810263ffffffff9091021990911617905592547fffffffff000000000000000000000000000000000000000000000000000000009095168152920190526040902055565b73ffffffffffffffffffffffffffffffffffffffff821660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f27602090815260408083207fffffffff00000000000000000000000000000000000000000000000000000000851684529091528120547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e9181900361153b5750505050565b600061154860018361214a565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260088501602052604081208054929350916115819060019061214a565b905080831461166157600082828154811061159e5761159e612017565b90600052602060002090600891828204019190066004029054906101000a900460e01b9050808385815481106115d6576115d6612017565b600091825260208083206008830401805463ffffffff60079094166004026101000a938402191660e09590951c9290920293909317905573ffffffffffffffffffffffffffffffffffffffff8a16815260098801825260408082207fffffffff0000000000000000000000000000000000000000000000000000000094909416825292909152208490555b818054806116715761167161215d565b6000828152602080822060087fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90940193840401805463ffffffff600460078716026101000a02191690559190925573ffffffffffffffffffffffffffffffffffffffff909816815260099095018752505060408084207fffffffff00000000000000000000000000000000000000000000000000000000909516845293909452509081205550565b73ffffffffffffffffffffffffffffffffffffffff811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e6020818152604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f2190915282205490918190036117bc57505050565b60006117c960018361214a565b60028401549091506000906117e09060019061214a565b90508082146118a057600084600201828154811061180057611800612017565b60009182526020909120015460028601805473ffffffffffffffffffffffffffffffffffffffff909216925082918590811061183e5761183e612017565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff94851617905592909116815260038601909152604090208390555b836002018054806118b3576118b361215d565b6000828152602080822083017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905590920190925573ffffffffffffffffffffffffffffffffffffffff96909616815260039094019094525050604081205550565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081527f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1f6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f229091528120547f7a8ac5d3b7183f220a0602439da45ea337311d699902d1ed11a3725a714e7f1e91819003611a0a57505050565b6000611a1760018361214a565b6005840154909150600090611a2e9060019061214a565b9050808214611af6576000846005018281548110611a4e57611a4e612017565b90600052602060002090600891828204019190066004029054906101000a900460e01b905080856005018481548110611a8957611a89612017565b600091825260208083206008830401805460e09590951c6004600790941684026101000a90810263ffffffff90910219909516949094179093557fffffffff0000000000000000000000000000000000000000000000000000000090931681529186019052604090208390555b83600501805480611b0957611b0961215d565b60019003818190600052602060002090600891828204019190066004026101000a81549063ffffffff02191690559055836004016000867bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001908152602001600020600090555050505050565b5080546000825590600052602060002090810190610b8a91905b80821115611bc55760008155600101611bb1565b5090565b600081518084526020808501945080840160005b83811015611c0f57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101611bdd565b509495945050505050565b604081526000611c2d6040830185611bc9565b6020838203818501528185518084528284019150828160051b8501018388016000805b84811015611ce3578784037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018652825180518086529088019088860190845b81811015611cce5783517fffffffff00000000000000000000000000000000000000000000000000000000168352928a0192918a0191600101611c90565b50509688019694505091860191600101611c50565b50919a9950505050505050505050565b60008083601f840112611d0557600080fd5b50813567ffffffffffffffff811115611d1d57600080fd5b6020830191508360208260051b8501011115611d3857600080fd5b9250929050565b80358015158114611d4f57600080fd5b919050565b600080600080600060608688031215611d6c57600080fd5b853567ffffffffffffffff80821115611d8457600080fd5b611d9089838a01611cf3565b90975095506020880135915080821115611da957600080fd5b50611db688828901611cf3565b9094509250611dc9905060408701611d3f565b90509295509295909350565b803573ffffffffffffffffffffffffffffffffffffffff81168114611d4f57600080fd5b600060208284031215611e0b57600080fd5b61051582611dd5565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114611d4f57600080fd5b600080600060608486031215611e5957600080fd5b611e6284611dd5565b9250611e7060208501611e14565b9150611e7e60408501611d3f565b90509250925092565b600060208284031215611e9957600080fd5b61051582611e14565b6020815260006105156020830184611bc9565b6020808252825182820181905260009190848201906040850190845b81811015611f0f5783517fffffffff000000000000000000000000000000000000000000000000000000001683529284019291840191600101611ed1565b50909695505050505050565b60008060408385031215611f2e57600080fd5b611f3783611dd5565b9150611f4560208401611e14565b90509250929050565b60008060008060008060608789031215611f6757600080fd5b863567ffffffffffffffff80821115611f7f57600080fd5b611f8b8a838b01611cf3565b90985096506020890135915080821115611fa457600080fd5b611fb08a838b01611cf3565b90965094506040890135915080821115611fc957600080fd5b50611fd689828a01611cf3565b979a9699509497509295939492505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261207b57600080fd5b83018035915067ffffffffffffffff82111561209657600080fd5b6020019150600581901b3603821315611d3857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361210e5761210e6120ae565b5060010190565b600081612124576121246120ae565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b818103818111156103c6576103c66120ae565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea2646970667358221220ef50cc718134d3f54bddd8446f4617dc3cf0e2fefd4dd72daa5c2a6b970907be64736f6c63430008110033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
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.