ETH Price: $1,905.35 (+0.06%)
Gas: 20 Gwei
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multi Chain

Multichain Addresses

0 address found via
Transaction Hash
Method
Block
From
To
Value
0x60c06040133666592021-10-06 17:02:53605 days 17 hrs ago1633539773IN
 Create: ExecFacet
0 ETH0.22938006130

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ExecFacet

Compiler Version
v0.8.0+commit.c7dfd78e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 21 : ExecFacet.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

import {BFacetOwner} from "../facets/base/BFacetOwner.sol";
import {LibAddress} from "../libraries/LibAddress.sol";
import {LibDiamond} from "../libraries/standard/LibDiamond.sol";
import {LibExecutor} from "../libraries/LibExecutor.sol";
import {
    EnumerableSet
} from "../../vendor/openzeppelin/contracts/utils/EnumerableSet.sol";
import {GelatoBytes} from "../../lib/GelatoBytes.sol";
import {GelatoString} from "../../lib/GelatoString.sol";
import {getBalance} from "../../vendor/utils/Utils.sol";
import {_getCappedGasPrice} from "../../functions/gelato/FGelato.sol";
import {IGelatoV1} from "../../interfaces/gelato/IGelatoV1.sol";
import {ETH} from "../../constants/CTokens.sol";
import {IOracleAggregator} from "../../interfaces/gelato/IOracleAggregator.sol";

contract ExecFacet is BFacetOwner {
    using LibDiamond for address;
    using LibExecutor for address;
    using GelatoBytes for bytes;
    using EnumerableSet for EnumerableSet.AddressSet;
    using GelatoString for string;

    // solhint-disable var-name-mixedcase
    uint256 public constant GAS_OVERHEAD = 40000;
    IGelatoV1 public immutable GELATO_V1;
    address public immutable GELATO_PROVIDER;

    // solhint-enable var-name-mixedcase

    event LogExecSuccess(
        address indexed executor,
        address indexed service,
        bool indexed wasExecutorPaid
    );

    event LogSetGasMargin(uint256 oldGasMargin, uint256 newGasMargin);

    constructor(IGelatoV1 _gelatoV1, address _gelatoProvider) {
        GELATO_V1 = _gelatoV1;
        GELATO_PROVIDER = _gelatoProvider;
    }

    // ################ Callable by Gov ################
    function addExecutors(address[] calldata _executors) external onlyOwner {
        for (uint256 i; i < _executors.length; i++)
            require(_executors[i].addExecutor(), "ExecFacet.addExecutors");
    }

    function addBundleExecutors(address[] calldata _bundleExecutors)
        external
        onlyOwner
    {
        for (uint256 i; i < _bundleExecutors.length; i++)
            require(
                _bundleExecutors[i].addBundleExecutor(),
                "ExecFacet.addBundleExecutors"
            );
    }

    function removeExecutors(address[] calldata _executors) external {
        for (uint256 i; i < _executors.length; i++) {
            require(
                msg.sender == _executors[i] || msg.sender.isContractOwner(),
                "ExecFacet.removeExecutors: msg.sender ! executor || owner"
            );
            require(
                _executors[i].removeExecutor(),
                "ExecFacet.removeExecutors"
            );
        }
    }

    function removeBundleExecutors(address[] calldata _bundleExecutors)
        external
    {
        for (uint256 i; i < _bundleExecutors.length; i++) {
            require(
                msg.sender == _bundleExecutors[i] ||
                    msg.sender.isContractOwner(),
                "ExecFacet.removeBundleExecutors: msg.sender ! bundleExecutor || owner"
            );
            require(
                _bundleExecutors[i].removeBundleExecutor(),
                "ExecFacet.removeBundleExecutors"
            );
        }
    }

    function setGasMargin(uint256 _gasMargin) external onlyOwner {
        emit LogSetGasMargin(gasMargin(), _gasMargin);
        LibExecutor.setGasMargin(_gasMargin);
    }

    // solhint-disable function-max-lines
    // ################ Callable by Executor ################
    /// @dev * reverts if Executor overcharges users
    ///      * assumes honest executors
    ///      * verifying correct fee can be removed after staking/slashing
    ///        was introduced
    // solhint-disable-next-line code-complexity
    function exec(
        address _service,
        bytes calldata _data,
        address _creditToken
    ) external {
        uint256 startGas = gasleft();

        require(
            msg.sender.isExecutor() || msg.sender.isBundleExecutor(),
            "ExecFacet.exec: onlyExecutors || onlyBundleExecutors"
        );

        if (_service == address(GELATO_V1)) _creditToken = ETH;

        uint256 preCreditTokenBalance = getBalance(_creditToken, address(this));

        (bool success, bytes memory returndata) = _service.call(_data);
        if (!success) returndata.revertWithError("ExecFacet.exec:");

        // Needs to be here in case service relies on GelatoV1 built-in provider ETH payments
        if (_service == address(GELATO_V1))
            GELATO_V1.withdrawExcessExecutorStake(type(uint256).max);

        uint256 postCreditTokenBalance = getBalance(
            _creditToken,
            address(this)
        );

        // TO DO: remove and replace with executor payments based on what services paid out
        require(
            postCreditTokenBalance > preCreditTokenBalance,
            "ExecFacet.exec: postCreditTokenBalance < preCreditTokenBalance"
        );

        uint256 credit = postCreditTokenBalance - preCreditTokenBalance;

        uint256 gasDebitInETH = (startGas - gasleft() + GAS_OVERHEAD) *
            _getCappedGasPrice(LibAddress.getGasPriceOracle());

        uint256 gasDebitInCreditToken;
        if (_creditToken == ETH) gasDebitInCreditToken = gasDebitInETH;
        else {
            (gasDebitInCreditToken, ) = IOracleAggregator(
                LibAddress.getOracleAggregator()
            ).getExpectedReturnAmount(gasDebitInETH, ETH, _creditToken);
        }

        require(
            gasDebitInCreditToken != 0,
            "ExecFacet.exec:  _creditToken not on OracleAggregator"
        );

        uint256 _gasMargin_ = gasMargin();

        require(
            credit <=
                gasDebitInCreditToken +
                    (gasDebitInCreditToken * _gasMargin_) /
                    100,
            "ExecFacet.exec: Executor Overcharged"
        );

        if (_service == address(GELATO_V1))
            if (abi.decode(_data[68:100], (address)) == GELATO_PROVIDER) {
                // solhint-disable no-empty-blocks
                try
                    GELATO_V1.provideFunds{value: credit}(GELATO_PROVIDER)
                {} catch {}
                // solhint-enable no-empty-blocks
            }

        /// TO DO: pay executors based 1:1 on what services paid gelato in ETH equivalents
        (success, ) = msg.sender.call{
            value: gasDebitInETH + (gasDebitInETH * _gasMargin_) / 100
        }("");

        emit LogExecSuccess(msg.sender, _service, success);
    }

    function estimateExecGasDebit(
        address _service,
        bytes calldata _data,
        address _creditToken
    ) external returns (uint256 gasDebitInETH, uint256 gasDebitInCreditToken) {
        uint256 startGas = gasleft();

        require(
            msg.sender.isExecutor() || msg.sender.isBundleExecutor(),
            "ExecFacet.exec: onlyExecutors || onlyBundleExecutors"
        );

        if (_service == address(GELATO_V1)) _creditToken = ETH;

        uint256 preCreditTokenBalance = getBalance(_creditToken, address(this));

        (bool success, bytes memory returndata) = _service.call(_data);
        if (!success) returndata.revertWithError("ExecFacet.exec:");

        // Needs to be here in case service relies on GelatoV1 built-in provider ETH payments
        if (_service == address(GELATO_V1))
            GELATO_V1.withdrawExcessExecutorStake(type(uint256).max);

        uint256 postCreditTokenBalance = getBalance(
            _creditToken,
            address(this)
        );

        uint256 credit = postCreditTokenBalance - preCreditTokenBalance;
        credit;

        gasDebitInETH =
            (startGas - gasleft() + GAS_OVERHEAD) *
            _getCappedGasPrice(LibAddress.getGasPriceOracle());

        if (_creditToken == ETH) gasDebitInCreditToken = gasDebitInETH;
        else {
            (gasDebitInCreditToken, ) = IOracleAggregator(
                LibAddress.getOracleAggregator()
            ).getExpectedReturnAmount(gasDebitInETH, ETH, _creditToken);
        }
    }

    function canExec(address _executor) external view returns (bool) {
        return _executor.canExec();
    }

    function isExecutor(address _executor) external view returns (bool) {
        return _executor.isExecutor();
    }

    function isBundleExecutor(address _bundleExecutor)
        external
        view
        returns (bool)
    {
        return _bundleExecutor.isBundleExecutor();
    }

    function executors() external view returns (address[] memory) {
        return LibExecutor.executors();
    }

    function bundleExecutors() external view returns (address[] memory) {
        return LibExecutor.bundleExecutors();
    }

    function numberOfExecutors() external view returns (uint256) {
        return LibExecutor.numberOfExecutors();
    }

    function numberOfBundleExecutors() external view returns (uint256) {
        return LibExecutor.numberOfBundleExecutors();
    }

    function gasMargin() public view returns (uint256) {
        return LibExecutor.gasMargin();
    }
}

File 2 of 21 : IGelatoCondition.sol
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity >=0.6.10;
pragma experimental ABIEncoderV2;

/// @title IGelatoCondition - solidity interface of GelatoConditionsStandard
/// @notice all the APIs of GelatoConditionsStandard
/// @dev all the APIs are implemented inside GelatoConditionsStandard
interface IGelatoCondition {

    /// @notice GelatoCore calls this to verify securely the specified Condition securely
    /// @dev Be careful only to encode a Task's condition.data as is and not with the
    ///  "ok" selector or _taskReceiptId, since those two things are handled by GelatoCore.
    /// @param _taskReceiptId This is passed by GelatoCore so we can rely on it as a secure
    ///  source of Task identification.
    /// @param _conditionData This is the Condition.data field developers must encode their
    ///  Condition's specific parameters in.
    /// @param _cycleId For Tasks that are executed as part of a cycle.
    function ok(uint256 _taskReceiptId, bytes calldata _conditionData, uint256 _cycleId)
        external
        view
        returns(string memory);
}

File 3 of 21 : IGelatoCore.sol
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity >=0.6.10;
pragma experimental ABIEncoderV2;

import {IGelatoProviderModule} from "../../gelato_provider_modules/IGelatoProviderModule.sol";
import {IGelatoCondition} from "../../gelato_conditions/IGelatoCondition.sol";

struct Provider {
    address addr;  //  if msg.sender == provider => self-Provider
    IGelatoProviderModule module;  //  can be IGelatoProviderModule(0) for self-Providers
}

struct Condition {
    IGelatoCondition inst;  // can be AddressZero for self-conditional Actions
    bytes data;  // can be bytes32(0) for self-conditional Actions
}

enum Operation { Call, Delegatecall }

enum DataFlow { None, In, Out, InAndOut }

struct Action {
    address addr;
    bytes data;
    Operation operation;
    DataFlow dataFlow;
    uint256 value;
    bool termsOkCheck;
}

struct Task {
    Condition[] conditions;  // optional
    Action[] actions;
    uint256 selfProviderGasLimit;  // optional: 0 defaults to gelatoMaxGas
    uint256 selfProviderGasPriceCeil;  // optional: 0 defaults to NO_CEIL
}

struct TaskReceipt {
    uint256 id;
    address userProxy;
    Provider provider;
    uint256 index;
    Task[] tasks;
    uint256 expiryDate;
    uint256 cycleId;  // auto-filled by GelatoCore. 0 for non-cyclic/chained tasks
    uint256 submissionsLeft;
}

interface IGelatoCore {
    event LogTaskSubmitted(
        uint256 indexed taskReceiptId,
        bytes32 indexed taskReceiptHash,
        TaskReceipt taskReceipt
    );

    event LogExecSuccess(
        address indexed executor,
        uint256 indexed taskReceiptId,
        uint256 executorSuccessFee,
        uint256 sysAdminSuccessFee
    );
    event LogCanExecFailed(
        address indexed executor,
        uint256 indexed taskReceiptId,
        string reason
    );
    event LogExecReverted(
        address indexed executor,
        uint256 indexed taskReceiptId,
        uint256 executorRefund,
        string reason
    );

    event LogTaskCancelled(uint256 indexed taskReceiptId, address indexed cancellor);

    /// @notice API to query whether Task can be submitted successfully.
    /// @dev In submitTask the msg.sender must be the same as _userProxy here.
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _userProxy The userProxy from which the task will be submitted.
    /// @param _task Selected provider, conditions, actions, expiry date of the task
    function canSubmitTask(
        address _userProxy,
        Provider calldata _provider,
        Task calldata _task,
        uint256 _expiryDate
    )
        external
        view
        returns(string memory);

    /// @notice API to submit a single Task.
    /// @dev You can let users submit multiple tasks at once by batching calls to this.
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _task A Gelato Task object: provider, conditions, actions.
    /// @param _expiryDate From then on the task cannot be executed. 0 for infinity.
    function submitTask(
        Provider calldata _provider,
        Task calldata _task,
        uint256 _expiryDate
    )
        external;


    /// @notice A Gelato Task Cycle consists of 1 or more Tasks that automatically submit
    ///  the next one, after they have been executed.
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _tasks This can be a single task or a sequence of tasks.
    /// @param _expiryDate  After this no task of the sequence can be executed any more.
    /// @param _cycles How many full cycles will be submitted
    function submitTaskCycle(
        Provider calldata _provider,
        Task[] calldata _tasks,
        uint256 _expiryDate,
        uint256 _cycles
    )
        external;


    /// @notice A Gelato Task Cycle consists of 1 or more Tasks that automatically submit
    ///  the next one, after they have been executed.
    /// @dev CAUTION: _sumOfRequestedTaskSubmits does not mean the number of cycles.
    /// @dev If _sumOfRequestedTaskSubmits = 1 && _tasks.length = 2, only the first task
    ///  would be submitted, but not the second
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _tasks This can be a single task or a sequence of tasks.
    /// @param _expiryDate  After this no task of the sequence can be executed any more.
    /// @param _sumOfRequestedTaskSubmits The TOTAL number of Task auto-submits
    ///  that should have occured once the cycle is complete:
    ///  _sumOfRequestedTaskSubmits = 0 => One Task will resubmit the next Task infinitly
    ///  _sumOfRequestedTaskSubmits = 1 => One Task will resubmit no other task
    ///  _sumOfRequestedTaskSubmits = 2 => One Task will resubmit 1 other task
    ///  ...
    function submitTaskChain(
        Provider calldata _provider,
        Task[] calldata _tasks,
        uint256 _expiryDate,
        uint256 _sumOfRequestedTaskSubmits
    )
        external;

    // ================  Exec Suite =========================
    /// @notice Off-chain API for executors to check, if a TaskReceipt is executable
    /// @dev GelatoCore checks this during execution, in order to safeguard the Conditions
    /// @param _TR TaskReceipt, consisting of user task, user proxy address and id
    /// @param _gasLimit Task.selfProviderGasLimit is used for SelfProviders. All other
    ///  Providers must use gelatoMaxGas. If the _gasLimit is used by an Executor and the
    ///  tx reverts, a refund is paid by the Provider and the TaskReceipt is annulated.
    /// @param _execTxGasPrice Must be used by Executors. Gas Price fed by gelatoCore's
    ///  Gas Price Oracle. Executors can query the current gelatoGasPrice from events.
    function canExec(TaskReceipt calldata _TR, uint256 _gasLimit, uint256 _execTxGasPrice)
        external
        view
        returns(string memory);

    /// @notice Executors call this when Conditions allow it to execute submitted Tasks.
    /// @dev Executors get rewarded for successful Execution. The Task remains open until
    ///   successfully executed, or when the execution failed, despite of gelatoMaxGas usage.
    ///   In the latter case Executors are refunded by the Task Provider.
    /// @param _TR TaskReceipt: id, userProxy, Task.
    function exec(TaskReceipt calldata _TR) external;

    /// @notice Cancel task
    /// @dev Callable only by userProxy or selected provider
    /// @param _TR TaskReceipt: id, userProxy, Task.
    function cancelTask(TaskReceipt calldata _TR) external;

    /// @notice Cancel multiple tasks at once
    /// @dev Callable only by userProxy or selected provider
    /// @param _taskReceipts TaskReceipts: id, userProxy, Task.
    function multiCancelTasks(TaskReceipt[] calldata _taskReceipts) external;

    /// @notice Compute hash of task receipt
    /// @param _TR TaskReceipt, consisting of user task, user proxy address and id
    /// @return hash of taskReceipt
    function hashTaskReceipt(TaskReceipt calldata _TR) external pure returns(bytes32);

    // ================  Getters =========================
    /// @notice Returns the taskReceiptId of the last TaskReceipt submitted
    /// @return currentId currentId, last TaskReceiptId submitted
    function currentTaskReceiptId() external view returns(uint256);

    /// @notice Returns computed taskReceipt hash, used to check for taskReceipt validity
    /// @param _taskReceiptId Id of taskReceipt emitted in submission event
    /// @return hash of taskReceipt
    function taskReceiptHash(uint256 _taskReceiptId) external view returns(bytes32);
}

File 4 of 21 : IGelatoProviderModule.sol
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity >=0.6.10;
pragma experimental ABIEncoderV2;

import {Action, Task} from "../gelato_core/interfaces/IGelatoCore.sol";

interface IGelatoProviderModule {

    /// @notice Check if provider agrees to pay for inputted task receipt
    /// @dev Enables arbitrary checks by provider
    /// @param _userProxy The smart contract account of the user who submitted the Task.
    /// @param _provider The account of the Provider who uses the ProviderModule.
    /// @param _task Gelato Task to be executed.
    /// @return "OK" if provider agrees
    function isProvided(address _userProxy, address _provider, Task calldata _task)
        external
        view
        returns(string memory);

    /// @notice Convert action specific payload into proxy specific payload
    /// @dev Encoded multiple actions into a multisend
    /// @param _taskReceiptId Unique ID of Gelato Task to be executed.
    /// @param _userProxy The smart contract account of the user who submitted the Task.
    /// @param _provider The account of the Provider who uses the ProviderModule.
    /// @param _task Gelato Task to be executed.
    /// @param _cycleId For Tasks that form part of a cycle/chain.
    /// @return Encoded payload that will be used for low-level .call on user proxy
    /// @return checkReturndata if true, fwd returndata from userProxy.call to ProviderModule
    function execPayload(
        uint256 _taskReceiptId,
        address _userProxy,
        address _provider,
        Task calldata _task,
        uint256 _cycleId
    )
        external
        view
        returns(bytes memory, bool checkReturndata);

    /// @notice Called by GelatoCore.exec to verifiy that no revert happend on userProxy
    /// @dev If a caught revert is detected, this fn should revert with the detected error
    /// @param _proxyReturndata Data from GelatoCore._exec.userProxy.call(execPayload)
    function execRevertCheck(bytes calldata _proxyReturndata) external pure;
}

File 5 of 21 : CTokens.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;

uint256 constant DECIMALS_USD = 8;

uint256 constant DECIMALS_USDT = 6;

uint256 constant DECIMALS_USDC = 6;

uint256 constant DECIMALS_DAI = 18;

uint256 constant DECIMALS_BUSD = 18;

uint256 constant DECIMALS_SUSD = 18;

uint256 constant DECIMALS_TUSD = 18;

address constant USD = 0x7354C81fbCb229187480c4f497F945C6A312d5C3; // Random address

address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

address constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;

address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;

address constant BUSD = 0x4Fabb145d64652a948d72533023f6E7A623C7C53;

address constant SUSD = 0x57Ab1ec28D129707052df4dF418D58a2D46d5f51;

address constant TUSD = 0x0000000000085d4780B73119b644AE5ecd22b376;

address constant AAVE = 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9;

address constant ADX = 0xADE00C28244d5CE17D72E40330B1c318cD12B7c3;

address constant BAT = 0x0D8775F648430679A709E98d2b0Cb6250d2887EF;

address constant BNB = 0xB8c77482e45F1F44dE1745F52C74426C631bDD52;

address constant BNT = 0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C;

address constant BZRX = 0x56d811088235F11C8920698a204A5010a788f4b3;

address constant COMP = 0xc00e94Cb662C3520282E6f5717214004A7f26888;

address constant CRO = 0xA0b73E1Ff0B80914AB6fe0444E65848C4C34450b;

address constant DMG = 0xEd91879919B71bB6905f23af0A68d231EcF87b14;

address constant ENJ = 0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c;

address constant KNC = 0xdd974D5C2e2928deA5F71b9825b8b646686BD200;

address constant LINK = 0x514910771AF9Ca656af840dff83E8264EcF986CA;

address constant LRC = 0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD;

address constant MANA = 0x0F5D2fB29fb7d3CFeE444a200298f468908cC942;

address constant MKR = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2;

address constant NMR = 0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671;

address constant REN = 0x408e41876cCCDC0F92210600ef50372656052a38;

address constant REP = 0x221657776846890989a759BA2973e427DfF5C9bB;

address constant SNX = 0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F;

address constant SXP = 0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9;

address constant UNI = 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984;

address constant WOM = 0xa982B2e19e90b2D9F7948e9C1b65D119F1CE88D6;

address constant YFI = 0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e;

address constant ZRX = 0xE41d2489571d322189246DaFA5ebDe1F4699F498;

File 6 of 21 : BFacetOwner.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

import {LibDiamond} from "../../libraries/standard/LibDiamond.sol";

abstract contract BFacetOwner {
    modifier onlyOwner() {
        LibDiamond.enforceIsContractOwner();
        _;
    }
}

File 7 of 21 : IGelato.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

import {IDiamondCut} from "./standard/IDiamondCut.sol";
import {IDiamondLoupe} from "./standard/IDiamondLoupe.sol";
import {
    TaskReceipt
} from "@gelatonetwork/core/contracts/gelato_core/interfaces/IGelatoCore.sol";
import {IGelatoV1} from "../../interfaces/gelato/IGelatoV1.sol";

// solhint-disable ordering

/// @dev includes the interfaces of all facets
interface IGelato {
    // ########## Diamond Cut Facet #########
    event DiamondCut(
        IDiamondCut.FacetCut[] _diamondCut,
        address _init,
        bytes _calldata
    );

    function diamondCut(
        IDiamondCut.FacetCut[] calldata _diamondCut,
        address _init,
        bytes calldata _calldata
    ) external;

    // ########## DiamondLoupeFacet #########
    function facets()
        external
        view
        returns (IDiamondLoupe.Facet[] memory facets_);

    function facetFunctionSelectors(address _facet)
        external
        view
        returns (bytes4[] memory facetFunctionSelectors_);

    function facetAddresses()
        external
        view
        returns (address[] memory facetAddresses_);

    function facetAddress(bytes4 _functionSelector)
        external
        view
        returns (address facetAddress_);

    function supportsInterface(bytes4 _interfaceId)
        external
        view
        returns (bool);

    // ########## Ownership Facet #########
    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );

    function transferOwnership(address _newOwner) external;

    function owner() external view returns (address owner_);

    // ########## AddressFacet #########
    event LogSetOracleAggregator(address indexed oracleAggregator);
    event LogSetGasPriceOracle(address indexed gasPriceOracle);

    function setOracleAggregator(address _oracleAggregator)
        external
        returns (address);

    function setGasPriceOracle(address _gasPriceOracle)
        external
        returns (address);

    function getOracleAggregator() external view returns (address);

    function getGasPriceOracle() external view returns (address);

    // ########## ConcurrentCanExecFacet #########
    enum SlotStatus {
        Open,
        Closing,
        Closed
    }

    function setSlotLength(uint256 _slotLength) external;

    function slotLength() external view returns (uint256);

    function concurrentCanExec(uint256 _buffer) external view returns (bool);

    function getCurrentExecutorIndex()
        external
        view
        returns (uint256 executorIndex, uint256 remainingBlocksInSlot);

    function currentExecutor()
        external
        view
        returns (
            address executor,
            uint256 executorIndex,
            uint256 remainingBlocksInSlot
        );

    function mySlotStatus(uint256 _buffer) external view returns (SlotStatus);

    function calcExecutorIndex(
        uint256 _currentBlock,
        uint256 _blocksPerSlot,
        uint256 _numberOfExecutors
    )
        external
        pure
        returns (uint256 executorIndex, uint256 remainingBlocksInSlot);

    // ########## ExecFacet #########

    // solhint-disable-next-line func-name-mixedcase
    function GAS_OVERHEAD() external view returns (uint256);

    // solhint-disable-next-line func-name-mixedcase
    function GELATO_V1() external view returns (address);

    // solhint-disable-next-line func-name-mixedcase
    function GELATO_PROVIDER() external view returns (address);

    event LogExecSuccess(
        address indexed executor,
        address indexed service,
        bool indexed wasExecutorPaid
    );

    event LogSetGasMargin(uint256 oldGasMargin, uint256 newGasMargin);

    function addExecutors(address[] calldata _executors) external;

    function addBundleExecutors(address[] calldata _bundleExecutors) external;

    function removeExecutors(address[] calldata _executors) external;

    function removeBundleExecutors(address[] calldata _bundleExecutors)
        external;

    function setGasMargin(uint256 _gasMargin) external;

    function exec(
        address _service,
        bytes calldata _data,
        address _creditToken
    ) external;

    function estimateExecGasDebit(
        address _service,
        bytes calldata _data,
        address _creditToken
    ) external returns (uint256 gasDebitInETH, uint256 gasDebitInCreditToken);

    function canExec(address _executor) external view returns (bool);

    function isExecutor(address _executor) external view returns (bool);

    function isBundleExecutor(address _bundleExecutor)
        external
        view
        returns (bool);

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

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

    function numberOfExecutors() external view returns (uint256);

    function numberOfBundleExecutors() external view returns (uint256);

    function gasMargin() external view returns (uint256);

    // ########## GelatoV1Facet #########
    struct Response {
        uint256 taskReceiptId;
        uint256 taskGasLimit;
        string response;
    }

    function stakeExecutor(IGelatoV1 _gelatoCore) external payable;

    function unstakeExecutor(IGelatoV1 _gelatoCore, address payable _to)
        external;

    function multiReassignProviders(
        IGelatoV1 _gelatoCore,
        address[] calldata _providers,
        address _newExecutor
    ) external;

    function providerRefund(
        IGelatoV1 _gelatoCore,
        address _provider,
        uint256 _amount
    ) external;

    function withdrawExcessExecutorStake(
        IGelatoV1 _gelatoCore,
        uint256 _withdrawAmount,
        address payable _to
    ) external;

    function v1ConcurrentMultiCanExec(
        address _gelatoCore,
        TaskReceipt[] calldata _taskReceipts,
        uint256 _gelatoGasPrice,
        uint256 _buffer
    )
        external
        view
        returns (
            bool canExecRes,
            uint256 blockNumber,
            Response[] memory responses
        );

    function v1MultiCanExec(
        address _gelatoCore,
        TaskReceipt[] calldata _taskReceipts,
        uint256 _gelatoGasPrice
    ) external view returns (uint256 blockNumber, Response[] memory responses);

    function getGasLimit(
        TaskReceipt calldata _taskReceipt,
        uint256 _gelatoMaxGas
    ) external pure returns (uint256);
}

File 8 of 21 : IDiamondCut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

/******************************************************************************\
* Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
* EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/

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

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

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

    /// @notice Add/replace/remove any number of functions and optionally execute
    ///         a function with delegatecall
    /// @param _diamondCut Contains the facet addresses and function selectors
    /// @param _init The address of the contract or facet to execute _calldata
    /// @param _calldata A function call, including function selector and arguments
    ///                  _calldata is executed with delegatecall on _init
    function diamondCut(
        FacetCut[] calldata _diamondCut,
        address _init,
        bytes calldata _calldata
    ) external;
}

File 9 of 21 : IDiamondLoupe.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

/******************************************************************************\
* Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
* EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/

// A loupe is a small magnifying glass used to look at diamonds.
// These functions look at diamonds
interface IDiamondLoupe {
    /// These functions are expected to be called frequently
    /// by tools.

    struct Facet {
        address facetAddress;
        bytes4[] functionSelectors;
    }

    /// @notice Gets all facet addresses and their four byte function selectors.
    /// @return facets_ Facet
    function facets() external view returns (Facet[] memory facets_);

    /// @notice Gets all the function selectors supported by a specific facet.
    /// @param _facet The facet address.
    /// @return facetFunctionSelectors_
    function facetFunctionSelectors(address _facet)
        external
        view
        returns (bytes4[] memory facetFunctionSelectors_);

    /// @notice Get all the facet addresses used by a diamond.
    /// @return facetAddresses_
    function facetAddresses()
        external
        view
        returns (address[] memory facetAddresses_);

    /// @notice Gets the facet that supports the given selector.
    /// @dev If facet is not found return address(0).
    /// @param _functionSelector The function selector.
    /// @return facetAddress_ The facet address.
    function facetAddress(bytes4 _functionSelector)
        external
        view
        returns (address facetAddress_);
}

File 10 of 21 : LibAddress.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

library LibAddress {
    struct AddressStorage {
        address oracleAggregator;
        address gasPriceOracle;
    }

    bytes32 private constant _ADDRESS_STORAGE =
        keccak256("gelato.diamond.address.storage");

    function setOracleAggregator(address _oracleAggregator) internal {
        LibAddress.addressStorage().oracleAggregator = _oracleAggregator;
    }

    function setGasPriceOracle(address _gasPriceOracle) internal {
        LibAddress.addressStorage().gasPriceOracle = _gasPriceOracle;
    }

    function getOracleAggregator() internal view returns (address) {
        return addressStorage().oracleAggregator;
    }

    function getGasPriceOracle() internal view returns (address) {
        return addressStorage().gasPriceOracle;
    }

    function addressStorage()
        internal
        pure
        returns (AddressStorage storage ads)
    {
        bytes32 position = _ADDRESS_STORAGE;
        assembly {
            ads.slot := position
        }
    }
}

File 11 of 21 : LibExecutor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

import {
    EnumerableSet
} from "../../vendor/openzeppelin/contracts/utils/EnumerableSet.sol";

library LibExecutor {
    using EnumerableSet for EnumerableSet.AddressSet;

    struct ExecutorStorage {
        EnumerableSet.AddressSet executors;
        uint256 gasMargin;
        EnumerableSet.AddressSet bundleExecutors;
    }

    bytes32 private constant _EXECUTOR_STORAGE_POSITION =
        keccak256("gelato.diamond.executor.storage");

    function addExecutor(address _executor) internal returns (bool) {
        return executorStorage().executors.add(_executor);
    }

    function addBundleExecutor(address _bundleExecutor)
        internal
        returns (bool)
    {
        return executorStorage().bundleExecutors.add(_bundleExecutor);
    }

    function removeExecutor(address _executor) internal returns (bool) {
        return executorStorage().executors.remove(_executor);
    }

    function removeBundleExecutor(address _bundleExecutor)
        internal
        returns (bool)
    {
        return executorStorage().bundleExecutors.remove(_bundleExecutor);
    }

    function setGasMargin(uint256 _gasMargin) internal {
        executorStorage().gasMargin = _gasMargin;
    }

    function canExec(address _executor) internal view returns (bool) {
        return isExecutor(_executor);
    }

    function isExecutor(address _executor) internal view returns (bool) {
        return executorStorage().executors.contains(_executor);
    }

    function isBundleExecutor(address _bundleExecutor)
        internal
        view
        returns (bool)
    {
        return executorStorage().bundleExecutors.contains(_bundleExecutor);
    }

    function executorAt(uint256 _index) internal view returns (address) {
        return executorStorage().executors.at(_index);
    }

    function bundleExecutorAt(uint256 _index) internal view returns (address) {
        return executorStorage().bundleExecutors.at(_index);
    }

    function executors() internal view returns (address[] memory executors_) {
        uint256 length = numberOfExecutors();
        executors_ = new address[](length);
        for (uint256 i; i < length; i++) executors_[i] = executorAt(i);
    }

    function bundleExecutors()
        internal
        view
        returns (address[] memory bundleExecutors_)
    {
        uint256 length = numberOfBundleExecutors();
        bundleExecutors_ = new address[](length);
        for (uint256 i; i < length; i++)
            bundleExecutors_[i] = bundleExecutorAt(i);
    }

    function numberOfExecutors() internal view returns (uint256) {
        return executorStorage().executors.length();
    }

    function numberOfBundleExecutors() internal view returns (uint256) {
        return executorStorage().bundleExecutors.length();
    }

    function gasMargin() internal view returns (uint256) {
        return executorStorage().gasMargin;
    }

    function executorStorage()
        internal
        pure
        returns (ExecutorStorage storage es)
    {
        bytes32 position = _EXECUTOR_STORAGE_POSITION;
        assembly {
            es.slot := position
        }
    }
}

File 12 of 21 : LibDiamond.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
// solhint-disable max-line-length
// https://github.com/mudgen/diamond-3/blob/b009cd08b7822bad727bbcc47aa1b50d8b50f7f0/contracts/libraries/LibDiamond.sol#L1

/******************************************************************************\
* Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
* EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/

import "../../interfaces/standard/IDiamondCut.sol";

// Custom due to incorrect string casting (non UTF-8 formatted)
import {GelatoBytes} from "../../../lib/GelatoBytes.sol";

library LibDiamond {
    bytes32 constant DIAMOND_STORAGE_POSITION =
        keccak256("diamond.standard.diamond.storage");

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

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

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

    function diamondStorage()
        internal
        pure
        returns (DiamondStorage storage ds)
    {
        bytes32 position = DIAMOND_STORAGE_POSITION;
        assembly {
            ds.slot := position
        }
    }

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

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

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

    function isContractOwner(address _guy) internal view returns (bool) {
        return _guy == contractOwner();
    }

    function enforceIsContractOwner() internal view {
        require(
            msg.sender == diamondStorage().contractOwner,
            "LibDiamond: Must be contract owner"
        );
    }

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

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

    function addFunctions(
        address _facetAddress,
        bytes4[] memory _functionSelectors
    ) internal {
        require(
            _functionSelectors.length > 0,
            "LibDiamondCut: No selectors in facet to cut"
        );
        DiamondStorage storage ds = diamondStorage();
        // uint16 selectorCount = uint16(diamondStorage().selectors.length);
        require(
            _facetAddress != address(0),
            "LibDiamondCut: Add facet can't be address(0)"
        );
        uint16 selectorPosition = uint16(
            ds.facetFunctionSelectors[_facetAddress].functionSelectors.length
        );
        // add new facet address if it does not exist
        if (selectorPosition == 0) {
            enforceHasContractCode(
                _facetAddress,
                "LibDiamondCut: New facet has no code"
            );
            ds
                .facetFunctionSelectors[_facetAddress]
                .facetAddressPosition = uint16(ds.facetAddresses.length);
            ds.facetAddresses.push(_facetAddress);
        }
        for (
            uint256 selectorIndex;
            selectorIndex < _functionSelectors.length;
            selectorIndex++
        ) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds
                .selectorToFacetAndPosition[selector]
                .facetAddress;
            require(
                oldFacetAddress == address(0),
                "LibDiamondCut: Can't add function that already exists"
            );
            ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(
                selector
            );
            ds
                .selectorToFacetAndPosition[selector]
                .facetAddress = _facetAddress;
            ds
                .selectorToFacetAndPosition[selector]
                .functionSelectorPosition = selectorPosition;
            selectorPosition++;
        }
    }

    function replaceFunctions(
        address _facetAddress,
        bytes4[] memory _functionSelectors
    ) internal {
        require(
            _functionSelectors.length > 0,
            "LibDiamondCut: No selectors in facet to cut"
        );
        DiamondStorage storage ds = diamondStorage();
        require(
            _facetAddress != address(0),
            "LibDiamondCut: Add facet can't be address(0)"
        );
        uint16 selectorPosition = uint16(
            ds.facetFunctionSelectors[_facetAddress].functionSelectors.length
        );
        // add new facet address if it does not exist
        if (selectorPosition == 0) {
            enforceHasContractCode(
                _facetAddress,
                "LibDiamondCut: New facet has no code"
            );
            ds
                .facetFunctionSelectors[_facetAddress]
                .facetAddressPosition = uint16(ds.facetAddresses.length);
            ds.facetAddresses.push(_facetAddress);
        }
        for (
            uint256 selectorIndex;
            selectorIndex < _functionSelectors.length;
            selectorIndex++
        ) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds
                .selectorToFacetAndPosition[selector]
                .facetAddress;
            require(
                oldFacetAddress != _facetAddress,
                "LibDiamondCut: Can't replace function with same function"
            );
            removeFunction(oldFacetAddress, selector);
            // add function
            ds
                .selectorToFacetAndPosition[selector]
                .functionSelectorPosition = selectorPosition;
            ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(
                selector
            );
            ds
                .selectorToFacetAndPosition[selector]
                .facetAddress = _facetAddress;
            selectorPosition++;
        }
    }

    function removeFunctions(
        address _facetAddress,
        bytes4[] memory _functionSelectors
    ) internal {
        require(
            _functionSelectors.length > 0,
            "LibDiamondCut: No selectors in facet to cut"
        );
        DiamondStorage storage ds = diamondStorage();
        // if function does not exist then do nothing and return
        require(
            _facetAddress == address(0),
            "LibDiamondCut: Remove facet address must be address(0)"
        );
        for (
            uint256 selectorIndex;
            selectorIndex < _functionSelectors.length;
            selectorIndex++
        ) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds
                .selectorToFacetAndPosition[selector]
                .facetAddress;
            removeFunction(oldFacetAddress, selector);
        }
    }

    function removeFunction(address _facetAddress, bytes4 _selector) internal {
        DiamondStorage storage ds = diamondStorage();
        require(
            _facetAddress != address(0),
            "LibDiamondCut: Can't remove function that doesn't exist"
        );
        // an immutable function is a function defined directly in a diamond
        require(
            _facetAddress != address(this),
            "LibDiamondCut: Can't remove immutable function"
        );
        // replace selector with last selector, then delete last selector
        uint256 selectorPosition = ds
            .selectorToFacetAndPosition[_selector]
            .functionSelectorPosition;
        uint256 lastSelectorPosition = ds
            .facetFunctionSelectors[_facetAddress]
            .functionSelectors
            .length - 1;
        // if not the same then replace _selector with lastSelector
        if (selectorPosition != lastSelectorPosition) {
            bytes4 lastSelector = ds
                .facetFunctionSelectors[_facetAddress]
                .functionSelectors[lastSelectorPosition];
            ds.facetFunctionSelectors[_facetAddress].functionSelectors[
                    selectorPosition
                ] = lastSelector;
            ds
                .selectorToFacetAndPosition[lastSelector]
                .functionSelectorPosition = uint16(selectorPosition);
        }
        // delete the last selector
        ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
        delete ds.selectorToFacetAndPosition[_selector];

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

    function initializeDiamondCut(address _init, bytes memory _calldata)
        internal
    {
        if (_init == address(0)) {
            require(
                _calldata.length == 0,
                "LibDiamondCut: _init is address(0) but_calldata is not empty"
            );
        } else {
            require(
                _calldata.length > 0,
                "LibDiamondCut: _calldata is empty but _init is not address(0)"
            );
            if (_init != address(this)) {
                enforceHasContractCode(
                    _init,
                    "LibDiamondCut: _init address has no code"
                );
            }
            (bool success, bytes memory error) = _init.delegatecall(_calldata);
            if (!success) {
                if (error.length > 0) {
                    // bubble up the error
                    GelatoBytes.revertWithError(error, "LibDiamondCut:_init:");
                } else {
                    revert("LibDiamondCut: _init function reverted");
                }
            }
        }
    }

    function enforceHasContractCode(
        address _contract,
        string memory _errorMessage
    ) internal view {
        uint256 contractSize;
        assembly {
            contractSize := extcodesize(_contract)
        }
        require(contractSize > 0, _errorMessage);
    }
}

File 13 of 21 : FGelato.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;

import {
    IChainlinkOracle
} from "../../interfaces/chainlink/IChainlinkOracle.sol";
import {ETH} from "../../constants/CTokens.sol";
import {IOracleAggregator} from "../../interfaces/gelato/IOracleAggregator.sol";
import {IGelato} from "../../diamond/interfaces/IGelato.sol";

function _getGelatoGasPrice(address _gasPriceOracle) view returns (uint256) {
    return uint256(IChainlinkOracle(_gasPriceOracle).latestAnswer());
}

function _getGelatoFee(
    uint256 _gasOverhead,
    uint256 _gasStart,
    address _payToken,
    address _gelato
) view returns (uint256 gelatoFee) {
    gelatoFee =
        (_gasStart - gasleft() + _gasOverhead) *
        _getCappedGasPrice(IGelato(_gelato).getGasPriceOracle());

    if (_payToken == ETH) return gelatoFee;

    // returns purely the ethereum tx fee
    (gelatoFee, ) = IOracleAggregator(IGelato(_gelato).getOracleAggregator())
        .getExpectedReturnAmount(gelatoFee, ETH, _payToken);
}

function _getCappedGasPrice(address _gasPriceOracle) view returns (uint256) {
    uint256 oracleGasPrice = _getGelatoGasPrice(_gasPriceOracle);

    // Use tx.gasprice capped by 1.3x Chainlink Oracle
    return
        tx.gasprice <= ((oracleGasPrice * 130) / 100)
            ? tx.gasprice
            : ((oracleGasPrice * 130) / 100);
}

File 14 of 21 : IChainlinkOracle.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;

interface IChainlinkOracle {
    function latestAnswer() external view returns (int256);

    function decimals() external view returns (uint256);
}

File 15 of 21 : IGelatoV1.sol
// SPDX-License-Identifier: MIT
// solhint-disable
pragma solidity >=0.6.10;
pragma experimental ABIEncoderV2;

import {
    Action,
    Provider,
    Task,
    DataFlow,
    TaskReceipt
} from "@gelatonetwork/core/contracts/gelato_core/interfaces/IGelatoCore.sol";

// TaskSpec - Will be whitelised by providers and selected by users
struct TaskSpec {
    IGelatoCondition[] conditions; // Address: optional AddressZero for self-conditional actions
    Action[] actions;
    uint256 gasPriceCeil;
}

interface IGelatoV1 {
    /// @notice API to query whether Task can be submitted successfully.
    /// @dev In submitTask the msg.sender must be the same as _userProxy here.
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _userProxy The userProxy from which the task will be submitted.
    /// @param _task Selected provider, conditions, actions, expiry date of the task
    function canSubmitTask(
        address _userProxy,
        Provider calldata _provider,
        Task calldata _task,
        uint256 _expiryDate
    ) external view returns (string memory);

    /// @notice API to submit a single Task.
    /// @dev You can let users submit multiple tasks at once by batching calls to this.
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _task A Gelato Task object: provider, conditions, actions.
    /// @param _expiryDate From then on the task cannot be executed. 0 for infinity.
    function submitTask(
        Provider calldata _provider,
        Task calldata _task,
        uint256 _expiryDate
    ) external;

    /// @notice A Gelato Task Cycle consists of 1 or more Tasks that automatically submit
    ///  the next one, after they have been executed.
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _tasks This can be a single task or a sequence of tasks.
    /// @param _expiryDate  After this no task of the sequence can be executed any more.
    /// @param _cycles How many full cycles will be submitted
    function submitTaskCycle(
        Provider calldata _provider,
        Task[] calldata _tasks,
        uint256 _expiryDate,
        uint256 _cycles
    ) external;

    /// @notice A Gelato Task Cycle consists of 1 or more Tasks that automatically submit
    ///  the next one, after they have been executed.
    /// @dev CAUTION: _sumOfRequestedTaskSubmits does not mean the number of cycles.
    /// @dev If _sumOfRequestedTaskSubmits = 1 && _tasks.length = 2, only the first task
    ///  would be submitted, but not the second
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _tasks This can be a single task or a sequence of tasks.
    /// @param _expiryDate  After this no task of the sequence can be executed any more.
    /// @param _sumOfRequestedTaskSubmits The TOTAL number of Task auto-submits
    ///  that should have occured once the cycle is complete:
    ///  _sumOfRequestedTaskSubmits = 0 => One Task will resubmit the next Task infinitly
    ///  _sumOfRequestedTaskSubmits = 1 => One Task will resubmit no other task
    ///  _sumOfRequestedTaskSubmits = 2 => One Task will resubmit 1 other task
    ///  ...
    function submitTaskChain(
        Provider calldata _provider,
        Task[] calldata _tasks,
        uint256 _expiryDate,
        uint256 _sumOfRequestedTaskSubmits
    ) external;

    // ================  Exec Suite =========================
    /// @notice Off-chain API for executors to check, if a TaskReceipt is executable
    /// @dev GelatoCore checks this during execution, in order to safeguard the Conditions
    /// @param _TR TaskReceipt, consisting of user task, user proxy address and id
    /// @param _gasLimit Task.selfProviderGasLimit is used for SelfProviders. All other
    ///  Providers must use gelatoMaxGas. If the _gasLimit is used by an Executor and the
    ///  tx reverts, a refund is paid by the Provider and the TaskReceipt is annulated.
    /// @param _execTxGasPrice Must be used by Executors. Gas Price fed by gelatoCore's
    ///  Gas Price Oracle. Executors can query the current gelatoGasPrice from events.
    function canExec(
        TaskReceipt calldata _TR,
        uint256 _gasLimit,
        uint256 _execTxGasPrice
    ) external view returns (string memory);

    /// @notice Executors call this when Conditions allow it to execute submitted Tasks.
    /// @dev Executors get rewarded for successful Execution. The Task remains open until
    ///   successfully executed, or when the execution failed, despite of gelatoMaxGas usage.
    ///   In the latter case Executors are refunded by the Task Provider.
    /// @param _TR TaskReceipt: id, userProxy, Task.
    function exec(TaskReceipt calldata _TR) external;

    /// @notice Cancel task
    /// @dev Callable only by userProxy or selected provider
    /// @param _TR TaskReceipt: id, userProxy, Task.
    function cancelTask(TaskReceipt calldata _TR) external;

    /// @notice Cancel multiple tasks at once
    /// @dev Callable only by userProxy or selected provider
    /// @param _taskReceipts TaskReceipts: id, userProxy, Task.
    function multiCancelTasks(TaskReceipt[] calldata _taskReceipts) external;

    /// @notice Compute hash of task receipt
    /// @param _TR TaskReceipt, consisting of user task, user proxy address and id
    /// @return hash of taskReceipt
    function hashTaskReceipt(TaskReceipt calldata _TR)
        external
        pure
        returns (bytes32);

    // ================  Getters =========================
    /// @notice Returns the taskReceiptId of the last TaskReceipt submitted
    /// @return currentId currentId, last TaskReceiptId submitted
    function currentTaskReceiptId() external view returns (uint256);

    /// @notice Returns computed taskReceipt hash, used to check for taskReceipt validity
    /// @param _taskReceiptId Id of taskReceipt emitted in submission event
    /// @return hash of taskReceipt
    function taskReceiptHash(uint256 _taskReceiptId)
        external
        view
        returns (bytes32);

    /// @notice Stake on Gelato to become a whitelisted executor
    /// @dev Msg.value has to be >= minExecutorStake
    function stakeExecutor() external payable;

    /// @notice Unstake on Gelato to become de-whitelisted and withdraw minExecutorStake
    function unstakeExecutor() external;

    /// @notice Re-assigns multiple providers to other executors
    /// @dev Executors must re-assign all providers before being able to unstake
    /// @param _providers List of providers to re-assign
    /// @param _newExecutor Address of new executor to assign providers to
    function multiReassignProviders(
        address[] calldata _providers,
        address _newExecutor
    ) external;

    /// @notice Withdraw excess Execur Stake
    /// @dev Can only be called if executor is isExecutorMinStaked
    /// @param _withdrawAmount Amount to withdraw
    /// @return Amount that was actually withdrawn
    function withdrawExcessExecutorStake(uint256 _withdrawAmount)
        external
        returns (uint256);

    // =========== GELATO PROVIDER APIs ==============

    /// @notice Validation that checks whether Task Spec is being offered by the selected provider
    /// @dev Checked in submitTask(), unless provider == userProxy
    /// @param _provider Address of selected provider
    /// @param _taskSpec Task Spec
    /// @return Expected to return "OK"
    function isTaskSpecProvided(address _provider, TaskSpec calldata _taskSpec)
        external
        view
        returns (string memory);

    /// @notice Validates that provider has provider module whitelisted + conducts isProvided check in ProviderModule
    /// @dev Checked in submitTask() if provider == userProxy
    /// @param _userProxy userProxy passed by GelatoCore during submission and exec
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _task Task defined in IGelatoCore
    /// @return Expected to return "OK"
    function providerModuleChecks(
        address _userProxy,
        Provider calldata _provider,
        Task calldata _task
    ) external view returns (string memory);

    /// @notice Validate if provider module and seleced TaskSpec is whitelisted by provider
    /// @dev Combines "isTaskSpecProvided" and providerModuleChecks
    /// @param _userProxy userProxy passed by GelatoCore during submission and exec
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _task Task defined in IGelatoCore
    /// @return res Expected to return "OK"
    function isTaskProvided(
        address _userProxy,
        Provider calldata _provider,
        Task calldata _task
    ) external view returns (string memory res);

    /// @notice Validate if selected TaskSpec is whitelisted by provider and that current gelatoGasPrice is below GasPriceCeil
    /// @dev If gasPriceCeil is != 0, Task Spec is whitelisted
    /// @param _userProxy userProxy passed by GelatoCore during submission and exec
    /// @param _provider Gelato Provider object: provider address and module.
    /// @param _task Task defined in IGelatoCore
    /// @param _gelatoGasPrice Task Receipt defined in IGelatoCore
    /// @return res Expected to return "OK"
    function providerCanExec(
        address _userProxy,
        Provider calldata _provider,
        Task calldata _task,
        uint256 _gelatoGasPrice
    ) external view returns (string memory res);

    // =========== PROVIDER STATE WRITE APIs ==============
    // Provider Funding
    /// @notice Deposit ETH as provider on Gelato
    /// @param _provider Address of provider who receives ETH deposit
    function provideFunds(address _provider) external payable;

    /// @notice Withdraw provider funds from gelato
    /// @param _withdrawAmount Amount
    /// @return amount that will be withdrawn
    function unprovideFunds(uint256 _withdrawAmount) external returns (uint256);

    /// @notice Assign executor as provider
    /// @param _executor Address of new executor
    function providerAssignsExecutor(address _executor) external;

    /// @notice Assign executor as previous selected executor
    /// @param _provider Address of provider whose executor to change
    /// @param _newExecutor Address of new executor
    function executorAssignsExecutor(address _provider, address _newExecutor)
        external;

    // (Un-)provide Task Spec

    /// @notice Whitelist TaskSpecs (A combination of a Condition, Action(s) and a gasPriceCeil) that users can select from
    /// @dev If gasPriceCeil is == 0, Task Spec will be executed at any gas price (no ceil)
    /// @param _taskSpecs Task Receipt List defined in IGelatoCore
    function provideTaskSpecs(TaskSpec[] calldata _taskSpecs) external;

    /// @notice De-whitelist TaskSpecs (A combination of a Condition, Action(s) and a gasPriceCeil) that users can select from
    /// @dev If gasPriceCeil was set to NO_CEIL, Input NO_CEIL constant as GasPriceCeil
    /// @param _taskSpecs Task Receipt List defined in IGelatoCore
    function unprovideTaskSpecs(TaskSpec[] calldata _taskSpecs) external;

    /// @notice Update gasPriceCeil of selected Task Spec
    /// @param _taskSpecHash Result of hashTaskSpec()
    /// @param _gasPriceCeil New gas price ceil for Task Spec
    function setTaskSpecGasPriceCeil(
        bytes32 _taskSpecHash,
        uint256 _gasPriceCeil
    ) external;

    // Provider Module
    /// @notice Whitelist new provider Module(s)
    /// @param _modules Addresses of the modules which will be called during providerModuleChecks()
    function addProviderModules(IGelatoProviderModule[] calldata _modules)
        external;

    /// @notice De-Whitelist new provider Module(s)
    /// @param _modules Addresses of the modules which will be removed
    function removeProviderModules(IGelatoProviderModule[] calldata _modules)
        external;

    // Batch (un-)provide

    /// @notice Whitelist new executor, TaskSpec(s) and Module(s) in one tx
    /// @param _executor Address of new executor of provider
    /// @param _taskSpecs List of Task Spec which will be whitelisted by provider
    /// @param _modules List of module addresses which will be whitelisted by provider
    function multiProvide(
        address _executor,
        TaskSpec[] calldata _taskSpecs,
        IGelatoProviderModule[] calldata _modules
    ) external payable;

    /// @notice De-Whitelist TaskSpec(s), Module(s) and withdraw funds from gelato in one tx
    /// @param _withdrawAmount Amount to withdraw from ProviderFunds
    /// @param _taskSpecs List of Task Spec which will be de-whitelisted by provider
    /// @param _modules List of module addresses which will be de-whitelisted by provider
    function multiUnprovide(
        uint256 _withdrawAmount,
        TaskSpec[] calldata _taskSpecs,
        IGelatoProviderModule[] calldata _modules
    ) external;

    // =========== PROVIDER STATE READ APIs ==============
    // Provider Funding

    /// @notice Get balance of provider
    /// @param _provider Address of provider
    /// @return Provider Balance
    function providerFunds(address _provider) external view returns (uint256);

    /// @notice Get min stake required by all providers for executors to call exec
    /// @param _gelatoMaxGas Current gelatoMaxGas
    /// @param _gelatoGasPrice Current gelatoGasPrice
    /// @return How much provider balance is required for executor to submit exec tx
    function minExecProviderFunds(
        uint256 _gelatoMaxGas,
        uint256 _gelatoGasPrice
    ) external view returns (uint256);

    /// @notice Check if provider has sufficient funds for executor to call exec
    /// @param _provider Address of provider
    /// @param _gelatoMaxGas Currentt gelatoMaxGas
    /// @param _gelatoGasPrice Current gelatoGasPrice
    /// @return Whether provider is liquid (true) or not (false)
    function isProviderLiquid(
        address _provider,
        uint256 _gelatoMaxGas,
        uint256 _gelatoGasPrice
    ) external view returns (bool);

    // Executor Stake

    /// @notice Get balance of executor
    /// @param _executor Address of executor
    /// @return Executor Balance
    function executorStake(address _executor) external view returns (uint256);

    /// @notice Check if executor has sufficient stake on gelato
    /// @param _executor Address of provider
    /// @return Whether executor has sufficient stake (true) or not (false)
    function isExecutorMinStaked(address _executor)
        external
        view
        returns (bool);

    /// @notice Get executor of provider
    /// @param _provider Address of provider
    /// @return Provider's executor
    function executorByProvider(address _provider)
        external
        view
        returns (address);

    /// @notice Get num. of providers which haved assigned an executor
    /// @param _executor Address of executor
    /// @return Count of how many providers assigned the executor
    function executorProvidersCount(address _executor)
        external
        view
        returns (uint256);

    /// @notice Check if executor has one or more providers assigned
    /// @param _executor Address of provider
    /// @return Where 1 or more providers have assigned the executor
    function isExecutorAssigned(address _executor) external view returns (bool);

    // Task Spec and Gas Price Ceil
    /// @notice The maximum gas price the transaction will be executed with
    /// @param _provider Address of provider
    /// @param _taskSpecHash Hash of provider TaskSpec
    /// @return Max gas price an executor will execute the transaction with in wei
    function taskSpecGasPriceCeil(address _provider, bytes32 _taskSpecHash)
        external
        view
        returns (uint256);

    /// @notice Returns the hash of the formatted TaskSpec.
    /// @dev The action.data field of each Action is stripped before hashing.
    /// @param _taskSpec TaskSpec
    /// @return keccak256 hash of encoded condition address and Action List
    function hashTaskSpec(TaskSpec calldata _taskSpec)
        external
        view
        returns (bytes32);

    /// @notice Constant used to specify the highest gas price available in the gelato system
    /// @dev Input 0 as gasPriceCeil and it will be assigned to NO_CEIL
    /// @return MAX_UINT
    function NO_CEIL() external pure returns (uint256);

    // Providers' Module Getters

    /// @notice Check if inputted module is whitelisted by provider
    /// @param _provider Address of provider
    /// @param _module Address of module
    /// @return true if it is whitelisted
    function isModuleProvided(address _provider, IGelatoProviderModule _module)
        external
        view
        returns (bool);

    /// @notice Get all whitelisted provider modules from a given provider
    /// @param _provider Address of provider
    /// @return List of whitelisted provider modules
    function providerModules(address _provider)
        external
        view
        returns (IGelatoProviderModule[] memory);

    // State Writing

    /// @notice Assign new gas price oracle
    /// @dev Only callable by sysAdmin
    /// @param _newOracle Address of new oracle
    function setGelatoGasPriceOracle(address _newOracle) external;

    /// @notice Assign new gas price oracle
    /// @dev Only callable by sysAdmin
    /// @param _requestData The encoded payload for the staticcall to the oracle.
    function setOracleRequestData(bytes calldata _requestData) external;

    /// @notice Assign new maximum gas limit providers can consume in executionWrapper()
    /// @dev Only callable by sysAdmin
    /// @param _newMaxGas New maximum gas limit
    function setGelatoMaxGas(uint256 _newMaxGas) external;

    /// @notice Assign new interal gas limit requirement for exec()
    /// @dev Only callable by sysAdmin
    /// @param _newRequirement New internal gas requirement
    function setInternalGasRequirement(uint256 _newRequirement) external;

    /// @notice Assign new minimum executor stake
    /// @dev Only callable by sysAdmin
    /// @param _newMin New minimum executor stake
    function setMinExecutorStake(uint256 _newMin) external;

    /// @notice Assign new success share for executors to receive after successful execution
    /// @dev Only callable by sysAdmin
    /// @param _percentage New % success share of total gas consumed
    function setExecutorSuccessShare(uint256 _percentage) external;

    /// @notice Assign new success share for sysAdmin to receive after successful execution
    /// @dev Only callable by sysAdmin
    /// @param _percentage New % success share of total gas consumed
    function setSysAdminSuccessShare(uint256 _percentage) external;

    /// @notice Withdraw sysAdmin funds
    /// @dev Only callable by sysAdmin
    /// @param _amount Amount to withdraw
    /// @param _to Address to receive the funds
    function withdrawSysAdminFunds(uint256 _amount, address payable _to)
        external
        returns (uint256);

    // State Reading
    /// @notice Unaccounted tx overhead that will be refunded to executors
    function EXEC_TX_OVERHEAD() external pure returns (uint256);

    /// @notice Addess of current Gelato Gas Price Oracle
    function gelatoGasPriceOracle() external view returns (address);

    /// @notice Getter for oracleRequestData state variable
    function oracleRequestData() external view returns (bytes memory);

    /// @notice Gas limit an executor has to submit to get refunded even if actions revert
    function gelatoMaxGas() external view returns (uint256);

    /// @notice Internal gas limit requirements ti ensure executor payout
    function internalGasRequirement() external view returns (uint256);

    /// @notice Minimum stake required from executors
    function minExecutorStake() external view returns (uint256);

    /// @notice % Fee executors get as a reward for a successful execution
    function executorSuccessShare() external view returns (uint256);

    /// @notice Total % Fee executors and sysAdmin collectively get as a reward for a successful execution
    /// @dev Saves a state read
    function totalSuccessShare() external view returns (uint256);

    /// @notice Get total fee providers pay executors for a successful execution
    /// @param _gas Gas consumed by transaction
    /// @param _gasPrice Current gelato gas price
    function executorSuccessFee(uint256 _gas, uint256 _gasPrice)
        external
        view
        returns (uint256);

    /// @notice % Fee sysAdmin gets as a reward for a successful execution
    function sysAdminSuccessShare() external view returns (uint256);

    /// @notice Get total fee providers pay sysAdmin for a successful execution
    /// @param _gas Gas consumed by transaction
    /// @param _gasPrice Current gelato gas price
    function sysAdminSuccessFee(uint256 _gas, uint256 _gasPrice)
        external
        view
        returns (uint256);

    /// @notice Get sysAdminds funds
    function sysAdminFunds() external view returns (uint256);
}

/// @title IGelatoCondition - solidity interface of GelatoConditionsStandard
/// @notice all the APIs of GelatoConditionsStandard
/// @dev all the APIs are implemented inside GelatoConditionsStandard
interface IGelatoCondition {
    /// @notice GelatoCore calls this to verify securely the specified Condition securely
    /// @dev Be careful only to encode a Task's condition.data as is and not with the
    ///  "ok" selector or _taskReceiptId, since those two things are handled by GelatoCore.
    /// @param _taskReceiptId This is passed by GelatoCore so we can rely on it as a secure
    ///  source of Task identification.
    /// @param _conditionData This is the Condition.data field developers must encode their
    ///  Condition's specific parameters in.
    /// @param _cycleId For Tasks that are executed as part of a cycle.
    function ok(
        uint256 _taskReceiptId,
        bytes calldata _conditionData,
        uint256 _cycleId
    ) external view returns (string memory);
}

/// @notice all the APIs and events of GelatoActionsStandard
/// @dev all the APIs are implemented inside GelatoActionsStandard
interface IGelatoAction {
    /// @notice Providers can use this for pre-execution sanity checks, to prevent reverts.
    /// @dev GelatoCore checks this in canExec and passes the parameters.
    /// @param _taskReceiptId The id of the task from which all arguments are passed.
    /// @param _userProxy The userProxy of the task. Often address(this) for delegatecalls.
    /// @param _actionData The encoded payload to be used in the Action.
    /// @param _dataFlow The dataFlow of the Action.
    /// @param _value A special param for ETH sending Actions. If the Action sends ETH
    ///  in its Action function implementation, one should expect msg.value therein to be
    ///  equal to _value. So Providers can check in termsOk that a valid ETH value will
    ///  be used because they also have access to the same value when encoding the
    ///  execPayload on their ProviderModule.
    /// @param _cycleId For tasks that are part of a Cycle.
    /// @return Returns OK, if Task can be executed safely according to the Provider's
    ///  terms laid out in this function implementation.
    function termsOk(
        uint256 _taskReceiptId,
        address _userProxy,
        bytes calldata _actionData,
        DataFlow _dataFlow,
        uint256 _value,
        uint256 _cycleId
    ) external view returns (string memory);
}

interface IGelatoProviderModule {
    /// @notice Check if provider agrees to pay for inputted task receipt
    /// @dev Enables arbitrary checks by provider
    /// @param _userProxy The smart contract account of the user who submitted the Task.
    /// @param _provider The account of the Provider who uses the ProviderModule.
    /// @param _task Gelato Task to be executed.
    /// @return "OK" if provider agrees
    function isProvided(
        address _userProxy,
        address _provider,
        Task calldata _task
    ) external view returns (string memory);

    /// @notice Convert action specific payload into proxy specific payload
    /// @dev Encoded multiple actions into a multisend
    /// @param _taskReceiptId Unique ID of Gelato Task to be executed.
    /// @param _userProxy The smart contract account of the user who submitted the Task.
    /// @param _provider The account of the Provider who uses the ProviderModule.
    /// @param _task Gelato Task to be executed.
    /// @param _cycleId For Tasks that form part of a cycle/chain.
    /// @return Encoded payload that will be used for low-level .call on user proxy
    /// @return checkReturndata if true, fwd returndata from userProxy.call to ProviderModule
    function execPayload(
        uint256 _taskReceiptId,
        address _userProxy,
        address _provider,
        Task calldata _task,
        uint256 _cycleId
    ) external view returns (bytes memory, bool checkReturndata);

    /// @notice Called by GelatoCore.exec to verifiy that no revert happend on userProxy
    /// @dev If a caught revert is detected, this fn should revert with the detected error
    /// @param _proxyReturndata Data from GelatoCore._exec.userProxy.call(execPayload)
    function execRevertCheck(bytes calldata _proxyReturndata) external pure;
}

File 16 of 21 : IOracleAggregator.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

/**
 * @dev Interface of the Oracle Aggregator Contract
 */
interface IOracleAggregator {
    function getExpectedReturnAmount(
        uint256 amount,
        address tokenAddressA,
        address tokenAddressB
    ) external view returns (uint256 returnAmount, uint256 decimals);
}

File 17 of 21 : GelatoBytes.sol
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.8.0;

library GelatoBytes {
    function calldataSliceSelector(bytes calldata _bytes)
        internal
        pure
        returns (bytes4 selector)
    {
        selector =
            _bytes[0] |
            (bytes4(_bytes[1]) >> 8) |
            (bytes4(_bytes[2]) >> 16) |
            (bytes4(_bytes[3]) >> 24);
    }

    function memorySliceSelector(bytes memory _bytes)
        internal
        pure
        returns (bytes4 selector)
    {
        selector =
            _bytes[0] |
            (bytes4(_bytes[1]) >> 8) |
            (bytes4(_bytes[2]) >> 16) |
            (bytes4(_bytes[3]) >> 24);
    }

    function revertWithError(bytes memory _bytes, string memory _tracingInfo)
        internal
        pure
    {
        // 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
        if (_bytes.length % 32 == 4) {
            bytes4 selector;
            assembly {
                selector := mload(add(0x20, _bytes))
            }
            if (selector == 0x08c379a0) {
                // Function selector for Error(string)
                assembly {
                    _bytes := add(_bytes, 68)
                }
                revert(string(abi.encodePacked(_tracingInfo, string(_bytes))));
            } else {
                revert(
                    string(abi.encodePacked(_tracingInfo, "NoErrorSelector"))
                );
            }
        } else {
            revert(
                string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"))
            );
        }
    }

    function returnError(bytes memory _bytes, string memory _tracingInfo)
        internal
        pure
        returns (string memory)
    {
        // 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
        if (_bytes.length % 32 == 4) {
            bytes4 selector;
            assembly {
                selector := mload(add(0x20, _bytes))
            }
            if (selector == 0x08c379a0) {
                // Function selector for Error(string)
                assembly {
                    _bytes := add(_bytes, 68)
                }
                return string(abi.encodePacked(_tracingInfo, string(_bytes)));
            } else {
                return
                    string(abi.encodePacked(_tracingInfo, "NoErrorSelector"));
            }
        } else {
            return
                string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"));
        }
    }
}

File 18 of 21 : GelatoString.sol
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.8.0;

library GelatoString {
    function startsWithOK(string memory _str) internal pure returns (bool) {
        if (
            bytes(_str).length >= 2 &&
            bytes(_str)[0] == "O" &&
            bytes(_str)[1] == "K"
        ) return true;
        return false;
    }

    function revertWithInfo(string memory _error, string memory _tracingInfo)
        internal
        pure
    {
        revert(string(abi.encodePacked(_tracingInfo, _error)));
    }

    function prefix(string memory _second, string memory _first)
        internal
        pure
        returns (string memory)
    {
        return string(abi.encodePacked(_first, _second));
    }

    function suffix(string memory _first, string memory _second)
        internal
        pure
        returns (string memory)
    {
        return string(abi.encodePacked(_first, _second));
    }
}

File 19 of 21 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {

    function decimals() external view returns (uint8 digits);

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

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

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

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

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

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

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

File 20 of 21 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value)
        private
        view
        returns (bool)
    {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index)
        private
        view
        returns (bytes32)
    {
        require(
            set._values.length > index,
            "EnumerableSet: index out of bounds"
        );
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value)
        internal
        returns (bool)
    {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value)
        internal
        returns (bool)
    {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value)
        internal
        view
        returns (bool)
    {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index)
        internal
        view
        returns (bytes32)
    {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value)
        internal
        returns (bool)
    {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value)
        internal
        returns (bool)
    {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value)
        internal
        view
        returns (bool)
    {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index)
        internal
        view
        returns (address)
    {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value)
        internal
        returns (bool)
    {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value)
        internal
        view
        returns (bool)
    {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index)
        internal
        view
        returns (uint256)
    {
        return uint256(_at(set._inner, index));
    }
}

File 21 of 21 : Utils.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.0;

import {ETH} from "../../constants/CTokens.sol";
import {IERC20} from  "../../vendor/openzeppelin/contracts/token/ERC20/IERC20.sol";

function getBalance(address token, address user) view returns (uint256) {
        if (token == ETH) {
            return user.balance;
        } else {
            return IERC20(token).balanceOf(user);
        }
    }

Settings
{
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IGelatoV1","name":"_gelatoV1","type":"address"},{"internalType":"address","name":"_gelatoProvider","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"executor","type":"address"},{"indexed":true,"internalType":"address","name":"service","type":"address"},{"indexed":true,"internalType":"bool","name":"wasExecutorPaid","type":"bool"}],"name":"LogExecSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldGasMargin","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newGasMargin","type":"uint256"}],"name":"LogSetGasMargin","type":"event"},{"inputs":[],"name":"GAS_OVERHEAD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GELATO_PROVIDER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GELATO_V1","outputs":[{"internalType":"contract IGelatoV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_bundleExecutors","type":"address[]"}],"name":"addBundleExecutors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_executors","type":"address[]"}],"name":"addExecutors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bundleExecutors","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_executor","type":"address"}],"name":"canExec","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_service","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"},{"internalType":"address","name":"_creditToken","type":"address"}],"name":"estimateExecGasDebit","outputs":[{"internalType":"uint256","name":"gasDebitInETH","type":"uint256"},{"internalType":"uint256","name":"gasDebitInCreditToken","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_service","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"},{"internalType":"address","name":"_creditToken","type":"address"}],"name":"exec","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"executors","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasMargin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_bundleExecutor","type":"address"}],"name":"isBundleExecutor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_executor","type":"address"}],"name":"isExecutor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfBundleExecutors","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfExecutors","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_bundleExecutors","type":"address[]"}],"name":"removeBundleExecutors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_executors","type":"address[]"}],"name":"removeExecutors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasMargin","type":"uint256"}],"name":"setGasMargin","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60c06040523480156200001157600080fd5b506040516200200f3803806200200f833981016040819052620000349162000053565b6001600160601b0319606092831b8116608052911b1660a052620000aa565b6000806040838503121562000066578182fd5b8251620000738162000091565b6020840151909250620000868162000091565b809150509250929050565b6001600160a01b0381168114620000a757600080fd5b50565b60805160601c60a05160601c611ef36200011c600039600081816104900152818161093e01526109dc01526000818161058901528181610683015281816106d201528181610904015281816109ad01528181610b2c01528181610c2601528181610c750152610f380152611ef36000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c80639f80fd92116100a2578063b9880caa11610071578063b9880caa1461020f578063dc09c3e414610230578063debfda3014610243578063e52e63c514610256578063f56b75fc1461025e57610116565b80639f80fd92146101ce578063a730cdfb146101e1578063afda35cb146101e9578063b87b0b4c146101fc57610116565b80631ecd9cd8116100e95780631ecd9cd814610169578063334328351461017c578063454b5d0514610191578063489a095c146101a65780638830bb87146101ae57610116565b8063143aebca1461011b5780631499e05b14610130578063156a08d91461014e57806317f255b914610161575b600080fd5b61012e6101293660046117e4565b610266565b005b6101386102fb565b6040516101459190611d5d565b60405180910390f35b61012e61015c3660046117e4565b61030a565b6101386103fd565b61012e6101773660046117e4565b610407565b61018461048e565b6040516101459190611953565b6101996104b2565b6040516101459190611967565b6101386104bc565b6101c16101bc366004611735565b6104c6565b60405161014591906119b4565b61012e6101dc36600461186b565b6104e0565b610138610534565b6101c16101f7366004611735565b61053a565b61012e61020a366004611751565b61054e565b61022261021d366004611751565b610aee565b604051610145929190611d85565b61012e61023e3660046117e4565b610e25565b6101c1610251366004611735565b610f18565b610199610f2c565b610184610f36565b61026e610f5a565b60005b818110156102f6576102bf83838381811061029c57634e487b7160e01b600052603260045260246000fd5b90506020020160208101906102b19190611735565b6001600160a01b0316610f8f565b6102e45760405162461bcd60e51b81526004016102db90611b6e565b60405180910390fd5b806102ee81611e4d565b915050610271565b505050565b6000610305610fa6565b905090565b60005b818110156102f65782828281811061033557634e487b7160e01b600052603260045260246000fd5b905060200201602081019061034a9190611735565b6001600160a01b0316336001600160a01b0316148061036d575061036d33610fb8565b6103895760405162461bcd60e51b81526004016102db90611a76565b6103cf8383838181106103ac57634e487b7160e01b600052603260045260246000fd5b90506020020160208101906103c19190611735565b6001600160a01b0316610fdd565b6103eb5760405162461bcd60e51b81526004016102db90611c02565b806103f581611e4d565b91505061030d565b6000610305610ff4565b61040f610f5a565b60005b818110156102f65761046083838381811061043d57634e487b7160e01b600052603260045260246000fd5b90506020020160208101906104529190611735565b6001600160a01b0316611007565b61047c5760405162461bcd60e51b81526004016102db90611b3e565b8061048681611e4d565b915050610412565b7f000000000000000000000000000000000000000000000000000000000000000081565b606061030561101b565b60006103056110df565b60006104da826001600160a01b03166110f4565b92915050565b6104e8610f5a565b7f0fed5e5113af1ae14d55c14860dc4992d82f571b9af77ddf73be8f6fd869c69d6105116103fd565b82604051610520929190611d85565b60405180910390a16105318161110b565b50565b619c4081565b60006104da826001600160a01b031661111b565b60005a905061055c33611122565b8061056b575061056b336110f4565b6105875760405162461bcd60e51b81526004016102db90611cb4565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316856001600160a01b031614156105d95773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee91505b60006105e58330611136565b9050600080876001600160a01b031687876040516106049291906118a6565b6000604051808303816000865af19150503d8060008114610641576040519150601f19603f3d011682016040523d82523d6000602084013e610646565b606091505b5091509150816106815760408051808201909152600f81526e22bc32b1a330b1b2ba1732bc32b19d60891b60208201526106819082906111f1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316886001600160a01b0316141561075d57604051630166f36360e31b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690630b379b18906107099060001990600401611d5d565b602060405180830381600087803b15801561072357600080fd5b505af1158015610737573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061075b9190611853565b505b60006107698630611136565b905083811161078a5760405162461bcd60e51b81526004016102db90611ae1565b60006107968583611e06565b905060006107aa6107a5611285565b6112a1565b619c405a6107b8908a611e06565b6107c29190611dbb565b6107cc9190611de7565b905060006001600160a01b03891673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156107fc57508061089a565b6108046112f1565b6001600160a01b0316633c772b6c8373eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8c6040518463ffffffff1660e01b815260040161084793929190611d66565b604080518083038186803b15801561085e57600080fd5b505afa158015610872573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108969190611883565b5090505b806108b75760405162461bcd60e51b81526004016102db90611d08565b60006108c16103fd565b905060646108cf8284611de7565b6108d99190611dd3565b6108e39083611dbb565b8411156109025760405162461bcd60e51b81526004016102db90611c39565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168d6001600160a01b03161415610a31577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168c8c60449060649261097a93929190611d93565b8101906109879190611735565b6001600160a01b03161415610a31576040516328d0cad760e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906328d0cad7908690610a04907f000000000000000000000000000000000000000000000000000000000000000090600401611953565b6000604051808303818588803b158015610a1d57600080fd5b505af193505050508015610a2f575060015b505b336064610a3e8386611de7565b610a489190611dd3565b610a529085611dbb565b604051610a5e90611950565b60006040518083038185875af1925050503d8060008114610a9b576040519150601f19603f3d011682016040523d82523d6000602084013e610aa0565b606091505b5050604051909750871515906001600160a01b038f169033907f300f6143b52cc51ad5dcf9813931eec0d565503b7bf98df212aa0f90c30aca1b90600090a450505050505050505050505050565b60008060005a9050610aff33611122565b80610b0e5750610b0e336110f4565b610b2a5760405162461bcd60e51b81526004016102db90611cb4565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b03161415610b7c5773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee93505b6000610b888530611136565b9050600080896001600160a01b03168989604051610ba79291906118a6565b6000604051808303816000865af19150503d8060008114610be4576040519150601f19603f3d011682016040523d82523d6000602084013e610be9565b606091505b509150915081610c245760408051808201909152600f81526e22bc32b1a330b1b2ba1732bc32b19d60891b6020820152610c249082906111f1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168a6001600160a01b03161415610d0057604051630166f36360e31b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690630b379b1890610cac9060001990600401611d5d565b602060405180830381600087803b158015610cc657600080fd5b505af1158015610cda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cfe9190611853565b505b6000610d0c8830611136565b90506000610d1a8583611e06565b9050610d276107a5611285565b619c405a610d359089611e06565b610d3f9190611dbb565b610d499190611de7565b97506001600160a01b03891673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415610d7857879650610e16565b610d806112f1565b6001600160a01b0316633c772b6c8973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8c6040518463ffffffff1660e01b8152600401610dc393929190611d66565b604080518083038186803b158015610dda57600080fd5b505afa158015610dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e129190611883565b5096505b50505050505094509492505050565b60005b818110156102f657828282818110610e5057634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610e659190611735565b6001600160a01b0316336001600160a01b03161480610e885750610e8833610fb8565b610ea45760405162461bcd60e51b81526004016102db90611ba5565b610eea838383818110610ec757634e487b7160e01b600052603260045260246000fd5b9050602002016020810190610edc9190611735565b6001600160a01b031661130a565b610f065760405162461bcd60e51b81526004016102db90611c7d565b80610f1081611e4d565b915050610e28565b60006104da826001600160a01b0316611122565b606061030561131e565b7f000000000000000000000000000000000000000000000000000000000000000081565b610f626113dd565b600401546001600160a01b03163314610f8d5760405162461bcd60e51b81526004016102db90611a34565b565b60006104da82610f9d611401565b60030190611425565b6000610305610fb3611401565b61143a565b6000610fc2611445565b6001600160a01b0316826001600160a01b0316149050919050565b60006104da82610feb611401565b60030190611461565b6000610ffe611401565b60020154905090565b60006104da82611015611401565b90611425565b606060006110276110df565b90508067ffffffffffffffff81111561105057634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611079578160200160208202803683370190505b50915060005b818110156110da5761109081611476565b8382815181106110b057634e487b7160e01b600052603260045260246000fd5b6001600160a01b0390921660209283029190910190910152806110d281611e4d565b91505061107f565b505090565b60006103056110ec611401565b60030161143a565b60006104da82611102611401565b6003019061148d565b80611114611401565b6002015550565b60006104da825b60006104da82611130611401565b9061148d565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee141561116e57506001600160a01b038116316104da565b6040516370a0823160e01b81526001600160a01b038416906370a082319061119a908590600401611953565b60206040518083038186803b1580156111b257600080fd5b505afa1580156111c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ea9190611853565b9392505050565b602082516111ff9190611e68565b6004141561127457602082015162461bcd60e51b6001600160e01b03198216141561126357604483019250818360405160200161123d9291906118b6565b60408051601f198184030181529082905262461bcd60e51b82526102db916004016119bf565b8160405160200161123d91906118e5565b8060405160200161123d9190611918565b600061128f6114a2565b600101546001600160a01b0316905090565b6000806112ad836114c6565b905060646112bc826082611de7565b6112c69190611dd3565b3a11156112e95760646112da826082611de7565b6112e49190611dd3565b6111ea565b3a9392505050565b60006112fb6114a2565b546001600160a01b0316905090565b60006104da82611318611401565b90611461565b6060600061132a610fa6565b90508067ffffffffffffffff81111561135357634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561137c578160200160208202803683370190505b50915060005b818110156110da5761139381611539565b8382815181106113b357634e487b7160e01b600052603260045260246000fd5b6001600160a01b0390921660209283029190910190910152806113d581611e4d565b915050611382565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c90565b7f7ad725e6d99a082d357ed78c93550a4ac89ca228cbbe8e92f3140a9c2a3effa590565b60006111ea836001600160a01b03841661154d565b60006104da82611597565b600061144f6113dd565b600401546001600160a01b0316919050565b60006111ea836001600160a01b03841661159b565b60006104da82611484611401565b600301906116b8565b60006111ea836001600160a01b0384166116c4565b7fadbe443bead7f1f673a33db18cf1b434b753911cae379d384aa3bd25cd3075d590565b6000816001600160a01b03166350d25bcd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561150157600080fd5b505afa158015611515573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104da9190611853565b60006104da82611547611401565b906116b8565b600061155983836116c4565b61158f575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556104da565b5060006104da565b5490565b600081815260018301602052604081205480156116ae5760006115bf600183611e06565b85549091506000906115d390600190611e06565b905060008660000182815481106115fa57634e487b7160e01b600052603260045260246000fd5b906000526020600020015490508087600001848154811061162b57634e487b7160e01b600052603260045260246000fd5b600091825260209091200155611642836001611dbb565b6000828152600189016020526040902055865487908061167257634e487b7160e01b600052603160045260246000fd5b600190038181906000526020600020016000905590558660010160008781526020019081526020016000206000905560019450505050506104da565b60009150506104da565b60006111ea83836116dc565b60009081526001919091016020526040902054151590565b815460009082106116ff5760405162461bcd60e51b81526004016102db906119f2565b82600001828154811061172257634e487b7160e01b600052603260045260246000fd5b9060005260206000200154905092915050565b600060208284031215611746578081fd5b81356111ea81611ea8565b60008060008060608587031215611766578283fd5b843561177181611ea8565b9350602085013567ffffffffffffffff8082111561178d578485fd5b818701915087601f8301126117a0578485fd5b8135818111156117ae578586fd5b8860208285010111156117bf578586fd5b60208301955080945050505060408501356117d981611ea8565b939692955090935050565b600080602083850312156117f6578182fd5b823567ffffffffffffffff8082111561180d578384fd5b818501915085601f830112611820578384fd5b81358181111561182e578485fd5b8660208083028501011115611841578485fd5b60209290920196919550909350505050565b600060208284031215611864578081fd5b5051919050565b60006020828403121561187c578081fd5b5035919050565b60008060408385031215611895578182fd5b505080516020909101519092909150565b6000828483379101908152919050565b600083516118c8818460208801611e1d565b8351908301906118dc818360208801611e1d565b01949350505050565b600082516118f7818460208701611e1d565b6e2737a2b93937b929b2b632b1ba37b960891b920191825250600f01919050565b6000825161192a818460208701611e1d565b73556e657870656374656452657475726e6461746160601b920191825250601401919050565b90565b6001600160a01b0391909116815260200190565b6020808252825182820181905260009190848201906040850190845b818110156119a85783516001600160a01b031683529284019291840191600101611983565b50909695505050505050565b901515815260200190565b60006020825282518060208401526119de816040850160208701611e1d565b601f01601f19169190910160400192915050565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b60208082526022908201527f4c69624469616d6f6e643a204d75737420626520636f6e7472616374206f776e60408201526132b960f11b606082015260800190565b60208082526045908201527f4578656346616365742e72656d6f766542756e646c654578656375746f72733a60408201527f206d73672e73656e64657220212062756e646c654578656375746f72207c7c2060608201526437bbb732b960d91b608082015260a00190565b6020808252603e908201527f4578656346616365742e657865633a20706f7374437265646974546f6b656e4260408201527f616c616e6365203c20707265437265646974546f6b656e42616c616e63650000606082015260800190565b6020808252601690820152754578656346616365742e6164644578656375746f727360501b604082015260600190565b6020808252601c908201527f4578656346616365742e61646442756e646c654578656375746f727300000000604082015260600190565b60208082526039908201527f4578656346616365742e72656d6f76654578656375746f72733a206d73672e7360408201527f656e6465722021206578656375746f72207c7c206f776e657200000000000000606082015260800190565b6020808252601f908201527f4578656346616365742e72656d6f766542756e646c654578656375746f727300604082015260600190565b60208082526024908201527f4578656346616365742e657865633a204578656375746f72204f7665726368616040820152631c99d95960e21b606082015260800190565b60208082526019908201527f4578656346616365742e72656d6f76654578656375746f727300000000000000604082015260600190565b60208082526034908201527f4578656346616365742e657865633a206f6e6c794578656375746f7273207c7c604082015273206f6e6c7942756e646c654578656375746f727360601b606082015260800190565b60208082526035908201527f4578656346616365742e657865633a20205f637265646974546f6b656e206e6f6040820152743a1037b71027b930b1b632a0b3b3b932b3b0ba37b960591b606082015260800190565b90815260200190565b9283526001600160a01b03918216602084015216604082015260600190565b918252602082015260400190565b60008085851115611da2578182fd5b83861115611dae578182fd5b5050820193919092039150565b60008219821115611dce57611dce611e7c565b500190565b600082611de257611de2611e92565b500490565b6000816000190483118215151615611e0157611e01611e7c565b500290565b600082821015611e1857611e18611e7c565b500390565b60005b83811015611e38578181015183820152602001611e20565b83811115611e47576000848401525b50505050565b6000600019821415611e6157611e61611e7c565b5060010190565b600082611e7757611e77611e92565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6001600160a01b038116811461053157600080fdfea2646970667358221220b3d16c877665985fe9a1001c68ead3139cf8ffb1bc14bed23fde3d90ec7350e064736f6c63430008000033000000000000000000000000025030bdaa159f281cae63873e68313a703725a50000000000000000000000003d9a46b5d421bb097ac28b4f70a4a1441a12920c

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

000000000000000000000000025030bdaa159f281cae63873e68313a703725a50000000000000000000000003d9a46b5d421bb097ac28b4f70a4a1441a12920c

-----Decoded View---------------
Arg [0] : _gelatoV1 (address): 0x025030BdAa159f281cAe63873E68313a703725A5
Arg [1] : _gelatoProvider (address): 0x3d9A46b5D421bb097AC28B4f70a4A1441A12920C

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000025030bdaa159f281cae63873e68313a703725a5
Arg [1] : 0000000000000000000000003d9a46b5d421bb097ac28b4f70a4a1441a12920c


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

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