creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@chainlink/contracts/src/v0.8/automation/AutomationBase.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract AutomationBase {\n  error OnlySimulatedBackend();\n\n  /**\n   * @notice method that allows it to be simulated via eth_call by checking that\n   * the sender is the zero address.\n   */\n  function preventExecution() internal view {\n    if (tx.origin != address(0)) {\n      revert OnlySimulatedBackend();\n    }\n  }\n\n  /**\n   * @notice modifier that allows it to be simulated via eth_call by checking\n   * that the sender is the zero address.\n   */\n  modifier cannotExecute() {\n    preventExecution();\n    _;\n  }\n}\n"
      },
      "@chainlink/contracts/src/v0.8/automation/AutomationCompatible.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport \"./AutomationBase.sol\";\nimport \"./interfaces/AutomationCompatibleInterface.sol\";\n\nabstract contract AutomationCompatible is AutomationBase, AutomationCompatibleInterface {}\n"
      },
      "@chainlink/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface AutomationCompatibleInterface {\n  /**\n   * @notice method that is simulated by the keepers to see if any work actually\n   * needs to be performed. This method does does not actually need to be\n   * executable, and since it is only ever simulated it can consume lots of gas.\n   * @dev To ensure that it is never called, you may want to add the\n   * cannotExecute modifier from KeeperBase to your implementation of this\n   * method.\n   * @param checkData specified in the upkeep registration so it is always the\n   * same for a registered upkeep. This can easily be broken down into specific\n   * arguments using `abi.decode`, so multiple upkeeps can be registered on the\n   * same contract and easily differentiated by the contract.\n   * @return upkeepNeeded boolean to indicate whether the keeper should call\n   * performUpkeep or not.\n   * @return performData bytes that the keeper should call performUpkeep with, if\n   * upkeep is needed. If you would like to encode data to decode later, try\n   * `abi.encode`.\n   */\n  function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData);\n\n  /**\n   * @notice method that is actually executed by the keepers, via the registry.\n   * The data returned by the checkUpkeep simulation will be passed into\n   * this method to actually be executed.\n   * @dev The input to this method should not be trusted, and the caller of the\n   * method should not even be restricted to any single registry. Anyone should\n   * be able call it, and the input should be validated, there is no guarantee\n   * that the data passed in is the performData returned from checkUpkeep. This\n   * could happen due to malicious keepers, racing keepers, or simply a state\n   * change while the performUpkeep transaction is waiting for confirmation.\n   * Always validate the data passed in.\n   * @param performData is the data which was passed back from the checkData\n   * simulation. If it is encoded, it can easily be decoded into other types by\n   * calling `abi.decode`. This data should not be trusted, and should be\n   * validated against the contract's current state.\n   */\n  function performUpkeep(bytes calldata performData) external;\n}\n"
      },
      "contracts/interfaces/IAutomatedFunctionsConsumer.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.10;\n\ninterface IAutomatedFunctionsConsumer {\n    function s_lastResponse() external view returns (bytes memory);\n    function lastUpkeepTimeStamp() external view returns (uint256);\n}\n"
      },
      "contracts/oracles/HlOracle.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.10;\n\nimport \"@chainlink/contracts/src/v0.8/automation/AutomationCompatible.sol\";\nimport \"../interfaces/IAutomatedFunctionsConsumer.sol\";\n\ncontract HlOracle is AutomationCompatibleInterface {\n    mapping(uint256 => uint256[2]) public settlePrices;\n    IAutomatedFunctionsConsumer internal immutable AUTOMATED_FUNCTIONS_CONSUMER;\n    uint256 public latestExpiryUpdated = 0;\n\n    event Settled(uint256 expiry, uint256[2] settlePrices);\n\n    modifier oracleAvailable() {\n        // latestUpdateOracleTime >= 8:00 UTC\n        uint256 latestUpdateOracleTime = AUTOMATED_FUNCTIONS_CONSUMER.lastUpkeepTimeStamp();\n        require(latestUpdateOracleTime >= block.timestamp - block.timestamp % 86400 + 28800, \"Oracle: not updated\");\n        _;\n    }\n\n    constructor(\n        IAutomatedFunctionsConsumer automatedFunctionsConsumer_\n    ) {\n        AUTOMATED_FUNCTIONS_CONSUMER = automatedFunctionsConsumer_;\n    }\n\n    function checkUpkeep(\n        bytes calldata /* checkData */\n    )\n        external\n        view\n        override\n        oracleAvailable\n        returns (bool upkeepNeeded, bytes memory /* performData */)\n    {\n        uint256 expiry = block.timestamp - block.timestamp % 86400 + 28800;\n        upkeepNeeded = settlePrices[expiry][1] == 0;\n        // We don't use the checkData in this example. The checkData is defined when the Upkeep was registered.\n    }\n\n    function performUpkeep(bytes calldata /* performData */) external override {\n        //We highly recommend revalidating the upkeep in the performUpkeep function\n        settle();\n    }\n\n    // settle price\n    function settle() public oracleAvailable {\n        uint256 expiry = block.timestamp - block.timestamp % 86400 + 28800;\n        require(settlePrices[expiry][1] == 0, \"Oracle: already settled\");\n\n        uint256[2] memory currentPrices = getLatestPrice();\n        require(currentPrices[0] < currentPrices[1], \"Oracle: invalid price\");\n\n        if (latestExpiryUpdated != 0 && latestExpiryUpdated <= expiry - 86400 * 2) {\n            uint256 missedDays = (expiry - latestExpiryUpdated) / 86400;\n            uint256[2] memory startPrices = settlePrices[latestExpiryUpdated];\n\n            for (uint256 i = 1; i < missedDays; i++) {\n                uint256 missedExpiry = latestExpiryUpdated + i * 86400;\n                if (startPrices[0] > currentPrices[0]) {\n                    settlePrices[missedExpiry][0] = startPrices[0] - (startPrices[0] - currentPrices[0]) * i / missedDays;\n                } else {\n                    settlePrices[missedExpiry][0] = startPrices[0] + (currentPrices[0] - startPrices[0]) * i / missedDays;\n                }\n                if (startPrices[1] > currentPrices[1]) {\n                    settlePrices[missedExpiry][1] = startPrices[1] - (startPrices[1] - currentPrices[1]) * i / missedDays;\n                } else {\n                    settlePrices[missedExpiry][1] = startPrices[1] + (currentPrices[1] - startPrices[1]) * i / missedDays;\n                }\n                emit Settled(missedExpiry, settlePrices[missedExpiry]);\n            }\n        }\n\n        settlePrices[expiry] = currentPrices;\n        latestExpiryUpdated = expiry;\n\n        emit Settled(expiry, settlePrices[expiry]);\n    }\n\n    function getLatestPrice() internal view returns (uint[2] memory prices) {\n        prices = abi.decode(AUTOMATED_FUNCTIONS_CONSUMER.s_lastResponse(), (uint256[2]));\n    }\n\n    function getHlPrices(uint256 term, uint256 expiry) public view returns (uint256[2] memory hlPrices) {\n        // find lowest and highest prices in the term\n        hlPrices = [type(uint256).max, 0];\n        if (term > 0) {\n            for (uint256 i = 0; i < term; i++) {\n                if (settlePrices[expiry][0] > 0 && settlePrices[expiry][0] < hlPrices[0]) {\n                    hlPrices[0] = settlePrices[expiry][0];\n                }\n                if (settlePrices[expiry][1] > hlPrices[1]) {\n                    hlPrices[1] = settlePrices[expiry][1];\n                }\n                expiry = expiry - 86400;\n            }\n        }\n    }\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}