ETH Price: $3,260.20 (+4.69%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Token Transfers found.

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FLAction

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2024-12-03
*/

// SPDX-License-Identifier: MIT
pragma solidity =0.8.24;










contract MainnetActionsUtilAddresses {
    address internal constant DFS_REG_CONTROLLER_ADDR = 0xF8f8B3C98Cf2E63Df3041b73f80F362a4cf3A576;
    address internal constant REGISTRY_ADDR = 0x287778F121F134C66212FB16c9b53eC991D32f5b;
    address internal constant DFS_LOGGER_ADDR = 0xcE7a977Cac4a481bc84AC06b2Da0df614e621cf3;
    address internal constant SUB_STORAGE_ADDR = 0x1612fc28Ee0AB882eC99842Cde0Fc77ff0691e90;
    address internal constant PROXY_AUTH_ADDR = 0x149667b6FAe2c63D1B4317C716b0D0e4d3E2bD70;
    address internal constant LSV_PROXY_REGISTRY_ADDRESS = 0xa8a3c86c4A2DcCf350E84D2b3c46BDeBc711C16e;
    address internal constant TRANSIENT_STORAGE = 0x2F7Ef2ea5E8c97B8687CA703A0e50Aa5a49B7eb2;
}







contract ActionsUtilHelper is MainnetActionsUtilAddresses {
}







contract MainnetAuthAddresses {
    address internal constant ADMIN_VAULT_ADDR = 0xCCf3d848e08b94478Ed8f46fFead3008faF581fD;
    address internal constant DSGUARD_FACTORY_ADDRESS = 0x5a15566417e6C1c9546523066500bDDBc53F88C7;
    address internal constant ADMIN_ADDR = 0x25eFA336886C74eA8E282ac466BdCd0199f85BB9; // USED IN ADMIN VAULT CONSTRUCTOR
    address internal constant PROXY_AUTH_ADDRESS = 0x149667b6FAe2c63D1B4317C716b0D0e4d3E2bD70;
    address internal constant MODULE_AUTH_ADDRESS = 0x7407974DDBF539e552F1d051e44573090912CC3D;
}







contract AuthHelper is MainnetAuthAddresses {
}








contract AdminVault is AuthHelper {
    address public owner;
    address public admin;

    error SenderNotAdmin();

    constructor() {
        owner = msg.sender;
        admin = ADMIN_ADDR;
    }

    /// @notice Admin is able to change owner
    /// @param _owner Address of new owner
    function changeOwner(address _owner) public {
        if (admin != msg.sender){
            revert SenderNotAdmin();
        }
        owner = _owner;
    }

    /// @notice Admin is able to set new admin
    /// @param _admin Address of multisig that becomes new admin
    function changeAdmin(address _admin) public {
        if (admin != msg.sender){
            revert SenderNotAdmin();
        }
        admin = _admin;
    }

}







interface IERC20 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint256 digits);
    function totalSupply() external view returns (uint256 supply);

    function balanceOf(address _owner) external view returns (uint256 balance);

    function transfer(address _to, uint256 _value) external returns (bool success);

    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    ) external returns (bool success);

    function approve(address _spender, uint256 _value) external returns (bool success);

    function allowance(address _owner, address _spender) external view returns (uint256 remaining);

    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}







library Address {
    //insufficient balance
    error InsufficientBalance(uint256 available, uint256 required);
    //unable to send value, recipient may have reverted
    error SendingValueFail();
    //insufficient balance for call
    error InsufficientBalanceForCall(uint256 available, uint256 required);
    //call to non-contract
    error NonContractCall();
    
    function isContract(address account) internal view returns (bool) {
        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            codehash := extcodehash(account)
        }
        return (codehash != accountHash && codehash != 0x0);
    }

    function sendValue(address payable recipient, uint256 amount) internal {
        uint256 balance = address(this).balance;
        if (balance < amount){
            revert InsufficientBalance(balance, amount);
        }

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{value: amount}("");
        if (!(success)){
            revert SendingValueFail();
        }
    }

    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return _functionCallWithValue(target, data, 0, errorMessage);
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return
            functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        uint256 balance = address(this).balance;
        if (balance < value){
            revert InsufficientBalanceForCall(balance, value);
        }
        return _functionCallWithValue(target, data, value, errorMessage);
    }

    function _functionCallWithValue(
        address target,
        bytes memory data,
        uint256 weiValue,
        string memory errorMessage
    ) private returns (bytes memory) {
        if (!(isContract(target))){
            revert NonContractCall();
        }

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{value: weiValue}(data);
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}











library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
     * 0 before setting it to a non-zero value.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}











contract AdminAuth is AuthHelper {
    using SafeERC20 for IERC20;

    AdminVault public constant adminVault = AdminVault(ADMIN_VAULT_ADDR);

    error SenderNotOwner();
    error SenderNotAdmin();

    modifier onlyOwner() {
        if (adminVault.owner() != msg.sender){
            revert SenderNotOwner();
        }
        _;
    }

    modifier onlyAdmin() {
        if (adminVault.admin() != msg.sender){
            revert SenderNotAdmin();
        }
        _;
    }

    /// @notice withdraw stuck funds
    function withdrawStuckFunds(address _token, address _receiver, uint256 _amount) public onlyOwner {
        if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
            payable(_receiver).transfer(_amount);
        } else {
            IERC20(_token).safeTransfer(_receiver, _amount);
        }
    }

    /// @notice Destroy the contract
    /// @dev Deprecated method, selfdestruct will soon just send eth
    function kill() public onlyAdmin {
        selfdestruct(payable(msg.sender));
    }
}








contract DFSRegistry is AdminAuth {
    error EntryAlreadyExistsError(bytes4);
    error EntryNonExistentError(bytes4);
    error EntryNotInChangeError(bytes4);
    error ChangeNotReadyError(uint256,uint256);
    error EmptyPrevAddrError(bytes4);
    error AlreadyInContractChangeError(bytes4);
    error AlreadyInWaitPeriodChangeError(bytes4);

    event AddNewContract(address,bytes4,address,uint256);
    event RevertToPreviousAddress(address,bytes4,address,address);
    event StartContractChange(address,bytes4,address,address);
    event ApproveContractChange(address,bytes4,address,address);
    event CancelContractChange(address,bytes4,address,address);
    event StartWaitPeriodChange(address,bytes4,uint256);
    event ApproveWaitPeriodChange(address,bytes4,uint256,uint256);
    event CancelWaitPeriodChange(address,bytes4,uint256,uint256);

    struct Entry {
        address contractAddr;
        uint256 waitPeriod;
        uint256 changeStartTime;
        bool inContractChange;
        bool inWaitPeriodChange;
        bool exists;
    }

    mapping(bytes4 => Entry) public entries;
    mapping(bytes4 => address) public previousAddresses;

    mapping(bytes4 => address) public pendingAddresses;
    mapping(bytes4 => uint256) public pendingWaitTimes;

    /// @notice Given an contract id returns the registered address
    /// @dev Id is keccak256 of the contract name
    /// @param _id Id of contract
    function getAddr(bytes4 _id) public view returns (address) {
        return entries[_id].contractAddr;
    }

    /// @notice Helper function to easily query if id is registered
    /// @param _id Id of contract
    function isRegistered(bytes4 _id) public view returns (bool) {
        return entries[_id].exists;
    }

    /////////////////////////// OWNER ONLY FUNCTIONS ///////////////////////////

    /// @notice Adds a new contract to the registry
    /// @param _id Id of contract
    /// @param _contractAddr Address of the contract
    /// @param _waitPeriod Amount of time to wait before a contract address can be changed
    function addNewContract(
        bytes4 _id,
        address _contractAddr,
        uint256 _waitPeriod
    ) public onlyOwner {
        if (entries[_id].exists){
            revert EntryAlreadyExistsError(_id);
        }

        entries[_id] = Entry({
            contractAddr: _contractAddr,
            waitPeriod: _waitPeriod,
            changeStartTime: 0,
            inContractChange: false,
            inWaitPeriodChange: false,
            exists: true
        });

        emit AddNewContract(msg.sender, _id, _contractAddr, _waitPeriod);
    }

    /// @notice Reverts to the previous address immediately
    /// @dev In case the new version has a fault, a quick way to fallback to the old contract
    /// @param _id Id of contract
    function revertToPreviousAddress(bytes4 _id) public onlyOwner {
        if (!(entries[_id].exists)){
            revert EntryNonExistentError(_id);
        }
        if (previousAddresses[_id] == address(0)){
            revert EmptyPrevAddrError(_id);
        }

        address currentAddr = entries[_id].contractAddr;
        entries[_id].contractAddr = previousAddresses[_id];

        emit RevertToPreviousAddress(msg.sender, _id, currentAddr, previousAddresses[_id]);
    }

    /// @notice Starts an address change for an existing entry
    /// @dev Can override a change that is currently in progress
    /// @param _id Id of contract
    /// @param _newContractAddr Address of the new contract
    function startContractChange(bytes4 _id, address _newContractAddr) public onlyOwner {
        if (!entries[_id].exists){
            revert EntryNonExistentError(_id);
        }
        if (entries[_id].inWaitPeriodChange){
            revert AlreadyInWaitPeriodChangeError(_id);
        }

        entries[_id].changeStartTime = block.timestamp; // solhint-disable-line
        entries[_id].inContractChange = true;

        pendingAddresses[_id] = _newContractAddr;

        emit StartContractChange(msg.sender, _id, entries[_id].contractAddr, _newContractAddr);
    }

    /// @notice Changes new contract address, correct time must have passed
    /// @param _id Id of contract
    function approveContractChange(bytes4 _id) public onlyOwner {
        if (!entries[_id].exists){
            revert EntryNonExistentError(_id);
        }
        if (!entries[_id].inContractChange){
            revert EntryNotInChangeError(_id);
        }
        if (block.timestamp < (entries[_id].changeStartTime + entries[_id].waitPeriod)){// solhint-disable-line
            revert ChangeNotReadyError(block.timestamp, (entries[_id].changeStartTime + entries[_id].waitPeriod));
        }

        address oldContractAddr = entries[_id].contractAddr;
        entries[_id].contractAddr = pendingAddresses[_id];
        entries[_id].inContractChange = false;
        entries[_id].changeStartTime = 0;

        pendingAddresses[_id] = address(0);
        previousAddresses[_id] = oldContractAddr;

        emit ApproveContractChange(msg.sender, _id, oldContractAddr, entries[_id].contractAddr);
    }

    /// @notice Cancel pending change
    /// @param _id Id of contract
    function cancelContractChange(bytes4 _id) public onlyOwner {
        if (!entries[_id].exists){
            revert EntryNonExistentError(_id);
        }
        if (!entries[_id].inContractChange){
            revert EntryNotInChangeError(_id);
        }

        address oldContractAddr = pendingAddresses[_id];

        pendingAddresses[_id] = address(0);
        entries[_id].inContractChange = false;
        entries[_id].changeStartTime = 0;

        emit CancelContractChange(msg.sender, _id, oldContractAddr, entries[_id].contractAddr);
    }

    /// @notice Starts the change for waitPeriod
    /// @param _id Id of contract
    /// @param _newWaitPeriod New wait time
    function startWaitPeriodChange(bytes4 _id, uint256 _newWaitPeriod) public onlyOwner {
        if (!entries[_id].exists){
            revert EntryNonExistentError(_id);
        }
        if (entries[_id].inContractChange){
            revert AlreadyInContractChangeError(_id);
        }

        pendingWaitTimes[_id] = _newWaitPeriod;

        entries[_id].changeStartTime = block.timestamp; // solhint-disable-line
        entries[_id].inWaitPeriodChange = true;

        emit StartWaitPeriodChange(msg.sender, _id, _newWaitPeriod);
    }

    /// @notice Changes new wait period, correct time must have passed
    /// @param _id Id of contract
    function approveWaitPeriodChange(bytes4 _id) public onlyOwner {
        if (!entries[_id].exists){
            revert EntryNonExistentError(_id);
        }
        if (!entries[_id].inWaitPeriodChange){
            revert EntryNotInChangeError(_id);
        }
        if (block.timestamp < (entries[_id].changeStartTime + entries[_id].waitPeriod)){ // solhint-disable-line
            revert ChangeNotReadyError(block.timestamp, (entries[_id].changeStartTime + entries[_id].waitPeriod));
        }

        uint256 oldWaitTime = entries[_id].waitPeriod;
        entries[_id].waitPeriod = pendingWaitTimes[_id];
        
        entries[_id].inWaitPeriodChange = false;
        entries[_id].changeStartTime = 0;

        pendingWaitTimes[_id] = 0;

        emit ApproveWaitPeriodChange(msg.sender, _id, oldWaitTime, entries[_id].waitPeriod);
    }

    /// @notice Cancel wait period change
    /// @param _id Id of contract
    function cancelWaitPeriodChange(bytes4 _id) public onlyOwner {
        if (!entries[_id].exists){
            revert EntryNonExistentError(_id);
        }
        if (!entries[_id].inWaitPeriodChange){
            revert EntryNotInChangeError(_id);
        }

        uint256 oldWaitPeriod = pendingWaitTimes[_id];

        pendingWaitTimes[_id] = 0;
        entries[_id].inWaitPeriodChange = false;
        entries[_id].changeStartTime = 0;

        emit CancelWaitPeriodChange(msg.sender, _id, oldWaitPeriod, entries[_id].waitPeriod);
    }
}







abstract contract DSAuthority {
    function canCall(
        address src,
        address dst,
        bytes4 sig
    ) public view virtual returns (bool);
}







contract DSAuthEvents {
    event LogSetAuthority(address indexed authority);
    event LogSetOwner(address indexed owner);
}

contract DSAuth is DSAuthEvents {
    DSAuthority public authority;
    address public owner;

    constructor() {
        owner = msg.sender;
        emit LogSetOwner(msg.sender);
    }

    function setOwner(address owner_) public auth {
        owner = owner_;
        emit LogSetOwner(owner);
    }

    function setAuthority(DSAuthority authority_) public auth {
        authority = authority_;
        emit LogSetAuthority(address(authority));
    }

    modifier auth {
        require(isAuthorized(msg.sender, msg.sig), "Not authorized");
        _;
    }

    function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
        if (src == address(this)) {
            return true;
        } else if (src == owner) {
            return true;
        } else if (authority == DSAuthority(address(0))) {
            return false;
        } else {
            return authority.canCall(src, address(this), sig);
        }
    }
}







contract DSNote {
    event LogNote(
        bytes4 indexed sig,
        address indexed guy,
        bytes32 indexed foo,
        bytes32 indexed bar,
        uint256 wad,
        bytes fax
    ) anonymous;

    modifier note {
        bytes32 foo;
        bytes32 bar;

        assembly {
            foo := calldataload(4)
            bar := calldataload(36)
        }

        emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);

        _;
    }
}








abstract contract DSProxy is DSAuth, DSNote {
    DSProxyCache public cache; // global cache for contracts

    constructor(address _cacheAddr) {
        if (!(setCache(_cacheAddr))){
            require(isAuthorized(msg.sender, msg.sig), "Not authorized");
        }
    }

    // solhint-disable-next-line no-empty-blocks
    receive() external payable {}

    // use the proxy to execute calldata _data on contract _code
    function execute(bytes memory _code, bytes memory _data)
        public
        payable
        virtual
        returns (address target, bytes32 response);

    function execute(address _target, bytes memory _data)
        public
        payable
        virtual
        returns (bytes32 response);

    //set new cache
    function setCache(address _cacheAddr) public payable virtual returns (bool);
}

contract DSProxyCache {
    mapping(bytes32 => address) cache;

    function read(bytes memory _code) public view returns (address) {
        bytes32 hash = keccak256(_code);
        return cache[hash];
    }

    function write(bytes memory _code) public returns (address target) {
        assembly {
            target := create(0, add(_code, 0x20), mload(_code))
            switch iszero(extcodesize(target))
                case 1 {
                    // throw if contract failed to deploy
                    revert(0, 0)
                }
        }
        bytes32 hash = keccak256(_code);
        cache[hash] = target;
    }
}







interface ISafe {
    enum Operation {
        Call,
        DelegateCall
    }

    function setup(
        address[] calldata _owners,
        uint256 _threshold,
        address to,
        bytes calldata data,
        address fallbackHandler,
        address paymentToken,
        uint256 payment,
        address payable paymentReceiver
    ) external;

    function execTransaction(
        address to,
        uint256 value,
        bytes calldata data,
        Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address payable refundReceiver,
        bytes memory signatures
    ) external payable returns (bool success);

    function execTransactionFromModule(
        address to,
        uint256 value,
        bytes memory data,
        Operation operation
    ) external returns (bool success);

    function checkSignatures(
        bytes32 dataHash,
        bytes memory data,
        bytes memory signatures
    ) external view;

    function checkNSignatures(
        address executor,
        bytes32 dataHash,
        bytes memory /* data */,
        bytes memory signatures,
        uint256 requiredSignatures
    ) external view;

    function approveHash(bytes32 hashToApprove) external;

    function domainSeparator() external view returns (bytes32);

    function getTransactionHash(
        address to,
        uint256 value,
        bytes calldata data,
        Operation operation,
        uint256 safeTxGas,
        uint256 baseGas,
        uint256 gasPrice,
        address gasToken,
        address refundReceiver,
        uint256 _nonce
    ) external view returns (bytes32);

    function nonce() external view returns (uint256);

    function setFallbackHandler(address handler) external;

    function getOwners() external view returns (address[] memory);

    function isOwner(address owner) external view returns (bool);

    function getThreshold() external view returns (uint256);

    function enableModule(address module) external;

    function isModuleEnabled(address module) external view returns (bool);

    function disableModule(address prevModule, address module) external;

    function getModulesPaginated(
        address start,
        uint256 pageSize
    ) external view returns (address[] memory array, address next);
}







interface IDSProxyFactory {
    function isProxy(address _proxy) external view returns (bool);
}







contract MainnetProxyFactoryAddresses {
    address internal constant PROXY_FACTORY_ADDR = 0xA26e15C895EFc0616177B7c1e7270A4C7D51C997;
}







contract DSProxyFactoryHelper is MainnetProxyFactoryAddresses {
}









contract CheckWalletType is DSProxyFactoryHelper {
    function isDSProxy(address _proxy) public view returns (bool) {
        return IDSProxyFactory(PROXY_FACTORY_ADDR).isProxy(_proxy);
    }
}







contract DefisaverLogger {
    event RecipeEvent(
        address indexed caller,
        string indexed logName
    );

    event ActionDirectEvent(
        address indexed caller,
        string indexed logName,
        bytes data
    );

    function logRecipeEvent(
        string memory _logName
    ) public {
        emit RecipeEvent(msg.sender, _logName);
    }

    function logActionDirectEvent(
        string memory _logName,
        bytes memory _data
    ) public {
        emit ActionDirectEvent(msg.sender, _logName, _data);
    }
}













abstract contract ActionBase is AdminAuth, ActionsUtilHelper, CheckWalletType {
    event ActionEvent(
        string indexed logName,
        bytes data
    );

    DFSRegistry public constant registry = DFSRegistry(REGISTRY_ADDR);

    DefisaverLogger public constant logger = DefisaverLogger(
        DFS_LOGGER_ADDR
    );

    //Wrong sub index value
    error SubIndexValueError();
    //Wrong return index value
    error ReturnIndexValueError();

    /// @dev Subscription params index range [128, 255]
    uint8 public constant SUB_MIN_INDEX_VALUE = 128;
    uint8 public constant SUB_MAX_INDEX_VALUE = 255;

    /// @dev Return params index range [1, 127]
    uint8 public constant RETURN_MIN_INDEX_VALUE = 1;
    uint8 public constant RETURN_MAX_INDEX_VALUE = 127;

    /// @dev If the input value should not be replaced
    uint8 public constant NO_PARAM_MAPPING = 0;

    /// @dev We need to parse Flash loan actions in a different way
    enum ActionType { FL_ACTION, STANDARD_ACTION, FEE_ACTION, CHECK_ACTION, CUSTOM_ACTION }

    /// @notice Parses inputs and runs the implemented action through a user wallet
    /// @dev Is called by the RecipeExecutor chaining actions together
    /// @param _callData Array of input values each value encoded as bytes
    /// @param _subData Array of subscribed vales, replaces input values if specified
    /// @param _paramMapping Array that specifies how return and subscribed values are mapped in input
    /// @param _returnValues Returns values from actions before, which can be injected in inputs
    /// @return Returns a bytes32 value through user wallet, each actions implements what that value is
    function executeAction(
        bytes memory _callData,
        bytes32[] memory _subData,
        uint8[] memory _paramMapping,
        bytes32[] memory _returnValues
    ) public payable virtual returns (bytes32);

    /// @notice Parses inputs and runs the single implemented action through a user wallet
    /// @dev Used to save gas when executing a single action directly
    function executeActionDirect(bytes memory _callData) public virtual payable;

    /// @notice Returns the type of action we are implementing
    function actionType() public pure virtual returns (uint8);


    //////////////////////////// HELPER METHODS ////////////////////////////

    /// @notice Given an uint256 input, injects return/sub values if specified
    /// @param _param The original input value
    /// @param _mapType Indicated the type of the input in paramMapping
    /// @param _subData Array of subscription data we can replace the input value with
    /// @param _returnValues Array of subscription data we can replace the input value with
    function _parseParamUint(
        uint _param,
        uint8 _mapType,
        bytes32[] memory _subData,
        bytes32[] memory _returnValues
    ) internal pure returns (uint) {
        if (isReplaceable(_mapType)) {
            if (isReturnInjection(_mapType)) {
                _param = uint(_returnValues[getReturnIndex(_mapType)]);
            } else {
                _param = uint256(_subData[getSubIndex(_mapType)]);
            }
        }

        return _param;
    }


    /// @notice Given an addr input, injects return/sub values if specified
    /// @param _param The original input value
    /// @param _mapType Indicated the type of the input in paramMapping
    /// @param _subData Array of subscription data we can replace the input value with
    /// @param _returnValues Array of subscription data we can replace the input value with
    function _parseParamAddr(
        address _param,
        uint8 _mapType,
        bytes32[] memory _subData,
        bytes32[] memory _returnValues
    ) internal view returns (address) {
        if (isReplaceable(_mapType)) {
            if (isReturnInjection(_mapType)) {
                _param = address(bytes20((_returnValues[getReturnIndex(_mapType)])));
            } else {
                /// @dev The last two values are specially reserved for proxy addr and owner addr
                if (_mapType == 254) return address(this); // wallet address
                if (_mapType == 255) return fetchOwnersOrWallet(); // owner if 1/1 wallet or the wallet itself

                _param = address(uint160(uint256(_subData[getSubIndex(_mapType)])));
            }
        }

        return _param;
    }

    /// @notice Given an bytes32 input, injects return/sub values if specified
    /// @param _param The original input value
    /// @param _mapType Indicated the type of the input in paramMapping
    /// @param _subData Array of subscription data we can replace the input value with
    /// @param _returnValues Array of subscription data we can replace the input value with
    function _parseParamABytes32(
        bytes32 _param,
        uint8 _mapType,
        bytes32[] memory _subData,
        bytes32[] memory _returnValues
    ) internal pure returns (bytes32) {
        if (isReplaceable(_mapType)) {
            if (isReturnInjection(_mapType)) {
                _param = (_returnValues[getReturnIndex(_mapType)]);
            } else {
                _param = _subData[getSubIndex(_mapType)];
            }
        }

        return _param;
    }

    /// @notice Checks if the paramMapping value indicated that we need to inject values
    /// @param _type Indicated the type of the input
    function isReplaceable(uint8 _type) internal pure returns (bool) {
        return _type != NO_PARAM_MAPPING;
    }

    /// @notice Checks if the paramMapping value is in the return value range
    /// @param _type Indicated the type of the input
    function isReturnInjection(uint8 _type) internal pure returns (bool) {
        return (_type >= RETURN_MIN_INDEX_VALUE) && (_type <= RETURN_MAX_INDEX_VALUE);
    }

    /// @notice Transforms the paramMapping value to the index in return array value
    /// @param _type Indicated the type of the input
    function getReturnIndex(uint8 _type) internal pure returns (uint8) {
        if (!(isReturnInjection(_type))){
            revert SubIndexValueError();
        }

        return (_type - RETURN_MIN_INDEX_VALUE);
    }

    /// @notice Transforms the paramMapping value to the index in sub array value
    /// @param _type Indicated the type of the input
    function getSubIndex(uint8 _type) internal pure returns (uint8) {
        if (_type < SUB_MIN_INDEX_VALUE){
            revert ReturnIndexValueError();
        }
        return (_type - SUB_MIN_INDEX_VALUE);
    }

    function fetchOwnersOrWallet() internal view returns (address) {
        if (isDSProxy(address(this))) 
            return DSProxy(payable(address(this))).owner();

        // if not DSProxy, we assume we are in context of Safe
        address[] memory owners = ISafe(address(this)).getOwners();
        return owners.length == 1 ? owners[0] : address(this);
    }
}







contract MainnetFLAddresses {
    address internal constant SOLO_MARGIN_ADDRESS = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;
    address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address internal constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address internal constant DYDX_FL_FEE_FAUCET = 0x47f159C90850D5cE09E21F931d504536840f34b4;

    address internal constant AAVE_LENDING_POOL = 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9;
    address internal constant AAVE_LENDING_POOL_ADDRESS_PROVIDER = 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5;

    address internal constant AAVE_V3_LENDING_POOL = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2;
    address internal constant AAVE_V3_LENDING_POOL_ADDRESS_PROVIDER = 0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e;

    address internal constant SPARK_LENDING_POOL_ADDRESS_PROVIDER = 0x02C3eA4e34C0cBd694D2adFa2c690EECbC1793eE;
    address internal constant SPARK_LENDING_POOL = 0xC13e21B648A5Ee794902342038FF3aDAB66BE987;

    address internal constant DSS_FLASH_ADDR = 0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA;
    address internal constant DAI_ADDR = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    address internal constant ST_ETH_ADDR = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
    address internal constant VAULT_ADDR = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;

    address internal constant GHO_ADDR = 0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f;
    address internal constant GHO_FLASH_MINTER_ADDR = 0xb639D208Bcf0589D54FaC24E655C79EC529762B8;
    address internal constant UNI_V3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984;

    address internal constant MORPHO_BLUE_ADDR = 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb;

    address internal constant CURVEUSD_FLASH_ADDR = 0xA7a4bb50AF91f90b6fEb3388E7f8286aF45b299B;
    address internal constant CURVEUSD_ADDR = 0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E;

    address internal constant RECIPE_EXECUTOR_ADDR = 0x5029336642814bC51a42bA80BF83a6322110035D;
}









contract StrategyModel {
        
    /// @dev Group of strategies bundled together so user can sub to multiple strategies at once
    /// @param creator Address of the user who created the bundle
    /// @param strategyIds Array of strategy ids stored in StrategyStorage
    struct StrategyBundle {
        address creator;
        uint64[] strategyIds;
    }

    /// @dev Template/Class which defines a Strategy
    /// @param name Name of the strategy useful for logging what strategy is executing
    /// @param creator Address of the user which created the strategy
    /// @param triggerIds Array of identifiers for trigger - bytes4(keccak256(TriggerName))
    /// @param actionIds Array of identifiers for actions - bytes4(keccak256(ActionName))
    /// @param paramMapping Describes how inputs to functions are piped from return/subbed values
    /// @param continuous If the action is repeated (continuos) or one time
    struct Strategy {
        string name;
        address creator;
        bytes4[] triggerIds;
        bytes4[] actionIds;
        uint8[][] paramMapping;
        bool continuous;
    }

    /// @dev List of actions grouped as a recipe
    /// @param name Name of the recipe useful for logging what recipe is executing
    /// @param callData Array of calldata inputs to each action
    /// @param subData Used only as part of strategy, subData injected from StrategySub.subData
    /// @param actionIds Array of identifiers for actions - bytes4(keccak256(ActionName))
    /// @param paramMapping Describes how inputs to functions are piped from return/subbed values
    struct Recipe {
        string name;
        bytes[] callData;
        bytes32[] subData;
        bytes4[] actionIds;
        uint8[][] paramMapping;
    }

    /// @dev Actual data of the sub we store on-chain
    /// @dev In order to save on gas we store a keccak256(StrategySub) and verify later on
    /// @param walletAddr Address of the users smart wallet/proxy
    /// @param isEnabled Toggle if the subscription is active
    /// @param strategySubHash Hash of the StrategySub data the user inputted
    struct StoredSubData {
        bytes20 walletAddr; // address but put in bytes20 for gas savings
        bool isEnabled;
        bytes32 strategySubHash;
    }

    /// @dev Instance of a strategy, user supplied data
    /// @param strategyOrBundleId Id of the strategy or bundle, depending on the isBundle bool
    /// @param isBundle If true the id points to bundle, if false points directly to strategyId
    /// @param triggerData User supplied data needed for checking trigger conditions
    /// @param subData User supplied data used in recipe
    struct StrategySub {
        uint64 strategyOrBundleId;
        bool isBundle;
        bytes[] triggerData;
        bytes32[] subData;
    }

    /// @dev Data needed when signing relay transaction
    /// @param maxTxCostInFeeToken Max tx cost user is willing to pay in fee token
    /// @param feeToken Address of the token user is willing to pay fee in
    /// @param tokenPriceInEth Price of the token in ETH
    /// @param deadline Deadline for the relay transaction to be executed
    /// @param shouldTakeFeeFromPosition Flag to indicate if fee should be taken from position, otherwise from EOA/wallet
    struct TxSaverSignedData {
        uint256 maxTxCostInFeeToken;
        address feeToken;
        uint256 tokenPriceInEth;
        uint256 deadline;
        bool shouldTakeFeeFromPosition;
    }
}







abstract contract IDSProxy {
    // function execute(bytes memory _code, bytes memory _data)
    //     public
    //     payable
    //     virtual
    //     returns (address, bytes32);

    function execute(address _target, bytes memory _data) public payable virtual returns (bytes32);

    function setCache(address _cacheAddr) public payable virtual returns (bool);

    function owner() public view virtual returns (address);
}









contract FLFeeFaucet {

    using SafeERC20 for IERC20;

    /// @notice Sends 2 wei to msg.sender
    /// @dev Anyone can call this method but it's not economically feasible to drain
    /// @param _tokenAddr Address of the token we want 2 wei
    function my2Wei(address _tokenAddr) public {
        IERC20(_tokenAddr).safeTransfer(msg.sender, 2);
    }
}











contract FLHelper is MainnetFLAddresses, StrategyModel {
    uint16 internal constant AAVE_REFERRAL_CODE = 64;
    uint16 internal constant SPARK_REFERRAL_CODE = 0;

    FLFeeFaucet public constant flFeeFaucet = FLFeeFaucet(DYDX_FL_FEE_FAUCET);

    /// @dev Function sig of RecipeExecutor._executeActionsFromFL()
    bytes4 public constant CALLBACK_SELECTOR =
        bytes4(
            keccak256(
                "_executeActionsFromFL((string,bytes[],bytes32[],bytes4[],uint8[][]),bytes32)"
            )
        );

    // Revert if execution fails when using safe wallet
    error SafeExecutionError();

    function _executeRecipe(address _wallet, bool _isDSProxy, Recipe memory _currRecipe, uint256 _paybackAmount) internal {
        if (_isDSProxy) {
            IDSProxy(_wallet).execute{value: address(this).balance}(
                RECIPE_EXECUTOR_ADDR,
                abi.encodeWithSelector(CALLBACK_SELECTOR, _currRecipe, _paybackAmount)
            );
        } else {
            bool success = ISafe(_wallet).execTransactionFromModule(
                RECIPE_EXECUTOR_ADDR,
                address(this).balance,
                abi.encodeWithSelector(CALLBACK_SELECTOR, _currRecipe, _paybackAmount),
                ISafe.Operation.DelegateCall
            );

            if (!success) {
                revert SafeExecutionError();
             }
        }
    }
}







interface ILendingPoolAddressesProviderV2 {
  event LendingPoolUpdated(address indexed newAddress);
  event ConfigurationAdminUpdated(address indexed newAddress);
  event EmergencyAdminUpdated(address indexed newAddress);
  event LendingPoolConfiguratorUpdated(address indexed newAddress);
  event LendingPoolCollateralManagerUpdated(address indexed newAddress);
  event PriceOracleUpdated(address indexed newAddress);
  event LendingRateOracleUpdated(address indexed newAddress);
  event ProxyCreated(bytes32 id, address indexed newAddress);
  event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy);

  function setAddress(bytes32 id, address newAddress) external;

  function setAddressAsProxy(bytes32 id, address impl) external;

  function getAddress(bytes32 id) external view returns (address);

  function getLendingPool() external view returns (address);

  function setLendingPoolImpl(address pool) external;

  function getLendingPoolConfigurator() external view returns (address);

  function setLendingPoolConfiguratorImpl(address configurator) external;

  function getLendingPoolCollateralManager() external view returns (address);

  function setLendingPoolCollateralManager(address manager) external;

  function getPoolAdmin() external view returns (address);

  function setPoolAdmin(address admin) external;

  function getEmergencyAdmin() external view returns (address);

  function setEmergencyAdmin(address admin) external;

  function getPriceOracle() external view returns (address);

  function setPriceOracle(address priceOracle) external;

  function getLendingRateOracle() external view returns (address);

  function setLendingRateOracle(address lendingRateOracle) external;
}






library DataTypes {
  // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
  struct ReserveData {
    //stores the reserve configuration
    ReserveConfigurationMap configuration;
    //the liquidity index. Expressed in ray
    uint128 liquidityIndex;
    //variable borrow index. Expressed in ray
    uint128 variableBorrowIndex;
    //the current supply rate. Expressed in ray
    uint128 currentLiquidityRate;
    //the current variable borrow rate. Expressed in ray
    uint128 currentVariableBorrowRate;
    //the current stable borrow rate. Expressed in ray
    uint128 currentStableBorrowRate;
    uint40 lastUpdateTimestamp;
    //tokens addresses
    address aTokenAddress;
    address stableDebtTokenAddress;
    address variableDebtTokenAddress;
    //address of the interest rate strategy
    address interestRateStrategyAddress;
    //the id of the reserve. Represents the position in the list of the active reserves
    uint8 id;
  }

  struct ReserveConfigurationMap {
    //bit 0-15: LTV
    //bit 16-31: Liq. threshold
    //bit 32-47: Liq. bonus
    //bit 48-55: Decimals
    //bit 56: Reserve is active
    //bit 57: reserve is frozen
    //bit 58: borrowing is enabled
    //bit 59: stable rate borrowing enabled
    //bit 60-63: reserved
    //bit 64-79: reserve factor
    uint256 data;
  }

  struct UserConfigurationMap {
    uint256 data;
  }

  enum InterestRateMode {NONE, STABLE, VARIABLE}
}

interface ILendingPoolV2 {
  /**
   * @dev Emitted on deposit()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The address initiating the deposit
   * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens
   * @param amount The amount deposited
   * @param referral The referral code used
   **/
  event Deposit(
    address indexed reserve,
    address user,
    address indexed onBehalfOf,
    uint256 amount,
    uint16 indexed referral
  );

  /**
   * @dev Emitted on withdraw()
   * @param reserve The address of the underlyng asset being withdrawn
   * @param user The address initiating the withdrawal, owner of aTokens
   * @param to Address that will receive the underlying
   * @param amount The amount to be withdrawn
   **/
  event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);

  /**
   * @dev Emitted on borrow() and flashLoan() when debt needs to be opened
   * @param reserve The address of the underlying asset being borrowed
   * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
   * initiator of the transaction on flashLoan()
   * @param onBehalfOf The address that will be getting the debt
   * @param amount The amount borrowed out
   * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable
   * @param borrowRate The numeric rate at which the user has borrowed
   * @param referral The referral code used
   **/
  event Borrow(
    address indexed reserve,
    address user,
    address indexed onBehalfOf,
    uint256 amount,
    uint256 borrowRateMode,
    uint256 borrowRate,
    uint16 indexed referral
  );

  /**
   * @dev Emitted on repay()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The beneficiary of the repayment, getting his debt reduced
   * @param repayer The address of the user initiating the repay(), providing the funds
   * @param amount The amount repaid
   **/
  event Repay(
    address indexed reserve,
    address indexed user,
    address indexed repayer,
    uint256 amount
  );

  /**
   * @dev Emitted on swapBorrowRateMode()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The address of the user swapping his rate mode
   * @param rateMode The rate mode that the user wants to swap to
   **/
  event Swap(address indexed reserve, address indexed user, uint256 rateMode);

  /**
   * @dev Emitted on setUserUseReserveAsCollateral()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The address of the user enabling the usage as collateral
   **/
  event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);

  /**
   * @dev Emitted on setUserUseReserveAsCollateral()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The address of the user enabling the usage as collateral
   **/
  event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);

  /**
   * @dev Emitted on rebalanceStableBorrowRate()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The address of the user for which the rebalance has been executed
   **/
  event RebalanceStableBorrowRate(address indexed reserve, address indexed user);

  /**
   * @dev Emitted on flashLoan()
   * @param target The address of the flash loan receiver contract
   * @param initiator The address initiating the flash loan
   * @param asset The address of the asset being flash borrowed
   * @param amount The amount flash borrowed
   * @param premium The fee flash borrowed
   * @param referralCode The referral code used
   **/
  event FlashLoan(
    address indexed target,
    address indexed initiator,
    address indexed asset,
    uint256 amount,
    uint256 premium,
    uint16 referralCode
  );

  /**
   * @dev Emitted when the pause is triggered.
   */
  event Paused();

  /**
   * @dev Emitted when the pause is lifted.
   */
  event Unpaused();

  /**
   * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via
   * LendingPoolCollateral manager using a DELEGATECALL
   * This allows to have the events in the generated ABI for LendingPool.
   * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
   * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
   * @param user The address of the borrower getting liquidated
   * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
   * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator
   * @param liquidator The address of the liquidator
   * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
   * to receive the underlying collateral asset directly
   **/
  event LiquidationCall(
    address indexed collateralAsset,
    address indexed debtAsset,
    address indexed user,
    uint256 debtToCover,
    uint256 liquidatedCollateralAmount,
    address liquidator,
    bool receiveAToken
  );

  /**
   * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared
   * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal,
   * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it
   * gets added to the LendingPool ABI
   * @param reserve The address of the underlying asset of the reserve
   * @param liquidityRate The new liquidity rate
   * @param stableBorrowRate The new stable borrow rate
   * @param variableBorrowRate The new variable borrow rate
   * @param liquidityIndex The new liquidity index
   * @param variableBorrowIndex The new variable borrow index
   **/
  event ReserveDataUpdated(
    address indexed reserve,
    uint256 liquidityRate,
    uint256 stableBorrowRate,
    uint256 variableBorrowRate,
    uint256 liquidityIndex,
    uint256 variableBorrowIndex
  );

  /**
   * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
   * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
   * @param asset The address of the underlying asset to deposit
   * @param amount The amount to be deposited
   * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
   *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
   *   is a different wallet
   * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
   *   0 if the action is executed directly by the user, without any middle-man
   **/
  function deposit(
    address asset,
    uint256 amount,
    address onBehalfOf,
    uint16 referralCode
  ) external;

  /**
   * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
   * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
   * @param asset The address of the underlying asset to withdraw
   * @param amount The underlying amount to be withdrawn
   *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
   * @param to Address that will receive the underlying, same as msg.sender if the user
   *   wants to receive it on his own wallet, or a different address if the beneficiary is a
   *   different wallet
   **/
  function withdraw(
    address asset,
    uint256 amount,
    address to
  ) external;

  /**
   * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
   * already deposited enough collateral, or he was given enough allowance by a credit delegator on the
   * corresponding debt token (StableDebtToken or VariableDebtToken)
   * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
   *   and 100 stable/variable debt tokens, depending on the `interestRateMode`
   * @param asset The address of the underlying asset to borrow
   * @param amount The amount to be borrowed
   * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
   * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
   *   0 if the action is executed directly by the user, without any middle-man
   * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself
   * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
   * if he has been given credit delegation allowance
   **/
  function borrow(
    address asset,
    uint256 amount,
    uint256 interestRateMode,
    uint16 referralCode,
    address onBehalfOf
  ) external;

  /**
   * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
   * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
   * @param asset The address of the borrowed underlying asset previously borrowed
   * @param amount The amount to repay
   * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
   * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
   * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
   * user calling the function if he wants to reduce/remove his own debt, or the address of any other
   * other borrower whose debt should be removed
   **/
  function repay(
    address asset,
    uint256 amount,
    uint256 rateMode,
    address onBehalfOf
  ) external;

  /**
   * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
   * @param asset The address of the underlying asset borrowed
   * @param rateMode The rate mode that the user wants to swap to
   **/
  function swapBorrowRateMode(address asset, uint256 rateMode) external;

  /**
   * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
   * - Users can be rebalanced if the following conditions are satisfied:
   *     1. Usage ratio is above 95%
   *     2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
   *        borrowed at a stable rate and depositors are not earning enough
   * @param asset The address of the underlying asset borrowed
   * @param user The address of the user to be rebalanced
   **/
  function rebalanceStableBorrowRate(address asset, address user) external;

  /**
   * @dev Allows depositors to enable/disable a specific deposited asset as collateral
   * @param asset The address of the underlying asset deposited
   * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise
   **/
  function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;

  /**
   * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
   * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
   *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
   * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
   * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
   * @param user The address of the borrower getting liquidated
   * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
   * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
   * to receive the underlying collateral asset directly
   **/
  function liquidationCall(
    address collateralAsset,
    address debtAsset,
    address user,
    uint256 debtToCover,
    bool receiveAToken
  ) external;

  /**
   * @dev Allows smartcontracts to access the liquidity of the pool within one transaction,
   * as long as the amount taken plus a fee is returned.
   * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration.
   * For further details please visit https://developers.aave.com
   * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface
   * @param assets The addresses of the assets being flash-borrowed
   * @param amounts The amounts amounts being flash-borrowed
   * @param modes Types of the debt to open if the flash loan is not returned:
   *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
   *   1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
   *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
   * @param onBehalfOf The address  that will receive the debt in the case of using on `modes` 1 or 2
   * @param params Variadic packed params to pass to the receiver as extra information
   * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
   *   0 if the action is executed directly by the user, without any middle-man
   **/
  function flashLoan(
    address receiverAddress,
    address[] calldata assets,
    uint256[] calldata amounts,
    uint256[] calldata modes,
    address onBehalfOf,
    bytes calldata params,
    uint16 referralCode
  ) external;

  /**
   * @dev Returns the user account data across all the reserves
   * @param user The address of the user
   * @return totalCollateralETH the total collateral in ETH of the user
   * @return totalDebtETH the total debt in ETH of the user
   * @return availableBorrowsETH the borrowing power left of the user
   * @return currentLiquidationThreshold the liquidation threshold of the user
   * @return ltv the loan to value of the user
   * @return healthFactor the current health factor of the user
   **/
  function getUserAccountData(address user)
    external
    view
    returns (
      uint256 totalCollateralETH,
      uint256 totalDebtETH,
      uint256 availableBorrowsETH,
      uint256 currentLiquidationThreshold,
      uint256 ltv,
      uint256 healthFactor
    );

  function initReserve(
    address reserve,
    address aTokenAddress,
    address stableDebtAddress,
    address variableDebtAddress,
    address interestRateStrategyAddress
  ) external;

  function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress)
    external;

  function setConfiguration(address reserve, uint256 configuration) external;

  /**
   * @dev Returns the configuration of the reserve
   * @param asset The address of the underlying asset of the reserve
   * @return The configuration of the reserve
   **/
  function getConfiguration(address asset) external view returns (DataTypes.ReserveConfigurationMap memory);

  /**
   * @dev Returns the configuration of the user across all the reserves
   * @param user The user address
   * @return The configuration of the user
   **/
  function getUserConfiguration(address user) external view returns (DataTypes.UserConfigurationMap memory);

  /**
   * @dev Returns the normalized income normalized income of the reserve
   * @param asset The address of the underlying asset of the reserve
   * @return The reserve's normalized income
   */
  function getReserveNormalizedIncome(address asset) external view returns (uint256);

  /**
   * @dev Returns the normalized variable debt per unit of asset
   * @param asset The address of the underlying asset of the reserve
   * @return The reserve normalized variable debt
   */
  function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);

  /**
   * @dev Returns the state and configuration of the reserve
   * @param asset The address of the underlying asset of the reserve
   * @return The state of the reserve
   **/
  function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);

  function finalizeTransfer(
    address asset,
    address from,
    address to,
    uint256 amount,
    uint256 balanceFromAfter,
    uint256 balanceToBefore
  ) external;

  function getReservesList() external view returns (address[] memory);

  function getAddressesProvider() external view returns (ILendingPoolAddressesProviderV2);

  function setPause(bool val) external;

  function paused() external view returns (bool);
}






interface IFlashLoans {
    function flashLoan(
        address recipient,
        address[] memory tokens,
        uint256[] memory amounts,
        bytes memory userData
    ) external;
}






interface IERC3156FlashBorrower {
    /**
     * @dev Receive a flash loan.
     * @param initiator The initiator of the loan.
     * @param token The loan currency.
     * @param amount The amount of tokens lent.
     * @param fee The additional amount of tokens to repay.
     * @param data Arbitrary data structure, intended to contain user-defined parameters.
     * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
     */
    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external returns (bytes32);
}






interface IERC3156FlashLender {
    /**
     * @dev The amount of currency available to be lent.
     * @param token The loan currency.
     * @return The amount of `token` that can be borrowed.
     */
    function maxFlashLoan(
        address token
    ) external view returns (uint256);

    /**
     * @dev The fee to be charged for a given loan.
     * @param token The loan currency.
     * @param amount The amount of tokens lent.
     * @return The amount of `token` to be charged for the loan, on top of the returned principal.
     */
    function flashFee(
        address token,
        uint256 amount
    ) external view returns (uint256);

    /**
     * @dev Initiate a flash loan.
     * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
     * @param token The loan currency.
     * @param amount The amount of tokens lent.
     * @param data Arbitrary data structure, intended to contain user-defined parameters.
     */
    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) external returns (bool);
}






abstract contract IFlashLoanBase{
    
    struct FlashLoanParams {
        address[] tokens;
        uint256[] amounts;
        uint256[] modes;
        address onBehalfOf;
        address flParamGetterAddr;
        bytes flParamGetterData;
        bytes recipeData;
    }
}






type Id is bytes32;

struct MarketParams {
    address loanToken;
    address collateralToken;
    address oracle;
    address irm;
    uint256 lltv;
}



struct MorphoBluePosition {
    uint256 supplyShares;
    uint128 borrowShares;
    uint128 collateral;
}





struct Market {
    uint128 totalSupplyAssets;
    uint128 totalSupplyShares;
    uint128 totalBorrowAssets;
    uint128 totalBorrowShares;
    uint128 lastUpdate;
    uint128 fee;
}

struct Authorization {
    address authorizer;
    address authorized;
    bool isAuthorized;
    uint256 nonce;
    uint256 deadline;
}

struct Signature {
    uint8 v;
    bytes32 r;
    bytes32 s;
}



interface IMorphoBase {
    /// @notice The EIP-712 domain separator.
    /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing
    /// the same chain id because the domain separator would be the same.
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice The owner of the contract.
    /// @dev It has the power to change the owner.
    /// @dev It has the power to set fees on markets and set the fee recipient.
    /// @dev It has the power to enable but not disable IRMs and LLTVs.
    function owner() external view returns (address);

    /// @notice The fee recipient of all markets.
    /// @dev The recipient receives the fees of a given market through a supply position on that market.
    function feeRecipient() external view returns (address);

    /// @notice Whether the `irm` is enabled.
    function isIrmEnabled(address irm) external view returns (bool);

    /// @notice Whether the `lltv` is enabled.
    function isLltvEnabled(uint256 lltv) external view returns (bool);

    /// @notice Whether `authorized` is authorized to modify `authorizer`'s positions.
    /// @dev Anyone is authorized to modify their own positions, regardless of this variable.
    function isAuthorized(address authorizer, address authorized) external view returns (bool);

    /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
    function nonce(address authorizer) external view returns (uint256);

    /// @notice Sets `newOwner` as `owner` of the contract.
    /// @dev Warning: No two-step transfer ownership.
    /// @dev Warning: The owner can be set to the zero address.
    function setOwner(address newOwner) external;

    /// @notice Enables `irm` as a possible IRM for market creation.
    /// @dev Warning: It is not possible to disable an IRM.
    function enableIrm(address irm) external;

    /// @notice Enables `lltv` as a possible LLTV for market creation.
    /// @dev Warning: It is not possible to disable a LLTV.
    function enableLltv(uint256 lltv) external;

    /// @notice Sets the `newFee` for the given market `marketParams`.
    /// @dev Warning: The recipient can be the zero address.
    function setFee(MarketParams memory marketParams, uint256 newFee) external;

    /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
    /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
    /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
    /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
    function setFeeRecipient(address newFeeRecipient) external;

    /// @notice Creates the market `marketParams`.
    /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
    /// Morpho behaves as expected:
    /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
    /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
    /// burn functions are not supported.
    /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
    /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
    /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
    /// - The IRM should not re-enter Morpho.
    /// - The oracle should return a price with the correct scaling.
    /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties
    /// (funds could get stuck):
    /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue.
    /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and
    /// `toSharesDown` overflow.
    /// - The IRM can revert on `borrowRate`.
    /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest`
    /// overflow.
    /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and
    /// `liquidate` from being used under certain market conditions.
    /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or
    /// the computation of `assetsRepaid` in `liquidate` overflow.
    /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
    /// the point where `totalBorrowShares` is very large and borrowing overflows.
    function createMarket(MarketParams memory marketParams) external;

    /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupply` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. Most usecases should rely on `assets` as an input so the caller
    /// is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific amount
    /// of shares is given for full compatibility and precision.
    /// @dev If the supply of a market gets depleted, the supply share price instantly resets to
    /// `VIRTUAL_ASSETS`:`VIRTUAL_SHARES`.
    /// @dev Supplying a large amount can revert for overflow.
    /// @param marketParams The market to supply assets to.
    /// @param assets The amount of assets to supply.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased supply position.
    /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
    /// @return assetsSupplied The amount of assets supplied.
    /// @return sharesSupplied The amount of shares minted.
    function supply(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsSupplied, uint256 sharesSupplied);

    /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
    /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
    /// conversion roundings between shares and assets.
    /// @param marketParams The market to withdraw assets from.
    /// @param assets The amount of assets to withdraw.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the supply position.
    /// @param receiver The address that will receive the withdrawn assets.
    /// @return assetsWithdrawn The amount of assets withdrawn.
    /// @return sharesWithdrawn The amount of shares burned.
    function withdraw(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);

    /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. Most usecases should rely on `assets` as an input so the caller
    /// is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is given for
    /// full compatibility and precision.
    /// @dev If the borrow of a market gets depleted, the borrow share price instantly resets to
    /// `VIRTUAL_ASSETS`:`VIRTUAL_SHARES`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Borrowing a large amount can revert for overflow.
    /// @param marketParams The market to borrow assets from.
    /// @param assets The amount of assets to borrow.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased borrow position.
    /// @param receiver The address that will receive the borrowed assets.
    /// @return assetsBorrowed The amount of assets borrowed.
    /// @return sharesBorrowed The amount of shares minted.
    function borrow(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);

    /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoReplay` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
    /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
    /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
    /// roundings between shares and assets.
    /// @param marketParams The market to repay assets to.
    /// @param assets The amount of assets to repay.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the debt position.
    /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
    /// @return assetsRepaid The amount of assets repaid.
    /// @return sharesRepaid The amount of shares burned.
    function repay(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsRepaid, uint256 sharesRepaid);

    /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupplyCollateral` function with the given `data`.
    /// @dev Interest are not accrued since it's not required and it saves gas.
    /// @dev Supplying a large amount can revert for overflow.
    /// @param marketParams The market to supply collateral to.
    /// @param assets The amount of collateral to supply.
    /// @param onBehalf The address that will own the increased collateral position.
    /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
    function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
        external;

    /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` to `receiver`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
    /// @param marketParams The market to withdraw collateral from.
    /// @param assets The amount of collateral to withdraw.
    /// @param onBehalf The address of the owner of the collateral position.
    /// @param receiver The address that will receive the collateral assets.
    function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
        external;

    /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
    /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
    /// `onMorphoLiquidate` function with the given `data`.
    /// @dev Either `seizedAssets` or `repaidShares` should be zero.
    /// @dev Seizing more than the collateral balance will underflow and revert without any error message.
    /// @dev Repaying more than the borrow balance will underflow and revert without any error message.
    /// @param marketParams The market of the position.
    /// @param borrower The owner of the position.
    /// @param seizedAssets The amount of collateral to seize.
    /// @param repaidShares The amount of shares to repay.
    /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
    /// @return The amount of assets seized.
    /// @return The amount of assets repaid.
    function liquidate(
        MarketParams memory marketParams,
        address borrower,
        uint256 seizedAssets,
        uint256 repaidShares,
        bytes memory data
    ) external returns (uint256, uint256);

    /// @notice Executes a flash loan.
    /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
    /// markets combined, plus donations).
    /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
    /// - `flashFee` is zero.
    /// - `maxFlashLoan` is the token's balance of this contract.
    /// - The receiver of `assets` is the caller.
    /// @param token The token to flash loan.
    /// @param assets The amount of assets to flash loan.
    /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
    function flashLoan(address token, uint256 assets, bytes calldata data) external;

    /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
    /// @param authorized The authorized address.
    /// @param newIsAuthorized The new authorization status.
    function setAuthorization(address authorized, bool newIsAuthorized) external;

    /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
    /// @dev Warning: Reverts if the signature has already been submitted.
    /// @dev The signature is malleable, but it has no impact on the security here.
    /// @dev The nonce is passed as argument to be able to revert with a different error message.
    /// @param authorization The `Authorization` struct.
    /// @param signature The signature.
    function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;

    /// @notice Accrues interest for the given market `marketParams`.
    function accrueInterest(MarketParams memory marketParams) external;

    /// @notice Returns the data stored on the different `slots`.
    function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
}



interface IMorphoStaticTyping is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user)
        external
        view
        returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
    /// accrual.
    function market(Id id)
        external
        view
        returns (
            uint128 totalSupplyAssets,
            uint128 totalSupplyShares,
            uint128 totalBorrowAssets,
            uint128 totalBorrowShares,
            uint128 lastUpdate,
            uint128 fee
        );

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id)
        external
        view
        returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
}





interface IMorphoBlue is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user) external view returns (MorphoBluePosition memory p);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
    /// interest accrual.
    function market(Id id) external view returns (Market memory m);

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id) external view returns (MarketParams memory);
}







abstract contract IUniswapV3Factory {
    function getPool(address token0, address token1, uint24 fee) external virtual view returns (address poolAddress);
}







abstract contract IUniswapV3Pool {
    struct Slot0 {
        // the current price
        uint160 sqrtPriceX96;
        // the current tick
        int24 tick;
        // the most-recently updated index of the observations array
        uint16 observationIndex;
        // the current maximum number of observations that are being stored
        uint16 observationCardinality;
        // the next maximum number of observations to store, triggered in observations.write
        uint16 observationCardinalityNext;
        // the current protocol fee as a percentage of the swap fee taken on withdrawal
        // represented as an integer denominator (1/x)%
        uint8 feeProtocol;
        // whether the pool is locked
        bool unlocked;
    }

    function slot0() external virtual view returns (Slot0 memory);

    function fee() external virtual view returns (uint24 fee);

    function flash(address recipient, uint256 amount0, uint256 amount1, bytes memory data) external virtual;
}







abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;
    
    error ReentrantCall();

    constructor () {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        if (_status == _ENTERED){
            revert ReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}







abstract contract IWETH {
    function allowance(address, address) public virtual view returns (uint256);

    function balanceOf(address) public virtual view returns (uint256);

    function approve(address, uint256) public virtual;

    function transfer(address, uint256) public virtual returns (bool);

    function transferFrom(
        address,
        address,
        uint256
    ) public virtual returns (bool);

    function deposit() public payable virtual;

    function withdraw(uint256) public virtual;
}









library TokenUtils {
    using SafeERC20 for IERC20;

    address public constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address public constant ETH_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /// @dev Only approves the amount if allowance is lower than amount, does not decrease allowance
    function approveToken(
        address _tokenAddr,
        address _to,
        uint256 _amount
    ) internal {
        if (_tokenAddr == ETH_ADDR) return;

        if (IERC20(_tokenAddr).allowance(address(this), _to) < _amount) {
            IERC20(_tokenAddr).safeApprove(_to, _amount);
        }
    }

    function pullTokensIfNeeded(
        address _token,
        address _from,
        uint256 _amount
    ) internal returns (uint256) {
        // handle max uint amount
        if (_amount == type(uint256).max) {
            _amount = getBalance(_token, _from);
        }

        if (_from != address(0) && _from != address(this) && _token != ETH_ADDR && _amount != 0) {
            IERC20(_token).safeTransferFrom(_from, address(this), _amount);
        }

        return _amount;
    }

    function withdrawTokens(
        address _token,
        address _to,
        uint256 _amount
    ) internal returns (uint256) {
        if (_amount == type(uint256).max) {
            _amount = getBalance(_token, address(this));
        }

        if (_to != address(0) && _to != address(this) && _amount != 0) {
            if (_token != ETH_ADDR) {
                IERC20(_token).safeTransfer(_to, _amount);
            } else {
                (bool success, ) = _to.call{value: _amount}("");
                require(success, "Eth send fail");
            }
        }

        return _amount;
    }

    function depositWeth(uint256 _amount) internal {
        IWETH(WETH_ADDR).deposit{value: _amount}();
    }

    function withdrawWeth(uint256 _amount) internal {
        IWETH(WETH_ADDR).withdraw(_amount);
    }

    function getBalance(address _tokenAddr, address _acc) internal view returns (uint256) {
        if (_tokenAddr == ETH_ADDR) {
            return _acc.balance;
        } else {
            return IERC20(_tokenAddr).balanceOf(_acc);
        }
    }

    function getTokenDecimals(address _token) internal view returns (uint256) {
        if (_token == ETH_ADDR) return 18;

        return IERC20(_token).decimals();
    }
}

















contract FLAction is ActionBase, ReentrancyGuard, IFlashLoanBase, FLHelper {
    using TokenUtils for address;

    /// @dev FL Initiator must be this contract
    error UntrustedInitiator();
    /// @dev Caller in these functions must be relevant FL source address
    error UntrustedLender();
    // Wrong FL payback amount sent
    error WrongPaybackAmountError();
    // When FL source is not found
    error NonexistentFLSource();

    enum FLSource {
        EMPTY,
        AAVEV2,
        BALANCER,
        GHO,
        MAKER,
        AAVEV3,
        UNIV3,
        SPARK,
        MORPHO_BLUE,
        CURVEUSD
    }

    /// @inheritdoc ActionBase
    function actionType() public pure override returns (uint8) {
        return uint8(ActionType.FL_ACTION);
    }

    // solhint-disable-next-line no-empty-blocks
    function executeActionDirect(bytes memory _callData) public payable override {}

    /// @inheritdoc ActionBase
    /// @notice This action doesn't use flParamGetterAddr and flParamGetterData
    /// @notice flParamGetterData is used to choose between FL providers
    function executeAction(
        bytes memory _callData,
        bytes32[] memory,
        uint8[] memory,
        bytes32[] memory
    ) public payable override returns (bytes32) {
        FlashLoanParams memory params = abi.decode(_callData, (FlashLoanParams));
        FLSource flSource = FLSource(uint8(bytes1(params.flParamGetterData)));

        handleFlashloan(params, flSource);

        return bytes32(params.amounts[0]);
    }

    function handleFlashloan(FlashLoanParams memory _flParams, FLSource _source) internal {
        if (_source == FLSource.AAVEV2) {
            _flAaveV2(_flParams);
        } else if (_source == FLSource.BALANCER) {
            _flBalancer(_flParams);
        } else if (_source == FLSource.GHO) {
            _flGho(_flParams);
        } else if (_source == FLSource.MAKER) {
            _flMaker(_flParams);
        } else if (_source == FLSource.AAVEV3) {
            _flAaveV3(_flParams);
        } else if (_source == FLSource.UNIV3) {
            _flUniV3(_flParams);
        } else if (_source == FLSource.SPARK) {
            _flSpark(_flParams);
        } else if (_source == FLSource.MORPHO_BLUE) {
            _flMorphoBlue(_flParams);
        } else if (_source == FLSource.CURVEUSD) {
            _flCurveUSD(_flParams);    
        } else {
            revert NonexistentFLSource();
        }
    }

    /// @notice Gets a Fl from Aave and returns back the execution to the action address
    /// @param _flParams All the amounts/tokens and related aave fl data
    function _flAaveV2(FlashLoanParams memory _flParams) internal {
        ILendingPoolV2(AAVE_LENDING_POOL).flashLoan(
            address(this),
            _flParams.tokens,
            _flParams.amounts,
            _flParams.modes,
            _flParams.onBehalfOf,
            _flParams.recipeData,
            AAVE_REFERRAL_CODE
        );

        emit ActionEvent(
            "FLAction",
            abi.encode(
                "AAVEV2",
                _flParams.tokens,
                _flParams.amounts,
                _flParams.modes,
                _flParams.onBehalfOf
            )
        );
    }

    /// @notice Gets a Fl from Aave V3 and returns back the execution to the action address
    /// @param _flParams All the amounts/tokens and related aave fl data
    function _flAaveV3(FlashLoanParams memory _flParams) internal {
        ILendingPoolV2(AAVE_V3_LENDING_POOL).flashLoan(
            address(this),
            _flParams.tokens,
            _flParams.amounts,
            _flParams.modes,
            _flParams.onBehalfOf,
            _flParams.recipeData,
            AAVE_REFERRAL_CODE
        );

        emit ActionEvent(
            "FLAction",
            abi.encode(
                "AAVEV3",
                _flParams.tokens,
                _flParams.amounts,
                _flParams.modes,
                _flParams.onBehalfOf
            )
        );
    }

    /// @notice Gets a FL from Balancer and returns back the execution to the action address
    function _flBalancer(FlashLoanParams memory _flParams) internal {
        IFlashLoans(VAULT_ADDR).flashLoan(
            address(this),
            _flParams.tokens,
            _flParams.amounts,
            _flParams.recipeData
        );

        emit ActionEvent("FLAction", abi.encode("BALANCER", _flParams));
    }

    /// @notice Gets a GHO FL from Gho Flash Minter
    function _flGho(FlashLoanParams memory _flParams) internal {
        IERC3156FlashLender(GHO_FLASH_MINTER_ADDR).flashLoan(
            IERC3156FlashBorrower(address(this)),
            GHO_ADDR,
            _flParams.amounts[0],
            _flParams.recipeData
        );

        emit ActionEvent("FLAction", abi.encode("GHO", _flParams.amounts[0]));
    }

    /// @notice Gets a DAI flash loan from Maker and returns back the execution to the action address
    /// @param _flParams All the amounts/tokens and related aave fl data
    function _flMaker(FlashLoanParams memory _flParams) internal {
        IERC3156FlashLender(DSS_FLASH_ADDR).flashLoan(
            IERC3156FlashBorrower(address(this)),
            DAI_ADDR,
            _flParams.amounts[0],
            _flParams.recipeData
        );

        emit ActionEvent("FLAction", abi.encode("MAKER", _flParams.amounts[0]));
    }

    function _flUniV3(FlashLoanParams memory _flParams) internal {
        // modes aren't used so we set them to later know starting balances
        _flParams.modes = new uint256[](2);
        _flParams.modes[0] = _flParams.amounts[0] > 0 ? _flParams.tokens[0].getBalance(address(this)) : 0;
        _flParams.modes[1] = _flParams.amounts[1] > 0 ? _flParams.tokens[1].getBalance(address(this)) : 0;

        /// @dev FlashLoanParams.tokens, first two array indexes contain tokens, third index contains pool address
        IUniswapV3Pool(_flParams.tokens[2]).flash(
            address(this),
            _flParams.amounts[0],
            _flParams.amounts[1],
            abi.encode(_flParams)
        );

        emit ActionEvent("FLAction", abi.encode("UNIV3", _flParams.amounts[0]));
    }

    /// @notice Gets a Fl from Spark and returns back the execution to the action address
    function _flSpark(FlashLoanParams memory _flParams) internal {

        ILendingPoolV2(SPARK_LENDING_POOL).flashLoan(
            address(this),
            _flParams.tokens,
            _flParams.amounts,
            _flParams.modes,
            _flParams.onBehalfOf,
            _flParams.recipeData,
            SPARK_REFERRAL_CODE
        );

        emit ActionEvent("FLAction", abi.encode("SPARK", _flParams.amounts[0]));
    }

    /// @notice Gets a FL from Morpho blue and returns back the execution to the action address
    function _flMorphoBlue(FlashLoanParams memory _params) internal {
        IMorphoBlue(MORPHO_BLUE_ADDR).flashLoan(
            _params.tokens[0],
            _params.amounts[0],
            abi.encode(_params.recipeData, _params.tokens[0])
        );

        emit ActionEvent("FLAction", abi.encode("MORPHOBLUE", _params.amounts[0]));
    }

    function _flCurveUSD(FlashLoanParams memory _params) internal {
        IERC3156FlashLender(CURVEUSD_FLASH_ADDR).flashLoan(
            IERC3156FlashBorrower(address(this)),
            CURVEUSD_ADDR,
            _params.amounts[0],
            _params.recipeData
        );

        emit ActionEvent("FLAction", abi.encode("CURVEUSD", _params.amounts[0]));
    }

    /// @notice Aave callback function that formats and calls back RecipeExecutor
    /// FLSource == AAVE | SPARK
    function executeOperation(
        address[] memory _assets,
        uint256[] memory _amounts,
        uint256[] memory _fees,
        address _initiator,
        bytes memory _params
    ) public nonReentrant returns (bool) {
        if (msg.sender != AAVE_LENDING_POOL && msg.sender != AAVE_V3_LENDING_POOL && msg.sender != SPARK_LENDING_POOL) {
            revert UntrustedLender();
        }
        if (_initiator != address(this)) {
            revert UntrustedInitiator();
        }

        (Recipe memory currRecipe, address wallet) = abi.decode(_params, (Recipe, address));
        uint256[] memory balancesBefore = new uint256[](_assets.length);
        // Send FL amounts to user wallet
        for (uint256 i = 0; i < _assets.length; ++i) {
            _assets[i].withdrawTokens(wallet, _amounts[i]);
            balancesBefore[i] = _assets[i].getBalance(address(this));
        }

        _executeRecipe(wallet, isDSProxy(wallet), currRecipe, _amounts[0] + _fees[0]);

        // return FL
        for (uint256 i = 0; i < _assets.length; i++) {
            uint256 paybackAmount = _amounts[i] + _fees[i];
            bool correctAmount = _assets[i].getBalance(address(this)) ==
                paybackAmount + balancesBefore[i];

            if (_assets[i] == ST_ETH_ADDR && !correctAmount) {
                flFeeFaucet.my2Wei(ST_ETH_ADDR);
                correctAmount = true;
            }
            if (!correctAmount) {
                revert WrongPaybackAmountError();
            }

            _assets[i].approveToken(address(msg.sender), paybackAmount);
        }

        return true;
    }

    /// @notice Balancer FL callback function that formats and calls back RecipeExecutor
    /// FLSource == BALANCER
    function receiveFlashLoan(
        address[] memory _tokens,
        uint256[] memory _amounts,
        uint256[] memory _feeAmounts,
        bytes memory _userData
    ) external nonReentrant {
        if (msg.sender != VAULT_ADDR) {
            revert UntrustedLender();
        }
        (Recipe memory currRecipe, address wallet) = abi.decode(_userData, (Recipe, address));

        uint256[] memory balancesBefore = new uint256[](_tokens.length);
        for (uint256 i = 0; i < _tokens.length; i++) {
            _tokens[i].withdrawTokens(wallet, _amounts[i]);
            balancesBefore[i] = _tokens[i].getBalance(address(this));
        }

        _executeRecipe(wallet, isDSProxy(wallet), currRecipe, _amounts[0] + _feeAmounts[0]);
        
        for (uint256 i = 0; i < _tokens.length; i++) {
            uint256 paybackAmount = _amounts[i] + (_feeAmounts[i]);

            if (_tokens[i].getBalance(address(this)) != paybackAmount + balancesBefore[i]) {
                revert WrongPaybackAmountError();
            }

            _tokens[i].withdrawTokens(address(VAULT_ADDR), paybackAmount);
        }
    }

    /// @notice ERC3156 callback function that formats and calls back RecipeExecutor
    /// FLSource == MAKER | GHO | CURVEUSD
    function onFlashLoan(
        address _initiator,
        address _token,
        uint256 _amount,
        uint256 _fee,
        bytes calldata _data
    ) external nonReentrant returns (bytes32) {
        if (msg.sender != DSS_FLASH_ADDR && msg.sender != GHO_FLASH_MINTER_ADDR && msg.sender != CURVEUSD_FLASH_ADDR) {
            revert UntrustedLender();
        }
        if (_initiator != address(this)) {
            revert UntrustedInitiator();
        }

        (Recipe memory currRecipe, address wallet) = abi.decode(_data, (Recipe, address));
        _token.withdrawTokens(wallet, _amount);
        uint256 balanceBefore = _token.getBalance(address(this));

        uint256 paybackAmount = _amount +_fee;

        _executeRecipe(wallet, isDSProxy(wallet), currRecipe, paybackAmount);

        if (_token.getBalance(address(this)) != paybackAmount + balanceBefore) {
            revert WrongPaybackAmountError();
        }

        if (msg.sender == CURVEUSD_FLASH_ADDR) {
            _token.withdrawTokens(msg.sender, paybackAmount);
        } else {
            _token.approveToken(msg.sender, paybackAmount);
        }

        return keccak256("ERC3156FlashBorrower.onFlashLoan");
    }

    function uniswapV3FlashCallback(
        uint256 _fee0,
        uint256 _fee1,
        bytes memory _params
    ) external nonReentrant {
        FlashLoanParams memory params = abi.decode(_params, (FlashLoanParams));
        {
            uint24 fee = IUniswapV3Pool(msg.sender).fee();
            address realPool = IUniswapV3Factory(UNI_V3_FACTORY).getPool(params.tokens[0], params.tokens[1], uint24(fee));
            if (msg.sender != realPool) revert UntrustedLender();
        }

        (Recipe memory currRecipe, address wallet) = abi.decode(params.recipeData, (Recipe, address));

        params.tokens[0].withdrawTokens(wallet, params.amounts[0]);
        params.tokens[1].withdrawTokens(wallet, params.amounts[1]);

        _executeRecipe(wallet, isDSProxy(wallet), currRecipe, params.amounts[0]);

        uint256 expectedBalance0 = params.modes[0] + params.amounts[0] + _fee0;
        uint256 expectedBalance1 = params.modes[1] + params.amounts[1] + _fee1;

        uint256 currBalance0 = params.amounts[0] > 0 ? params.tokens[0].getBalance(address(this)) : 0;
        uint256 currBalance1 = params.amounts[1] > 0 ? params.tokens[1].getBalance(address(this)) : 0;

        bool isCorrectAmount0 = currBalance0 == expectedBalance0;
        bool isCorrectAmount1 = currBalance1 == expectedBalance1;

        if (params.amounts[0] > 0 && params.tokens[0] == ST_ETH_ADDR && !isCorrectAmount0) {
            flFeeFaucet.my2Wei(ST_ETH_ADDR);
            isCorrectAmount0 = true;
        }
        if (params.amounts[1] > 0 && params.tokens[1] == ST_ETH_ADDR && !isCorrectAmount1) {
            flFeeFaucet.my2Wei(ST_ETH_ADDR);
            isCorrectAmount1 = true;
        }

        if (!isCorrectAmount0) revert WrongPaybackAmountError();
        if (!isCorrectAmount1) revert WrongPaybackAmountError();

        params.tokens[0].withdrawTokens(msg.sender, params.amounts[0] + _fee0);
        params.tokens[1].withdrawTokens(msg.sender, params.amounts[1] + _fee1);
    }

    function onMorphoFlashLoan(uint256 assets, bytes calldata data) external nonReentrant{
        if (msg.sender != MORPHO_BLUE_ADDR) {
            revert UntrustedLender();
        }
        (bytes memory taskData, address token) = abi.decode(data, (bytes, address));
        (Recipe memory currRecipe, address wallet) = abi.decode(taskData, (Recipe, address));

        token.withdrawTokens(wallet, assets);

        uint256 balanceBefore = token.getBalance(address(this));

        _executeRecipe(wallet, isDSProxy(wallet), currRecipe, assets);

        if (token.getBalance(address(this)) != assets + balanceBefore) {
            revert WrongPaybackAmountError();
        }

        token.approveToken(MORPHO_BLUE_ADDR, assets);
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"NonContractCall","type":"error"},{"inputs":[],"name":"NonexistentFLSource","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"ReturnIndexValueError","type":"error"},{"inputs":[],"name":"SafeExecutionError","type":"error"},{"inputs":[],"name":"SenderNotAdmin","type":"error"},{"inputs":[],"name":"SenderNotOwner","type":"error"},{"inputs":[],"name":"SubIndexValueError","type":"error"},{"inputs":[],"name":"UntrustedInitiator","type":"error"},{"inputs":[],"name":"UntrustedLender","type":"error"},{"inputs":[],"name":"WrongPaybackAmountError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"logName","type":"string"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"ActionEvent","type":"event"},{"inputs":[],"name":"CALLBACK_SELECTOR","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NO_PARAM_MAPPING","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RETURN_MAX_INDEX_VALUE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RETURN_MIN_INDEX_VALUE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUB_MAX_INDEX_VALUE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUB_MIN_INDEX_VALUE","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"actionType","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"adminVault","outputs":[{"internalType":"contract AdminVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_callData","type":"bytes"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"},{"internalType":"uint8[]","name":"","type":"uint8[]"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"executeAction","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_callData","type":"bytes"}],"name":"executeActionDirect","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_assets","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"_fees","type":"uint256[]"},{"internalType":"address","name":"_initiator","type":"address"},{"internalType":"bytes","name":"_params","type":"bytes"}],"name":"executeOperation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flFeeFaucet","outputs":[{"internalType":"contract FLFeeFaucet","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_proxy","type":"address"}],"name":"isDSProxy","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"logger","outputs":[{"internalType":"contract DefisaverLogger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initiator","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"onFlashLoan","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onMorphoFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint256[]","name":"_feeAmounts","type":"uint256[]"},{"internalType":"bytes","name":"_userData","type":"bytes"}],"name":"receiveFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract DFSRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee0","type":"uint256"},{"internalType":"uint256","name":"_fee1","type":"uint256"},{"internalType":"bytes","name":"_params","type":"bytes"}],"name":"uniswapV3FlashCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawStuckFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561000f575f80fd5b5060015f55614254806100215f395ff3fe608060405260043610610162575f3560e01c80638cedca71116100c6578063b20e80041161007c578063e9cbafb011610057578063e9cbafb0146103be578063f04f2707146103dd578063f24ccbfe146103fc575f80fd5b8063b20e800414610364578063c579d4901461038b578063d3c2e7ed146103aa575f80fd5b8063920f5c84116100ac578063920f5c84146102e55780639864dcdd146103045780639ce3e91914610318575f80fd5b80638cedca71146102ab5780638df50f74146102d2575f80fd5b806331f570721161011b57806341c0e1b51161010157806341c0e1b5146102445780637b103999146102585780638bcb621614610297575f80fd5b806331f5707214610212578063389f87ff14610233575f80fd5b806323e30c8b1161014b57806323e30c8b146101c0578063247492f8146101ed5780632fa13cb8146101ff575f80fd5b80630f2eee42146101665780631c451ceb14610191575b5f80fd5b348015610171575f80fd5b5061017a608081565b60405160ff90911681526020015b60405180910390f35b34801561019c575f80fd5b506101b06101ab366004612ce0565b610423565b6040519015158152602001610188565b3480156101cb575f80fd5b506101df6101da366004612d40565b6104be565b604051908152602001610188565b3480156101f8575f80fd5b505f61017a565b34801561020a575f80fd5b5061017a5f81565b34801561021d575f80fd5b5061023161022c366004612db7565b6106a7565b005b610231610241366004612f09565b50565b34801561024f575f80fd5b506102316107de565b348015610263575f80fd5b5061027f73287778f121f134c66212fb16c9b53ec991d32f5b81565b6040516001600160a01b039091168152602001610188565b3480156102a2575f80fd5b5061017a600181565b3480156102b6575f80fd5b5061027f73ccf3d848e08b94478ed8f46ffead3008faf581fd81565b6101df6102e0366004613038565b61089f565b3480156102f0575f80fd5b506101b06102ff366004613143565b610913565b34801561030f575f80fd5b5061017a607f81565b348015610323575f80fd5b5061034b7ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b881565b6040516001600160e01b03199091168152602001610188565b34801561036f575f80fd5b5061027f7347f159c90850d5ce09e21f931d504536840f34b481565b348015610396575f80fd5b506102316103a53660046131fd565b610d0a565b3480156103b5575f80fd5b5061017a60ff81565b3480156103c9575f80fd5b506102316103d836600461323b565b610e3e565b3480156103e8575f80fd5b506102316103f7366004613287565b611493565b348015610407575f80fd5b5061027f73ce7a977cac4a481bc84ac06b2da0df614e621cf381565b6040517f297103880000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201525f9073a26e15c895efc0616177b7c1e7270a4c7d51c99790632971038890602401602060405180830381865afa158015610494573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104b89190613321565b92915050565b5f60025f54036104e1576040516306fda65d60e31b815260040160405180910390fd5b60025f55337360744434d6339a6b27d73d9eda62b6f66a0a04fa1480159061051d57503373b639d208bcf0589d54fac24e655c79ec529762b814155b801561053d57503373a7a4bb50af91f90b6feb3388e7f8286af45b299b14155b1561055b5760405163245ffd9960e11b815260040160405180910390fd5b6001600160a01b0387163014610584576040516301000cbb60e11b815260040160405180910390fd5b5f80610592848601866134c0565b90925090506105ab6001600160a01b03891682896116da565b505f6105c06001600160a01b038a163061180b565b90505f6105cd888a6135c6565b90506105e3836105dc85610423565b86846118ca565b6105ed82826135c6565b6106006001600160a01b038c163061180b565b1461061e576040516333dcb1d760e21b815260040160405180910390fd5b7fffffffffffffffffffffffff585b44af506e06f49014cc771807d7950ba4d665330161065f576106596001600160a01b038b1633836116da565b50610673565b6106736001600160a01b038b163383611ada565b505060015f55507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd998975050505050505050565b60025f54036106c9576040516306fda65d60e31b815260040160405180910390fd5b60025f553373bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb146107015760405163245ffd9960e11b815260040160405180910390fd5b5f8061070f838501856135f9565b915091505f808380602001905181019061072991906138d7565b90925090506107426001600160a01b03841682896116da565b505f6107576001600160a01b0385163061180b565b905061076d8261076684610423565b858b6118ca565b61077781896135c6565b61078a6001600160a01b0386163061180b565b146107a8576040516333dcb1d760e21b815260040160405180910390fd5b6107d06001600160a01b03851673bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb8a611ada565b505060015f55505050505050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610838573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085c91906139d4565b6001600160a01b03161461089c576040517fa6c827a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33ff5b5f80858060200190518101906108b59190613a54565b90505f8160a001516108c690613b6a565b60f81c60098111156108da576108da6135e5565b90506108e68282611ba8565b81602001515f815181106108fc576108fc613bb9565b60200260200101515f1b925050505b949350505050565b5f60025f5403610936576040516306fda65d60e31b815260040160405180910390fd5b60025f5533737d2768de32b0b80b7a3454c06bdac94a69ddc7a9148015906109725750337387870bca3f3fd6335c3f4ce8392d69350b4fa4e214155b801561099257503373c13e21b648a5ee794902342038ff3adab66be98714155b156109b05760405163245ffd9960e11b815260040160405180910390fd5b6001600160a01b03831630146109d9576040516301000cbb60e11b815260040160405180910390fd5b5f80838060200190518101906109ef91906138d7565b915091505f885167ffffffffffffffff811115610a0e57610a0e612dff565b604051908082528060200260200182016040528015610a37578160200160208202803683370190505b5090505f5b8951811015610af057610a95838a8381518110610a5b57610a5b613bb9565b60200260200101518c8481518110610a7557610a75613bb9565b60200260200101516001600160a01b03166116da9092919063ffffffff16565b50610acb308b8381518110610aac57610aac613bb9565b60200260200101516001600160a01b031661180b90919063ffffffff16565b828281518110610add57610add613bb9565b6020908102919091010152600101610a3c565b50610b4282610afe84610423565b858a5f81518110610b1157610b11613bb9565b60200260200101518c5f81518110610b2b57610b2b613bb9565b6020026020010151610b3d91906135c6565b6118ca565b5f5b8951811015610cf5575f888281518110610b6057610b60613bb9565b60200260200101518a8381518110610b7a57610b7a613bb9565b6020026020010151610b8c91906135c6565b90505f838381518110610ba157610ba1613bb9565b602002602001015182610bb491906135c6565b610bca308e8681518110610aac57610aac613bb9565b14905073ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b03168c8481518110610bfd57610bfd613bb9565b60200260200101516001600160a01b0316148015610c19575080155b15610c96576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b158015610c7b575f80fd5b505af1158015610c8d573d5f803e3d5ffd5b50505050600190505b80610cb4576040516333dcb1d760e21b815260040160405180910390fd5b610ceb33838e8681518110610ccb57610ccb613bb9565b60200260200101516001600160a01b0316611ada9092919063ffffffff16565b5050600101610b44565b506001935050505060015f5595945050505050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d64573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d8891906139d4565b6001600160a01b031614610dc8576040517f19494c8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03610e25576040516001600160a01b0383169082156108fc029083905f818181858888f19350505050158015610e1f573d5f803e3d5ffd5b50505050565b610e396001600160a01b0384168383611d10565b505050565b60025f5403610e60576040516306fda65d60e31b815260040160405180910390fd5b60025f9081558151610e7b9083016020908101908401613a54565b90505f336001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eba573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ede9190613bcd565b90505f731f98431c8ad98523631ae4a59f267346ea31f9846001600160a01b0316631698ee82845f01515f81518110610f1957610f19613bb9565b6020026020010151855f0151600181518110610f3757610f37613bb9565b60209081029190910101516040516001600160e01b031960e085901b1681526001600160a01b0392831660048201529116602482015262ffffff85166044820152606401602060405180830381865afa158015610f96573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fba91906139d4565b9050336001600160a01b03821614610fe55760405163245ffd9960e11b815260040160405180910390fd5b50505f808260c0015180602001905181019061100191906138d7565b9150915061103c8184602001515f8151811061101f5761101f613bb9565b6020026020010151855f01515f81518110610a7557610a75613bb9565b5061107681846020015160018151811061105857611058613bb9565b6020026020010151855f0151600181518110610a7557610a75613bb9565b506110a88161108483610423565b8486602001515f8151811061109b5761109b613bb9565b60200260200101516118ca565b5f8684602001515f815181106110c0576110c0613bb9565b602002602001015185604001515f815181106110de576110de613bb9565b60200260200101516110f091906135c6565b6110fa91906135c6565b90505f86856020015160018151811061111557611115613bb9565b6020026020010151866040015160018151811061113457611134613bb9565b602002602001015161114691906135c6565b61115091906135c6565b90505f8086602001515f8151811061116a5761116a613bb9565b60200260200101511161117d575f611196565b61119630875f01515f81518110610aac57610aac613bb9565b90505f8087602001516001815181106111b1576111b1613bb9565b6020026020010151116111c4575f6111de565b6111de30885f0151600181518110610aac57610aac613bb9565b6020880151805191925083861491858414915f91829061120057611200613bb9565b602002602001015111801561124c57508851805173ae7ab96520de3a18e5e111b5eaab095312d7fe8491905f9061123957611239613bb9565b60200260200101516001600160a01b0316145b8015611256575081155b156112d3576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b1580156112b8575f80fd5b505af11580156112ca573d5f803e3d5ffd5b50505050600191505b5f89602001516001815181106112eb576112eb613bb9565b602002602001015111801561133a57508851805173ae7ab96520de3a18e5e111b5eaab095312d7fe849190600190811061132757611327613bb9565b60200260200101516001600160a01b0316145b8015611344575080155b156113c1576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b1580156113a6575f80fd5b505af11580156113b8573d5f803e3d5ffd5b50505050600190505b816113df576040516333dcb1d760e21b815260040160405180910390fd5b806113fd576040516333dcb1d760e21b815260040160405180910390fd5b61143c338d8b602001515f8151811061141857611418613bb9565b602002602001015161142a91906135c6565b8b5180515f90610a7557610a75613bb9565b50611480338c8b6020015160018151811061145957611459613bb9565b602002602001015161146b91906135c6565b8b5180516001908110610a7557610a75613bb9565b505060015f555050505050505050505050565b60025f54036114b5576040516306fda65d60e31b815260040160405180910390fd5b60025f553373ba12222222228d8ba445958a75a0704d566bf2c8146114ed5760405163245ffd9960e11b815260040160405180910390fd5b5f808280602001905181019061150391906138d7565b915091505f865167ffffffffffffffff81111561152257611522612dff565b60405190808252806020026020018201604052801561154b578160200160208202803683370190505b5090505f5b87518110156115c5576115898388838151811061156f5761156f613bb9565b60200260200101518a8481518110610a7557610a75613bb9565b506115a030898381518110610aac57610aac613bb9565b8282815181106115b2576115b2613bb9565b6020908102919091010152600101611550565b50611600826115d384610423565b85885f815181106115e6576115e6613bb9565b60200260200101518a5f81518110610b2b57610b2b613bb9565b5f5b87518110156107d0575f86828151811061161e5761161e613bb9565b602002602001015188838151811061163857611638613bb9565b602002602001015161164a91906135c6565b905082828151811061165e5761165e613bb9565b60200260200101518161167191906135c6565b611687308b8581518110610aac57610aac613bb9565b146116a5576040516333dcb1d760e21b815260040160405180910390fd5b6116d073ba12222222228d8ba445958a75a0704d566bf2c8828b8581518110610a7557610a75613bb9565b5050600101611602565b5f5f1982036116f0576116ed843061180b565b91505b6001600160a01b0383161580159061171157506001600160a01b0383163014155b801561171c57508115155b15611804576001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461175e576117596001600160a01b0385168484611d10565b611804565b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f81146117a7576040519150601f19603f3d011682016040523d82523d5f602084013e6117ac565b606091505b50509050806118025760405162461bcd60e51b815260206004820152600d60248201527f4574682073656e64206661696c0000000000000000000000000000000000000060448201526064015b60405180910390fd5b505b5092915050565b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161184257506001600160a01b038116316104b8565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa15801561189f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118c39190613bef565b9392505050565b82156119b657836001600160a01b0316631cff79cd47735029336642814bc51a42ba80bf83a6322110035d7ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b88686604051602401611929929190613d15565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199485161790525160e086901b909216825261196f9291600401613df8565b60206040518083038185885af115801561198b573d5f803e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906119b09190613bef565b50610e1f565b5f846001600160a01b031663468721a7735029336642814bc51a42ba80bf83a6322110035d477ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b88787604051602401611a10929190613d15565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199485161790525160e086901b9092168252611a5a939291600190600401613e19565b6020604051808303815f875af1158015611a76573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a9a9190613321565b905080611ad3576040517fe540c1c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601611b0457505050565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015282919085169063dd62ed3e90604401602060405180830381865afa158015611b6a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b8e9190613bef565b1015610e3957610e396001600160a01b0384168383611d8c565b6001816009811115611bbc57611bbc6135e5565b03611bce57611bca82611e1a565b5050565b6002816009811115611be257611be26135e5565b03611bf057611bca82611f2c565b6003816009811115611c0457611c046135e5565b03611c1257611bca8261200a565b6004816009811115611c2657611c266135e5565b03611c3457611bca82612167565b6005816009811115611c4857611c486135e5565b03611c5657611bca826122c4565b6006816009811115611c6a57611c6a6135e5565b03611c7857611bca826123b1565b6007816009811115611c8c57611c8c6135e5565b03611c9a57611bca8261261d565b6008816009811115611cae57611cae6135e5565b03611cbc57611bca82612745565b6009816009811115611cd057611cd06135e5565b03611cde57611bca826128ce565b6040517f2be02e1100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516001600160a01b038316602482015260448101829052610e399084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612a2b565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611ddd8482612b11565b610e1f576040516001600160a01b03841660248201525f6044820152611e1090859063095ea7b360e01b90606401611d55565b610e1f8482612a2b565b80516020820151604080840151606085015160c0860151835163ab9c4b5d60e01b8152737d2768de32b0b80b7a3454c06bdac94a69ddc7a99663ab9c4b5d96611e70963096929591949293919291600401613ea5565b5f604051808303815f87803b158015611e87575f80fd5b505af1158015611e99573d5f803e3d5ffd5b50505050604051611eb89067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce3825f0151836020015184604001518560600151604051602001611f079493929190613f23565b60408051601f1981840301815290829052611f2191613faa565b60405180910390a250565b8051602082015160c08301516040517f5c38449e00000000000000000000000000000000000000000000000000000000815273ba12222222228d8ba445958a75a0704d566bf2c893635c38449e93611f88933093600401613fbc565b5f604051808303815f87803b158015611f9f575f80fd5b505af1158015611fb1573d5f803e3d5ffd5b50505050604051611fd09067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382604051602001611f0791906140ac565b73b639d208bcf0589d54fac24e655c79ec529762b86001600160a01b0316635cffe9de307340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f84602001515f8151811061205957612059613bb9565b60200260200101518560c001516040518563ffffffff1660e01b815260040161208594939291906140f2565b6020604051808303815f875af11580156120a1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120c59190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061211957612119613bb9565b60209081029190910181015160408051928301819052600360608401527f47484f0000000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b7360744434d6339a6b27d73d9eda62b6f66a0a04fa6001600160a01b0316635cffe9de30736b175474e89094c44da98b954eedeac495271d0f84602001515f815181106121b6576121b6613bb9565b60200260200101518560c001516040518563ffffffff1660e01b81526004016121e294939291906140f2565b6020604051808303815f875af11580156121fe573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122229190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061227657612276613bb9565b60209081029190910181015160408051928301819052600560608401527f4d414b4552000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b80516020820151604080840151606085015160c0860151835163ab9c4b5d60e01b81527387870bca3f3fd6335c3f4ce8392d69350b4fa4e29663ab9c4b5d9661231a963096929591949293919291600401613ea5565b5f604051808303815f87803b158015612331575f80fd5b505af1158015612343573d5f803e3d5ffd5b505050506040516123629067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce3825f0151836020015184604001518560600151604051602001611f07949392919061412d565b60408051600280825260608201835290916020830190803683375050506040820152602081015180515f919082906123eb576123eb613bb9565b6020026020010151116123fe575f612417565b61241730825f01515f81518110610aac57610aac613bb9565b81604001515f8151811061242d5761242d613bb9565b6020026020010181815250505f816020015160018151811061245157612451613bb9565b602002602001015111612464575f61247e565b61247e30825f0151600181518110610aac57610aac613bb9565b816040015160018151811061249557612495613bb9565b60209081029190910101528051805160029081106124b5576124b5613bb9565b60200260200101516001600160a01b031663490e6cbc3083602001515f815181106124e2576124e2613bb9565b6020026020010151846020015160018151811061250157612501613bb9565b60200260200101518560405160200161251a9190614173565b6040516020818303038152906040526040518563ffffffff1660e01b81526004016125489493929190614185565b5f604051808303815f87803b15801561255f575f80fd5b505af1158015612571573d5f803e3d5ffd5b505050506040516125909067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106125cf576125cf613bb9565b60209081029190910181015160408051928301819052600560608401527f554e495633000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b80516020820151604080840151606085015160c0860151925163ab9c4b5d60e01b815273c13e21b648a5ee794902342038ff3adab66be9879563ab9c4b5d95612670953095929491935f90600401613ea5565b5f604051808303815f87803b158015612687575f80fd5b505af1158015612699573d5f803e3d5ffd5b505050506040516126b89067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106126f7576126f7613bb9565b60209081029190910181015160408051928301819052600560608401527f535041524b000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b8051805173bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb9163e0232b42915f9061277357612773613bb9565b602002602001015183602001515f8151811061279157612791613bb9565b60200260200101518460c00151855f01515f815181106127b3576127b3613bb9565b60200260200101516040516020016127cc9291906141b2565b6040516020818303038152906040526040518463ffffffff1660e01b81526004016127f9939291906141dc565b5f604051808303815f87803b158015612810575f80fd5b505af1158015612822573d5f803e3d5ffd5b505050506040516128419067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061288057612880613bb9565b60209081029190910181015160408051928301819052600a60608401527f4d4f5250484f424c554500000000000000000000000000000000000000000000608084015282015260a001611f07565b73a7a4bb50af91f90b6feb3388e7f8286af45b299b6001600160a01b0316635cffe9de3073f939e0a03fb07f59a73314e73794be0e57ac1b4e84602001515f8151811061291d5761291d613bb9565b60200260200101518560c001516040518563ffffffff1660e01b815260040161294994939291906140f2565b6020604051808303815f875af1158015612965573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129899190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106129dd576129dd613bb9565b60209081029190910181015160408051928301819052600860608401527f4355525645555344000000000000000000000000000000000000000000000000608084015282015260a001611f07565b5f612a7f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612bb29092919063ffffffff16565b905080515f1480612a9f575080806020019051810190612a9f9190613321565b610e395760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016117f9565b5f805f846001600160a01b031684604051612b2c9190614203565b5f604051808303815f865af19150503d805f8114612b65576040519150601f19603f3d011682016040523d82523d5f602084013e612b6a565b606091505b5091509150818015612b94575080511580612b94575080806020019051810190612b949190613321565b8015612ba957505f856001600160a01b03163b115b95945050505050565b606061090b84845f856060612bc685612c94565b612bfc576040517f304619b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80866001600160a01b03168587604051612c179190614203565b5f6040518083038185875af1925050503d805f8114612c51576040519150601f19603f3d011682016040523d82523d5f602084013e612c56565b606091505b50915091508115612c6a57915061090b9050565b805115612c7a5780518082602001fd5b8360405162461bcd60e51b81526004016117f99190613faa565b5f813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061090b575050151592915050565b6001600160a01b0381168114610241575f80fd5b5f60208284031215612cf0575f80fd5b81356118c381612ccc565b5f8083601f840112612d0b575f80fd5b50813567ffffffffffffffff811115612d22575f80fd5b602083019150836020828501011115612d39575f80fd5b9250929050565b5f805f805f8060a08789031215612d55575f80fd5b8635612d6081612ccc565b95506020870135612d7081612ccc565b94506040870135935060608701359250608087013567ffffffffffffffff811115612d99575f80fd5b612da589828a01612cfb565b979a9699509497509295939492505050565b5f805f60408486031215612dc9575f80fd5b83359250602084013567ffffffffffffffff811115612de6575f80fd5b612df286828701612cfb565b9497909650939450505050565b634e487b7160e01b5f52604160045260245ffd5b60405160a0810167ffffffffffffffff81118282101715612e3657612e36612dff565b60405290565b60405160e0810167ffffffffffffffff81118282101715612e3657612e36612dff565b604051601f8201601f1916810167ffffffffffffffff81118282101715612e8857612e88612dff565b604052919050565b5f67ffffffffffffffff821115612ea957612ea9612dff565b50601f01601f191660200190565b5f82601f830112612ec6575f80fd5b8135612ed9612ed482612e90565b612e5f565b818152846020838601011115612eed575f80fd5b816020850160208301375f918101602001919091529392505050565b5f60208284031215612f19575f80fd5b813567ffffffffffffffff811115612f2f575f80fd5b61090b84828501612eb7565b5f67ffffffffffffffff821115612f5457612f54612dff565b5060051b60200190565b5f82601f830112612f6d575f80fd5b81356020612f7d612ed483612f3b565b8083825260208201915060208460051b870101935086841115612f9e575f80fd5b602086015b84811015612fba5780358352918301918301612fa3565b509695505050505050565b60ff81168114610241575f80fd5b5f82601f830112612fe2575f80fd5b81356020612ff2612ed483612f3b565b8083825260208201915060208460051b870101935086841115613013575f80fd5b602086015b84811015612fba57803561302b81612fc5565b8352918301918301613018565b5f805f806080858703121561304b575f80fd5b843567ffffffffffffffff80821115613062575f80fd5b61306e88838901612eb7565b95506020870135915080821115613083575f80fd5b61308f88838901612f5e565b945060408701359150808211156130a4575f80fd5b6130b088838901612fd3565b935060608701359150808211156130c5575f80fd5b506130d287828801612f5e565b91505092959194509250565b5f82601f8301126130ed575f80fd5b813560206130fd612ed483612f3b565b8083825260208201915060208460051b87010193508684111561311e575f80fd5b602086015b84811015612fba57803561313681612ccc565b8352918301918301613123565b5f805f805f60a08688031215613157575f80fd5b853567ffffffffffffffff8082111561316e575f80fd5b61317a89838a016130de565b9650602088013591508082111561318f575f80fd5b61319b89838a01612f5e565b955060408801359150808211156131b0575f80fd5b6131bc89838a01612f5e565b9450606088013591506131ce82612ccc565b909250608087013590808211156131e3575f80fd5b506131f088828901612eb7565b9150509295509295909350565b5f805f6060848603121561320f575f80fd5b833561321a81612ccc565b9250602084013561322a81612ccc565b929592945050506040919091013590565b5f805f6060848603121561324d575f80fd5b8335925060208401359150604084013567ffffffffffffffff811115613271575f80fd5b61327d86828701612eb7565b9150509250925092565b5f805f806080858703121561329a575f80fd5b843567ffffffffffffffff808211156132b1575f80fd5b6132bd888389016130de565b955060208701359150808211156132d2575f80fd5b6132de88838901612f5e565b945060408701359150808211156132f3575f80fd5b6132ff88838901612f5e565b93506060870135915080821115613314575f80fd5b506130d287828801612eb7565b5f60208284031215613331575f80fd5b815180151581146118c3575f80fd5b5f82601f83011261334f575f80fd5b8135602061335f612ed483612f3b565b82815260059290921b8401810191818101908684111561337d575f80fd5b8286015b84811015612fba57803567ffffffffffffffff81111561339f575f80fd5b6133ad8986838b0101612eb7565b845250918301918301613381565b6001600160e01b031981168114610241575f80fd5b5f82601f8301126133df575f80fd5b813560206133ef612ed483612f3b565b8083825260208201915060208460051b870101935086841115613410575f80fd5b602086015b84811015612fba578035613428816133bb565b8352918301918301613415565b5f82601f830112613444575f80fd5b81356020613454612ed483612f3b565b82815260059290921b84018101918181019086841115613472575f80fd5b8286015b84811015612fba57803567ffffffffffffffff811115613494575f80fd5b6134a28986838b0101612fd3565b845250918301918301613476565b80356134bb81612ccc565b919050565b5f80604083850312156134d1575f80fd5b823567ffffffffffffffff808211156134e8575f80fd5b9084019060a082870312156134fb575f80fd5b613503612e13565b823582811115613511575f80fd5b61351d88828601612eb7565b825250602083013582811115613531575f80fd5b61353d88828601613340565b602083015250604083013582811115613554575f80fd5b61356088828601612f5e565b604083015250606083013582811115613577575f80fd5b613583888286016133d0565b60608301525060808301358281111561359a575f80fd5b6135a688828601613435565b60808301525093506135bd915050602084016134b0565b90509250929050565b808201808211156104b857634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f806040838503121561360a575f80fd5b823567ffffffffffffffff811115613620575f80fd5b61362c85828601612eb7565b925050602083013561363d81612ccc565b809150509250929050565b5f5b8381101561366257818101518382015260200161364a565b50505f910152565b5f82601f830112613679575f80fd5b8151613687612ed482612e90565b81815284602083860101111561369b575f80fd5b61090b826020830160208701613648565b5f82601f8301126136bb575f80fd5b815160206136cb612ed483612f3b565b82815260059290921b840181019181810190868411156136e9575f80fd5b8286015b84811015612fba57805167ffffffffffffffff81111561370b575f80fd5b6137198986838b010161366a565b8452509183019183016136ed565b5f82601f830112613736575f80fd5b81516020613746612ed483612f3b565b8083825260208201915060208460051b870101935086841115613767575f80fd5b602086015b84811015612fba578051835291830191830161376c565b5f82601f830112613792575f80fd5b815160206137a2612ed483612f3b565b8083825260208201915060208460051b8701019350868411156137c3575f80fd5b602086015b84811015612fba5780516137db816133bb565b83529183019183016137c8565b5f82601f8301126137f7575f80fd5b81516020613807612ed483612f3b565b828152600592831b8501820192828201919087851115613825575f80fd5b8387015b858110156138bf57805167ffffffffffffffff811115613847575f80fd5b8801603f81018a13613857575f80fd5b858101516040613869612ed483612f3b565b82815291851b8301810191888101908d841115613884575f80fd5b938201935b838510156138ae578451925061389e83612fc5565b8282529389019390890190613889565b885250505093850193508401613829565b5090979650505050505050565b80516134bb81612ccc565b5f80604083850312156138e8575f80fd5b825167ffffffffffffffff808211156138ff575f80fd5b9084019060a08287031215613912575f80fd5b61391a612e13565b825182811115613928575f80fd5b6139348882860161366a565b825250602083015182811115613948575f80fd5b613954888286016136ac565b60208301525060408301518281111561396b575f80fd5b61397788828601613727565b60408301525060608301518281111561398e575f80fd5b61399a88828601613783565b6060830152506080830151828111156139b1575f80fd5b6139bd888286016137e8565b60808301525093506135bd915050602084016138cc565b5f602082840312156139e4575f80fd5b81516118c381612ccc565b5f82601f8301126139fe575f80fd5b81516020613a0e612ed483612f3b565b8083825260208201915060208460051b870101935086841115613a2f575f80fd5b602086015b84811015612fba578051613a4781612ccc565b8352918301918301613a34565b5f60208284031215613a64575f80fd5b815167ffffffffffffffff80821115613a7b575f80fd5b9083019060e08286031215613a8e575f80fd5b613a96612e3c565b825182811115613aa4575f80fd5b613ab0878286016139ef565b825250602083015182811115613ac4575f80fd5b613ad087828601613727565b602083015250604083015182811115613ae7575f80fd5b613af387828601613727565b604083015250613b05606084016138cc565b6060820152613b16608084016138cc565b608082015260a083015182811115613b2c575f80fd5b613b388782860161366a565b60a08301525060c083015182811115613b4f575f80fd5b613b5b8782860161366a565b60c08301525095945050505050565b5f815160208301517fff0000000000000000000000000000000000000000000000000000000000000080821693506001831015613bb15780818460010360031b1b83161693505b505050919050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613bdd575f80fd5b815162ffffff811681146118c3575f80fd5b5f60208284031215613bff575f80fd5b5051919050565b5f8151808452613c1d816020860160208601613648565b601f01601f19169290920160200192915050565b5f815180845260208085019450602084015f5b83811015613c6057815187529582019590820190600101613c44565b509495945050505050565b5f815180845260208085019450602084015f5b83811015613c605781516001600160e01b03191687529582019590820190600101613c7e565b5f82825180855260208086019550808260051b8401018186015f5b848110156138bf57858303601f190189528151805180855290850190858501905f5b81811015613d0057835160ff1683529287019291870191600101613ce1565b50509985019993505090830190600101613cbf565b604081525f835160a06040840152613d3060e0840182613c06565b9050602080860151603f19808685030160608701528382518086528486019150848160051b87010185850194505f5b82811015613d8d57601f19888303018452613d7b828751613c06565b95870195938701939150600101613d5f565b5060408b01519650838982030160808a0152613da98188613c31565b965050505060608801519150808685030160a0870152613dc98483613c6b565b935060808801519150808685030160c087015250613de78382613ca4565b935050848185015250509392505050565b6001600160a01b0383168152604060208201525f61090b6040830184613c06565b6001600160a01b0385168152836020820152608060408201525f613e406080830185613c06565b905060028310613e5e57634e487b7160e01b5f52602160045260245ffd5b82606083015295945050505050565b5f815180845260208085019450602084015f5b83811015613c605781516001600160a01b031687529582019590820190600101613e80565b5f6001600160a01b03808a16835260e06020840152613ec760e084018a613e6d565b8381036040850152613ed9818a613c31565b90508381036060850152613eed8189613c31565b9050818716608085015283810360a0850152613f098187613c06565b9250505061ffff831660c083015298975050505050505050565b60a08152600660a08201527f414156455632000000000000000000000000000000000000000000000000000060c082015260e060208201525f613f6960e0830187613e6d565b8281036040840152613f7b8187613c31565b90508281036060840152613f8f8186613c31565b9150506001600160a01b038316608083015295945050505050565b602081525f6118c36020830184613c06565b6001600160a01b0385168152608060208201525f613fdd6080830186613e6d565b8281036040840152613fef8186613c31565b905082810360608401526140038185613c06565b979650505050505050565b5f815160e0845261402260e0850182613e6d565b90506020830151848203602086015261403b8282613c31565b915050604083015184820360408601526140558282613c31565b91505060608301516001600160a01b038082166060870152806080860151166080870152505060a083015184820360a08601526140928282613c06565b91505060c083015184820360c0860152612ba98282613c06565b60408152600860408201527f42414c414e4345520000000000000000000000000000000000000000000000006060820152608060208201525f6118c3608083018461400e565b5f6001600160a01b038087168352808616602084015250836040830152608060608301526141236080830184613c06565b9695505050505050565b60a08152600660a08201527f414156455633000000000000000000000000000000000000000000000000000060c082015260e060208201525f613f6960e0830187613e6d565b602081525f6118c3602083018461400e565b6001600160a01b0385168152836020820152826040820152608060608201525f6141236080830184613c06565b604081525f6141c46040830185613c06565b90506001600160a01b03831660208301529392505050565b6001600160a01b0384168152826020820152606060408201525f612ba96060830184613c06565b5f8251614214818460208701613648565b919091019291505056fea264697066735822122097a88cdfe1242c80d5b121a1d4a50e116a7b5ba94936c1f4bb450f95c4bae95464736f6c63430008180033

Deployed Bytecode

0x608060405260043610610162575f3560e01c80638cedca71116100c6578063b20e80041161007c578063e9cbafb011610057578063e9cbafb0146103be578063f04f2707146103dd578063f24ccbfe146103fc575f80fd5b8063b20e800414610364578063c579d4901461038b578063d3c2e7ed146103aa575f80fd5b8063920f5c84116100ac578063920f5c84146102e55780639864dcdd146103045780639ce3e91914610318575f80fd5b80638cedca71146102ab5780638df50f74146102d2575f80fd5b806331f570721161011b57806341c0e1b51161010157806341c0e1b5146102445780637b103999146102585780638bcb621614610297575f80fd5b806331f5707214610212578063389f87ff14610233575f80fd5b806323e30c8b1161014b57806323e30c8b146101c0578063247492f8146101ed5780632fa13cb8146101ff575f80fd5b80630f2eee42146101665780631c451ceb14610191575b5f80fd5b348015610171575f80fd5b5061017a608081565b60405160ff90911681526020015b60405180910390f35b34801561019c575f80fd5b506101b06101ab366004612ce0565b610423565b6040519015158152602001610188565b3480156101cb575f80fd5b506101df6101da366004612d40565b6104be565b604051908152602001610188565b3480156101f8575f80fd5b505f61017a565b34801561020a575f80fd5b5061017a5f81565b34801561021d575f80fd5b5061023161022c366004612db7565b6106a7565b005b610231610241366004612f09565b50565b34801561024f575f80fd5b506102316107de565b348015610263575f80fd5b5061027f73287778f121f134c66212fb16c9b53ec991d32f5b81565b6040516001600160a01b039091168152602001610188565b3480156102a2575f80fd5b5061017a600181565b3480156102b6575f80fd5b5061027f73ccf3d848e08b94478ed8f46ffead3008faf581fd81565b6101df6102e0366004613038565b61089f565b3480156102f0575f80fd5b506101b06102ff366004613143565b610913565b34801561030f575f80fd5b5061017a607f81565b348015610323575f80fd5b5061034b7ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b881565b6040516001600160e01b03199091168152602001610188565b34801561036f575f80fd5b5061027f7347f159c90850d5ce09e21f931d504536840f34b481565b348015610396575f80fd5b506102316103a53660046131fd565b610d0a565b3480156103b5575f80fd5b5061017a60ff81565b3480156103c9575f80fd5b506102316103d836600461323b565b610e3e565b3480156103e8575f80fd5b506102316103f7366004613287565b611493565b348015610407575f80fd5b5061027f73ce7a977cac4a481bc84ac06b2da0df614e621cf381565b6040517f297103880000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201525f9073a26e15c895efc0616177b7c1e7270a4c7d51c99790632971038890602401602060405180830381865afa158015610494573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104b89190613321565b92915050565b5f60025f54036104e1576040516306fda65d60e31b815260040160405180910390fd5b60025f55337360744434d6339a6b27d73d9eda62b6f66a0a04fa1480159061051d57503373b639d208bcf0589d54fac24e655c79ec529762b814155b801561053d57503373a7a4bb50af91f90b6feb3388e7f8286af45b299b14155b1561055b5760405163245ffd9960e11b815260040160405180910390fd5b6001600160a01b0387163014610584576040516301000cbb60e11b815260040160405180910390fd5b5f80610592848601866134c0565b90925090506105ab6001600160a01b03891682896116da565b505f6105c06001600160a01b038a163061180b565b90505f6105cd888a6135c6565b90506105e3836105dc85610423565b86846118ca565b6105ed82826135c6565b6106006001600160a01b038c163061180b565b1461061e576040516333dcb1d760e21b815260040160405180910390fd5b7fffffffffffffffffffffffff585b44af506e06f49014cc771807d7950ba4d665330161065f576106596001600160a01b038b1633836116da565b50610673565b6106736001600160a01b038b163383611ada565b505060015f55507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd998975050505050505050565b60025f54036106c9576040516306fda65d60e31b815260040160405180910390fd5b60025f553373bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb146107015760405163245ffd9960e11b815260040160405180910390fd5b5f8061070f838501856135f9565b915091505f808380602001905181019061072991906138d7565b90925090506107426001600160a01b03841682896116da565b505f6107576001600160a01b0385163061180b565b905061076d8261076684610423565b858b6118ca565b61077781896135c6565b61078a6001600160a01b0386163061180b565b146107a8576040516333dcb1d760e21b815260040160405180910390fd5b6107d06001600160a01b03851673bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb8a611ada565b505060015f55505050505050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b031663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610838573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085c91906139d4565b6001600160a01b03161461089c576040517fa6c827a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33ff5b5f80858060200190518101906108b59190613a54565b90505f8160a001516108c690613b6a565b60f81c60098111156108da576108da6135e5565b90506108e68282611ba8565b81602001515f815181106108fc576108fc613bb9565b60200260200101515f1b925050505b949350505050565b5f60025f5403610936576040516306fda65d60e31b815260040160405180910390fd5b60025f5533737d2768de32b0b80b7a3454c06bdac94a69ddc7a9148015906109725750337387870bca3f3fd6335c3f4ce8392d69350b4fa4e214155b801561099257503373c13e21b648a5ee794902342038ff3adab66be98714155b156109b05760405163245ffd9960e11b815260040160405180910390fd5b6001600160a01b03831630146109d9576040516301000cbb60e11b815260040160405180910390fd5b5f80838060200190518101906109ef91906138d7565b915091505f885167ffffffffffffffff811115610a0e57610a0e612dff565b604051908082528060200260200182016040528015610a37578160200160208202803683370190505b5090505f5b8951811015610af057610a95838a8381518110610a5b57610a5b613bb9565b60200260200101518c8481518110610a7557610a75613bb9565b60200260200101516001600160a01b03166116da9092919063ffffffff16565b50610acb308b8381518110610aac57610aac613bb9565b60200260200101516001600160a01b031661180b90919063ffffffff16565b828281518110610add57610add613bb9565b6020908102919091010152600101610a3c565b50610b4282610afe84610423565b858a5f81518110610b1157610b11613bb9565b60200260200101518c5f81518110610b2b57610b2b613bb9565b6020026020010151610b3d91906135c6565b6118ca565b5f5b8951811015610cf5575f888281518110610b6057610b60613bb9565b60200260200101518a8381518110610b7a57610b7a613bb9565b6020026020010151610b8c91906135c6565b90505f838381518110610ba157610ba1613bb9565b602002602001015182610bb491906135c6565b610bca308e8681518110610aac57610aac613bb9565b14905073ae7ab96520de3a18e5e111b5eaab095312d7fe846001600160a01b03168c8481518110610bfd57610bfd613bb9565b60200260200101516001600160a01b0316148015610c19575080155b15610c96576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b158015610c7b575f80fd5b505af1158015610c8d573d5f803e3d5ffd5b50505050600190505b80610cb4576040516333dcb1d760e21b815260040160405180910390fd5b610ceb33838e8681518110610ccb57610ccb613bb9565b60200260200101516001600160a01b0316611ada9092919063ffffffff16565b5050600101610b44565b506001935050505060015f5595945050505050565b336001600160a01b031673ccf3d848e08b94478ed8f46ffead3008faf581fd6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d64573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d8891906139d4565b6001600160a01b031614610dc8576040517f19494c8a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03610e25576040516001600160a01b0383169082156108fc029083905f818181858888f19350505050158015610e1f573d5f803e3d5ffd5b50505050565b610e396001600160a01b0384168383611d10565b505050565b60025f5403610e60576040516306fda65d60e31b815260040160405180910390fd5b60025f9081558151610e7b9083016020908101908401613a54565b90505f336001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eba573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ede9190613bcd565b90505f731f98431c8ad98523631ae4a59f267346ea31f9846001600160a01b0316631698ee82845f01515f81518110610f1957610f19613bb9565b6020026020010151855f0151600181518110610f3757610f37613bb9565b60209081029190910101516040516001600160e01b031960e085901b1681526001600160a01b0392831660048201529116602482015262ffffff85166044820152606401602060405180830381865afa158015610f96573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fba91906139d4565b9050336001600160a01b03821614610fe55760405163245ffd9960e11b815260040160405180910390fd5b50505f808260c0015180602001905181019061100191906138d7565b9150915061103c8184602001515f8151811061101f5761101f613bb9565b6020026020010151855f01515f81518110610a7557610a75613bb9565b5061107681846020015160018151811061105857611058613bb9565b6020026020010151855f0151600181518110610a7557610a75613bb9565b506110a88161108483610423565b8486602001515f8151811061109b5761109b613bb9565b60200260200101516118ca565b5f8684602001515f815181106110c0576110c0613bb9565b602002602001015185604001515f815181106110de576110de613bb9565b60200260200101516110f091906135c6565b6110fa91906135c6565b90505f86856020015160018151811061111557611115613bb9565b6020026020010151866040015160018151811061113457611134613bb9565b602002602001015161114691906135c6565b61115091906135c6565b90505f8086602001515f8151811061116a5761116a613bb9565b60200260200101511161117d575f611196565b61119630875f01515f81518110610aac57610aac613bb9565b90505f8087602001516001815181106111b1576111b1613bb9565b6020026020010151116111c4575f6111de565b6111de30885f0151600181518110610aac57610aac613bb9565b6020880151805191925083861491858414915f91829061120057611200613bb9565b602002602001015111801561124c57508851805173ae7ab96520de3a18e5e111b5eaab095312d7fe8491905f9061123957611239613bb9565b60200260200101516001600160a01b0316145b8015611256575081155b156112d3576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b1580156112b8575f80fd5b505af11580156112ca573d5f803e3d5ffd5b50505050600191505b5f89602001516001815181106112eb576112eb613bb9565b602002602001015111801561133a57508851805173ae7ab96520de3a18e5e111b5eaab095312d7fe849190600190811061132757611327613bb9565b60200260200101516001600160a01b0316145b8015611344575080155b156113c1576040516373c3bb2760e11b815273ae7ab96520de3a18e5e111b5eaab095312d7fe8460048201527347f159c90850d5ce09e21f931d504536840f34b49063e787764e906024015f604051808303815f87803b1580156113a6575f80fd5b505af11580156113b8573d5f803e3d5ffd5b50505050600190505b816113df576040516333dcb1d760e21b815260040160405180910390fd5b806113fd576040516333dcb1d760e21b815260040160405180910390fd5b61143c338d8b602001515f8151811061141857611418613bb9565b602002602001015161142a91906135c6565b8b5180515f90610a7557610a75613bb9565b50611480338c8b6020015160018151811061145957611459613bb9565b602002602001015161146b91906135c6565b8b5180516001908110610a7557610a75613bb9565b505060015f555050505050505050505050565b60025f54036114b5576040516306fda65d60e31b815260040160405180910390fd5b60025f553373ba12222222228d8ba445958a75a0704d566bf2c8146114ed5760405163245ffd9960e11b815260040160405180910390fd5b5f808280602001905181019061150391906138d7565b915091505f865167ffffffffffffffff81111561152257611522612dff565b60405190808252806020026020018201604052801561154b578160200160208202803683370190505b5090505f5b87518110156115c5576115898388838151811061156f5761156f613bb9565b60200260200101518a8481518110610a7557610a75613bb9565b506115a030898381518110610aac57610aac613bb9565b8282815181106115b2576115b2613bb9565b6020908102919091010152600101611550565b50611600826115d384610423565b85885f815181106115e6576115e6613bb9565b60200260200101518a5f81518110610b2b57610b2b613bb9565b5f5b87518110156107d0575f86828151811061161e5761161e613bb9565b602002602001015188838151811061163857611638613bb9565b602002602001015161164a91906135c6565b905082828151811061165e5761165e613bb9565b60200260200101518161167191906135c6565b611687308b8581518110610aac57610aac613bb9565b146116a5576040516333dcb1d760e21b815260040160405180910390fd5b6116d073ba12222222228d8ba445958a75a0704d566bf2c8828b8581518110610a7557610a75613bb9565b5050600101611602565b5f5f1982036116f0576116ed843061180b565b91505b6001600160a01b0383161580159061171157506001600160a01b0383163014155b801561171c57508115155b15611804576001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461175e576117596001600160a01b0385168484611d10565b611804565b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f81146117a7576040519150601f19603f3d011682016040523d82523d5f602084013e6117ac565b606091505b50509050806118025760405162461bcd60e51b815260206004820152600d60248201527f4574682073656e64206661696c0000000000000000000000000000000000000060448201526064015b60405180910390fd5b505b5092915050565b5f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0384160161184257506001600160a01b038116316104b8565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528416906370a0823190602401602060405180830381865afa15801561189f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118c39190613bef565b9392505050565b82156119b657836001600160a01b0316631cff79cd47735029336642814bc51a42ba80bf83a6322110035d7ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b88686604051602401611929929190613d15565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199485161790525160e086901b909216825261196f9291600401613df8565b60206040518083038185885af115801561198b573d5f803e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906119b09190613bef565b50610e1f565b5f846001600160a01b031663468721a7735029336642814bc51a42ba80bf83a6322110035d477ff890814b417745d791c9230c2f130c9c0a442917386da77dda5dc34b2a22b9b88787604051602401611a10929190613d15565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199485161790525160e086901b9092168252611a5a939291600190600401613e19565b6020604051808303815f875af1158015611a76573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a9a9190613321565b905080611ad3576040517fe540c1c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03841601611b0457505050565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015282919085169063dd62ed3e90604401602060405180830381865afa158015611b6a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b8e9190613bef565b1015610e3957610e396001600160a01b0384168383611d8c565b6001816009811115611bbc57611bbc6135e5565b03611bce57611bca82611e1a565b5050565b6002816009811115611be257611be26135e5565b03611bf057611bca82611f2c565b6003816009811115611c0457611c046135e5565b03611c1257611bca8261200a565b6004816009811115611c2657611c266135e5565b03611c3457611bca82612167565b6005816009811115611c4857611c486135e5565b03611c5657611bca826122c4565b6006816009811115611c6a57611c6a6135e5565b03611c7857611bca826123b1565b6007816009811115611c8c57611c8c6135e5565b03611c9a57611bca8261261d565b6008816009811115611cae57611cae6135e5565b03611cbc57611bca82612745565b6009816009811115611cd057611cd06135e5565b03611cde57611bca826128ce565b6040517f2be02e1100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516001600160a01b038316602482015260448101829052610e399084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612a2b565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611ddd8482612b11565b610e1f576040516001600160a01b03841660248201525f6044820152611e1090859063095ea7b360e01b90606401611d55565b610e1f8482612a2b565b80516020820151604080840151606085015160c0860151835163ab9c4b5d60e01b8152737d2768de32b0b80b7a3454c06bdac94a69ddc7a99663ab9c4b5d96611e70963096929591949293919291600401613ea5565b5f604051808303815f87803b158015611e87575f80fd5b505af1158015611e99573d5f803e3d5ffd5b50505050604051611eb89067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce3825f0151836020015184604001518560600151604051602001611f079493929190613f23565b60408051601f1981840301815290829052611f2191613faa565b60405180910390a250565b8051602082015160c08301516040517f5c38449e00000000000000000000000000000000000000000000000000000000815273ba12222222228d8ba445958a75a0704d566bf2c893635c38449e93611f88933093600401613fbc565b5f604051808303815f87803b158015611f9f575f80fd5b505af1158015611fb1573d5f803e3d5ffd5b50505050604051611fd09067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382604051602001611f0791906140ac565b73b639d208bcf0589d54fac24e655c79ec529762b86001600160a01b0316635cffe9de307340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f84602001515f8151811061205957612059613bb9565b60200260200101518560c001516040518563ffffffff1660e01b815260040161208594939291906140f2565b6020604051808303815f875af11580156120a1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120c59190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061211957612119613bb9565b60209081029190910181015160408051928301819052600360608401527f47484f0000000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b7360744434d6339a6b27d73d9eda62b6f66a0a04fa6001600160a01b0316635cffe9de30736b175474e89094c44da98b954eedeac495271d0f84602001515f815181106121b6576121b6613bb9565b60200260200101518560c001516040518563ffffffff1660e01b81526004016121e294939291906140f2565b6020604051808303815f875af11580156121fe573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122229190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061227657612276613bb9565b60209081029190910181015160408051928301819052600560608401527f4d414b4552000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b80516020820151604080840151606085015160c0860151835163ab9c4b5d60e01b81527387870bca3f3fd6335c3f4ce8392d69350b4fa4e29663ab9c4b5d9661231a963096929591949293919291600401613ea5565b5f604051808303815f87803b158015612331575f80fd5b505af1158015612343573d5f803e3d5ffd5b505050506040516123629067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce3825f0151836020015184604001518560600151604051602001611f07949392919061412d565b60408051600280825260608201835290916020830190803683375050506040820152602081015180515f919082906123eb576123eb613bb9565b6020026020010151116123fe575f612417565b61241730825f01515f81518110610aac57610aac613bb9565b81604001515f8151811061242d5761242d613bb9565b6020026020010181815250505f816020015160018151811061245157612451613bb9565b602002602001015111612464575f61247e565b61247e30825f0151600181518110610aac57610aac613bb9565b816040015160018151811061249557612495613bb9565b60209081029190910101528051805160029081106124b5576124b5613bb9565b60200260200101516001600160a01b031663490e6cbc3083602001515f815181106124e2576124e2613bb9565b6020026020010151846020015160018151811061250157612501613bb9565b60200260200101518560405160200161251a9190614173565b6040516020818303038152906040526040518563ffffffff1660e01b81526004016125489493929190614185565b5f604051808303815f87803b15801561255f575f80fd5b505af1158015612571573d5f803e3d5ffd5b505050506040516125909067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106125cf576125cf613bb9565b60209081029190910181015160408051928301819052600560608401527f554e495633000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b80516020820151604080840151606085015160c0860151925163ab9c4b5d60e01b815273c13e21b648a5ee794902342038ff3adab66be9879563ab9c4b5d95612670953095929491935f90600401613ea5565b5f604051808303815f87803b158015612687575f80fd5b505af1158015612699573d5f803e3d5ffd5b505050506040516126b89067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106126f7576126f7613bb9565b60209081029190910181015160408051928301819052600560608401527f535041524b000000000000000000000000000000000000000000000000000000608084015282015260a001611f07565b8051805173bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb9163e0232b42915f9061277357612773613bb9565b602002602001015183602001515f8151811061279157612791613bb9565b60200260200101518460c00151855f01515f815181106127b3576127b3613bb9565b60200260200101516040516020016127cc9291906141b2565b6040516020818303038152906040526040518463ffffffff1660e01b81526004016127f9939291906141dc565b5f604051808303815f87803b158015612810575f80fd5b505af1158015612822573d5f803e3d5ffd5b505050506040516128419067232620b1ba34b7b760c11b815260080190565b60405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f8151811061288057612880613bb9565b60209081029190910181015160408051928301819052600a60608401527f4d4f5250484f424c554500000000000000000000000000000000000000000000608084015282015260a001611f07565b73a7a4bb50af91f90b6feb3388e7f8286af45b299b6001600160a01b0316635cffe9de3073f939e0a03fb07f59a73314e73794be0e57ac1b4e84602001515f8151811061291d5761291d613bb9565b60200260200101518560c001516040518563ffffffff1660e01b815260040161294994939291906140f2565b6020604051808303815f875af1158015612965573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129899190613321565b5060405167232620b1ba34b7b760c11b815260080160405180910390207f2b6d22f419271bcc89bbac8deec947c664365d6e24d06fef0ca7c325c704dce382602001515f815181106129dd576129dd613bb9565b60209081029190910181015160408051928301819052600860608401527f4355525645555344000000000000000000000000000000000000000000000000608084015282015260a001611f07565b5f612a7f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612bb29092919063ffffffff16565b905080515f1480612a9f575080806020019051810190612a9f9190613321565b610e395760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016117f9565b5f805f846001600160a01b031684604051612b2c9190614203565b5f604051808303815f865af19150503d805f8114612b65576040519150601f19603f3d011682016040523d82523d5f602084013e612b6a565b606091505b5091509150818015612b94575080511580612b94575080806020019051810190612b949190613321565b8015612ba957505f856001600160a01b03163b115b95945050505050565b606061090b84845f856060612bc685612c94565b612bfc576040517f304619b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80866001600160a01b03168587604051612c179190614203565b5f6040518083038185875af1925050503d805f8114612c51576040519150601f19603f3d011682016040523d82523d5f602084013e612c56565b606091505b50915091508115612c6a57915061090b9050565b805115612c7a5780518082602001fd5b8360405162461bcd60e51b81526004016117f99190613faa565b5f813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061090b575050151592915050565b6001600160a01b0381168114610241575f80fd5b5f60208284031215612cf0575f80fd5b81356118c381612ccc565b5f8083601f840112612d0b575f80fd5b50813567ffffffffffffffff811115612d22575f80fd5b602083019150836020828501011115612d39575f80fd5b9250929050565b5f805f805f8060a08789031215612d55575f80fd5b8635612d6081612ccc565b95506020870135612d7081612ccc565b94506040870135935060608701359250608087013567ffffffffffffffff811115612d99575f80fd5b612da589828a01612cfb565b979a9699509497509295939492505050565b5f805f60408486031215612dc9575f80fd5b83359250602084013567ffffffffffffffff811115612de6575f80fd5b612df286828701612cfb565b9497909650939450505050565b634e487b7160e01b5f52604160045260245ffd5b60405160a0810167ffffffffffffffff81118282101715612e3657612e36612dff565b60405290565b60405160e0810167ffffffffffffffff81118282101715612e3657612e36612dff565b604051601f8201601f1916810167ffffffffffffffff81118282101715612e8857612e88612dff565b604052919050565b5f67ffffffffffffffff821115612ea957612ea9612dff565b50601f01601f191660200190565b5f82601f830112612ec6575f80fd5b8135612ed9612ed482612e90565b612e5f565b818152846020838601011115612eed575f80fd5b816020850160208301375f918101602001919091529392505050565b5f60208284031215612f19575f80fd5b813567ffffffffffffffff811115612f2f575f80fd5b61090b84828501612eb7565b5f67ffffffffffffffff821115612f5457612f54612dff565b5060051b60200190565b5f82601f830112612f6d575f80fd5b81356020612f7d612ed483612f3b565b8083825260208201915060208460051b870101935086841115612f9e575f80fd5b602086015b84811015612fba5780358352918301918301612fa3565b509695505050505050565b60ff81168114610241575f80fd5b5f82601f830112612fe2575f80fd5b81356020612ff2612ed483612f3b565b8083825260208201915060208460051b870101935086841115613013575f80fd5b602086015b84811015612fba57803561302b81612fc5565b8352918301918301613018565b5f805f806080858703121561304b575f80fd5b843567ffffffffffffffff80821115613062575f80fd5b61306e88838901612eb7565b95506020870135915080821115613083575f80fd5b61308f88838901612f5e565b945060408701359150808211156130a4575f80fd5b6130b088838901612fd3565b935060608701359150808211156130c5575f80fd5b506130d287828801612f5e565b91505092959194509250565b5f82601f8301126130ed575f80fd5b813560206130fd612ed483612f3b565b8083825260208201915060208460051b87010193508684111561311e575f80fd5b602086015b84811015612fba57803561313681612ccc565b8352918301918301613123565b5f805f805f60a08688031215613157575f80fd5b853567ffffffffffffffff8082111561316e575f80fd5b61317a89838a016130de565b9650602088013591508082111561318f575f80fd5b61319b89838a01612f5e565b955060408801359150808211156131b0575f80fd5b6131bc89838a01612f5e565b9450606088013591506131ce82612ccc565b909250608087013590808211156131e3575f80fd5b506131f088828901612eb7565b9150509295509295909350565b5f805f6060848603121561320f575f80fd5b833561321a81612ccc565b9250602084013561322a81612ccc565b929592945050506040919091013590565b5f805f6060848603121561324d575f80fd5b8335925060208401359150604084013567ffffffffffffffff811115613271575f80fd5b61327d86828701612eb7565b9150509250925092565b5f805f806080858703121561329a575f80fd5b843567ffffffffffffffff808211156132b1575f80fd5b6132bd888389016130de565b955060208701359150808211156132d2575f80fd5b6132de88838901612f5e565b945060408701359150808211156132f3575f80fd5b6132ff88838901612f5e565b93506060870135915080821115613314575f80fd5b506130d287828801612eb7565b5f60208284031215613331575f80fd5b815180151581146118c3575f80fd5b5f82601f83011261334f575f80fd5b8135602061335f612ed483612f3b565b82815260059290921b8401810191818101908684111561337d575f80fd5b8286015b84811015612fba57803567ffffffffffffffff81111561339f575f80fd5b6133ad8986838b0101612eb7565b845250918301918301613381565b6001600160e01b031981168114610241575f80fd5b5f82601f8301126133df575f80fd5b813560206133ef612ed483612f3b565b8083825260208201915060208460051b870101935086841115613410575f80fd5b602086015b84811015612fba578035613428816133bb565b8352918301918301613415565b5f82601f830112613444575f80fd5b81356020613454612ed483612f3b565b82815260059290921b84018101918181019086841115613472575f80fd5b8286015b84811015612fba57803567ffffffffffffffff811115613494575f80fd5b6134a28986838b0101612fd3565b845250918301918301613476565b80356134bb81612ccc565b919050565b5f80604083850312156134d1575f80fd5b823567ffffffffffffffff808211156134e8575f80fd5b9084019060a082870312156134fb575f80fd5b613503612e13565b823582811115613511575f80fd5b61351d88828601612eb7565b825250602083013582811115613531575f80fd5b61353d88828601613340565b602083015250604083013582811115613554575f80fd5b61356088828601612f5e565b604083015250606083013582811115613577575f80fd5b613583888286016133d0565b60608301525060808301358281111561359a575f80fd5b6135a688828601613435565b60808301525093506135bd915050602084016134b0565b90509250929050565b808201808211156104b857634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f806040838503121561360a575f80fd5b823567ffffffffffffffff811115613620575f80fd5b61362c85828601612eb7565b925050602083013561363d81612ccc565b809150509250929050565b5f5b8381101561366257818101518382015260200161364a565b50505f910152565b5f82601f830112613679575f80fd5b8151613687612ed482612e90565b81815284602083860101111561369b575f80fd5b61090b826020830160208701613648565b5f82601f8301126136bb575f80fd5b815160206136cb612ed483612f3b565b82815260059290921b840181019181810190868411156136e9575f80fd5b8286015b84811015612fba57805167ffffffffffffffff81111561370b575f80fd5b6137198986838b010161366a565b8452509183019183016136ed565b5f82601f830112613736575f80fd5b81516020613746612ed483612f3b565b8083825260208201915060208460051b870101935086841115613767575f80fd5b602086015b84811015612fba578051835291830191830161376c565b5f82601f830112613792575f80fd5b815160206137a2612ed483612f3b565b8083825260208201915060208460051b8701019350868411156137c3575f80fd5b602086015b84811015612fba5780516137db816133bb565b83529183019183016137c8565b5f82601f8301126137f7575f80fd5b81516020613807612ed483612f3b565b828152600592831b8501820192828201919087851115613825575f80fd5b8387015b858110156138bf57805167ffffffffffffffff811115613847575f80fd5b8801603f81018a13613857575f80fd5b858101516040613869612ed483612f3b565b82815291851b8301810191888101908d841115613884575f80fd5b938201935b838510156138ae578451925061389e83612fc5565b8282529389019390890190613889565b885250505093850193508401613829565b5090979650505050505050565b80516134bb81612ccc565b5f80604083850312156138e8575f80fd5b825167ffffffffffffffff808211156138ff575f80fd5b9084019060a08287031215613912575f80fd5b61391a612e13565b825182811115613928575f80fd5b6139348882860161366a565b825250602083015182811115613948575f80fd5b613954888286016136ac565b60208301525060408301518281111561396b575f80fd5b61397788828601613727565b60408301525060608301518281111561398e575f80fd5b61399a88828601613783565b6060830152506080830151828111156139b1575f80fd5b6139bd888286016137e8565b60808301525093506135bd915050602084016138cc565b5f602082840312156139e4575f80fd5b81516118c381612ccc565b5f82601f8301126139fe575f80fd5b81516020613a0e612ed483612f3b565b8083825260208201915060208460051b870101935086841115613a2f575f80fd5b602086015b84811015612fba578051613a4781612ccc565b8352918301918301613a34565b5f60208284031215613a64575f80fd5b815167ffffffffffffffff80821115613a7b575f80fd5b9083019060e08286031215613a8e575f80fd5b613a96612e3c565b825182811115613aa4575f80fd5b613ab0878286016139ef565b825250602083015182811115613ac4575f80fd5b613ad087828601613727565b602083015250604083015182811115613ae7575f80fd5b613af387828601613727565b604083015250613b05606084016138cc565b6060820152613b16608084016138cc565b608082015260a083015182811115613b2c575f80fd5b613b388782860161366a565b60a08301525060c083015182811115613b4f575f80fd5b613b5b8782860161366a565b60c08301525095945050505050565b5f815160208301517fff0000000000000000000000000000000000000000000000000000000000000080821693506001831015613bb15780818460010360031b1b83161693505b505050919050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613bdd575f80fd5b815162ffffff811681146118c3575f80fd5b5f60208284031215613bff575f80fd5b5051919050565b5f8151808452613c1d816020860160208601613648565b601f01601f19169290920160200192915050565b5f815180845260208085019450602084015f5b83811015613c6057815187529582019590820190600101613c44565b509495945050505050565b5f815180845260208085019450602084015f5b83811015613c605781516001600160e01b03191687529582019590820190600101613c7e565b5f82825180855260208086019550808260051b8401018186015f5b848110156138bf57858303601f190189528151805180855290850190858501905f5b81811015613d0057835160ff1683529287019291870191600101613ce1565b50509985019993505090830190600101613cbf565b604081525f835160a06040840152613d3060e0840182613c06565b9050602080860151603f19808685030160608701528382518086528486019150848160051b87010185850194505f5b82811015613d8d57601f19888303018452613d7b828751613c06565b95870195938701939150600101613d5f565b5060408b01519650838982030160808a0152613da98188613c31565b965050505060608801519150808685030160a0870152613dc98483613c6b565b935060808801519150808685030160c087015250613de78382613ca4565b935050848185015250509392505050565b6001600160a01b0383168152604060208201525f61090b6040830184613c06565b6001600160a01b0385168152836020820152608060408201525f613e406080830185613c06565b905060028310613e5e57634e487b7160e01b5f52602160045260245ffd5b82606083015295945050505050565b5f815180845260208085019450602084015f5b83811015613c605781516001600160a01b031687529582019590820190600101613e80565b5f6001600160a01b03808a16835260e06020840152613ec760e084018a613e6d565b8381036040850152613ed9818a613c31565b90508381036060850152613eed8189613c31565b9050818716608085015283810360a0850152613f098187613c06565b9250505061ffff831660c083015298975050505050505050565b60a08152600660a08201527f414156455632000000000000000000000000000000000000000000000000000060c082015260e060208201525f613f6960e0830187613e6d565b8281036040840152613f7b8187613c31565b90508281036060840152613f8f8186613c31565b9150506001600160a01b038316608083015295945050505050565b602081525f6118c36020830184613c06565b6001600160a01b0385168152608060208201525f613fdd6080830186613e6d565b8281036040840152613fef8186613c31565b905082810360608401526140038185613c06565b979650505050505050565b5f815160e0845261402260e0850182613e6d565b90506020830151848203602086015261403b8282613c31565b915050604083015184820360408601526140558282613c31565b91505060608301516001600160a01b038082166060870152806080860151166080870152505060a083015184820360a08601526140928282613c06565b91505060c083015184820360c0860152612ba98282613c06565b60408152600860408201527f42414c414e4345520000000000000000000000000000000000000000000000006060820152608060208201525f6118c3608083018461400e565b5f6001600160a01b038087168352808616602084015250836040830152608060608301526141236080830184613c06565b9695505050505050565b60a08152600660a08201527f414156455633000000000000000000000000000000000000000000000000000060c082015260e060208201525f613f6960e0830187613e6d565b602081525f6118c3602083018461400e565b6001600160a01b0385168152836020820152826040820152608060608201525f6141236080830184613c06565b604081525f6141c46040830185613c06565b90506001600160a01b03831660208301529392505050565b6001600160a01b0384168152826020820152606060408201525f612ba96060830184613c06565b5f8251614214818460208701613648565b919091019291505056fea264697066735822122097a88cdfe1242c80d5b121a1d4a50e116a7b5ba94936c1f4bb450f95c4bae95464736f6c63430008180033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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.