creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/feeds/DolaPriceFeed.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.4;\n\ninterface IChainlinkFeed {\n    function aggregator() external view returns (address aggregator);\n\n    function decimals() external view returns (uint8);\n\n    function latestRoundData()\n        external\n        view\n        returns (\n            uint80 roundId,\n            int256 lpDollarPrice,\n            uint256 startedAt,\n            uint256 updatedAt,\n            uint80 answeredInRound\n        );\n}\n\ninterface IAggregator {\n    function maxAnswer() external view returns (int192);\n\n    function minAnswer() external view returns (int192);\n}\n\ninterface ICurvePool {\n    function price_oracle(uint256 k) external view returns (uint256);\n\n    function get_virtual_price() external view returns (uint256);\n\n    function ema_price() external view returns (uint256);\n}\n\ncontract DolaPriceFeed {\n    error OnlyGov();\n\n    ICurvePool public constant pyUSDFrax =\n        ICurvePool(0xA5588F7cdf560811710A2D82D3C9c99769DB1Dcb);\n\n    ICurvePool public constant crvDOLA =\n        ICurvePool(0xef484de8C07B6e2d732A92B5F78e81B38f99f95E);\n\n    IChainlinkFeed public constant pyUsdToUsd =\n        IChainlinkFeed(0x8f1dF6D7F2db73eECE86a18b4381F4707b918FB1);\n\n    IChainlinkFeed public constant fraxToUsd =\n        IChainlinkFeed(0xB9E1E3A9feFf48998E45Fa90847ed4D467E8BcfD);\n\n    uint256 public fraxHeartbeat = 1 hours;\n\n    uint256 public pyUSDHeartbeat = 24 hours;\n\n    address public gov = 0x926dF14a23BE491164dCF93f4c468A50ef659D5B;\n\n    modifier onlyGov() {\n        if (msg.sender != gov) revert OnlyGov();\n        _;\n    }\n\n    /**\n     * @return roundId The round ID of the Chainlink price feed\n     * @return dolaUsdprice The latest price for DOLA\n     * @return startedAt The timestamp when the latest round of Chainlink price feed started\n     * @return updatedAt The timestamp when the latest round of Chainlink price feed was updated\n     * @return answeredInRound The round ID in which the answer was computed\n     */\n    function latestRoundData()\n        public\n        view\n        returns (uint80, int256, uint256, uint256, uint80)\n    {\n        (\n            uint80 roundId,\n            int256 pyUsdPrice,\n            uint startedAt,\n            uint updatedAt,\n            uint80 answeredInRound\n        ) = pyUsdToUsd.latestRoundData();\n\n        if (\n            isPriceOutOfBounds(pyUsdPrice, pyUsdToUsd) ||\n            block.timestamp - updatedAt > pyUSDHeartbeat\n        ) {\n            updatedAt = 0;\n        }\n\n        (\n            uint80 roundIdFrax,\n            int256 fraxUsdPrice,\n            uint startedAtFrax,\n            uint updatedAtFrax,\n            uint80 answeredInRoundFrax\n        ) = fraxToUsd.latestRoundData();\n\n        if (\n            isPriceOutOfBounds(fraxUsdPrice, fraxToUsd) ||\n            block.timestamp - updatedAtFrax > fraxHeartbeat\n        ) {\n            updatedAtFrax = 0;\n        }\n\n        int256 minUsdPrice;\n\n        // If FRAX price is lower or equal than pyUSD price, use FRAX price\n        if (\n            (fraxUsdPrice <= pyUsdPrice && updatedAtFrax > 0) || \n            pyUsdPrice == 0\n        ) {\n            minUsdPrice = fraxUsdPrice;\n            roundId = roundIdFrax;\n            startedAt = startedAtFrax;\n            updatedAt = updatedAtFrax;\n            answeredInRound = answeredInRoundFrax;\n        } else {\n            minUsdPrice = pyUsdPrice;\n        }\n\n        return (\n            roundId,\n            int(\n                (pyUSDFrax.get_virtual_price() *\n                    uint256(minUsdPrice) *\n                    10 ** 18) /\n                    crvDOLA.price_oracle(0) /\n                    10 ** 8\n            ),\n            startedAt,\n            updatedAt,\n            answeredInRound\n        );\n    }\n\n    /** \n    @notice Retrieves the latest price for DOLA\n    @return price The latest price for DOLA\n    */\n    function latestAnswer() external view returns (int256) {\n        (, int256 price, , , ) = latestRoundData();\n        return price;\n    }\n\n    /**\n     * @notice Retrieves number of decimals for the DOLA price feed\n     * @return decimals The number of decimals for the DOLA price feed\n     */\n    function decimals() public pure returns (uint8) {\n        return 18;\n    }\n\n    /**\n     * @notice Checks if a given price is out of the boundaries defined in the Chainlink aggregator.\n     * @param price The price to be checked.\n     * @param feed The Chainlink feed to retrieve the boundary information from.\n     * @return bool Returns `true` if the price is out of bounds, otherwise `false`.\n     */\n    function isPriceOutOfBounds(\n        int price,\n        IChainlinkFeed feed\n    ) public view returns (bool) {\n        IAggregator aggregator = IAggregator(feed.aggregator());\n        int192 max = aggregator.maxAnswer();\n        int192 min = aggregator.minAnswer();\n        return (max <= price || min >= price);\n    }\n\n    /**\n     * @notice Sets a new pyUSD heartbeat\n     * @dev Can only be called by the current gov address\n     * @param newHeartbeat The new pyUSD heartbeat\n     */\n    function setPyUSDHeartbeat(uint256 newHeartbeat) external onlyGov {\n        pyUSDHeartbeat = newHeartbeat;\n    }\n\n    /**\n     * @notice Sets a new FRAX heartbeat\n     * @dev Can only be called by the current gov address\n     * @param newHeartbeat The new FRAX heartbeat\n     */\n    function setFraxHeartbeat(uint256 newHeartbeat) external onlyGov {\n        fraxHeartbeat = newHeartbeat;\n    }\n\n    /**\n     * @notice Sets a new gov address\n     * @dev Can only be called by the current gov address\n     * @param newGov The new gov address\n     */\n    function setGov(address newGov) external onlyGov {\n        gov = newGov;\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
        "forge-std/=lib/forge-std/src/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/",
        "solmate/=lib/solmate/src/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "shanghai",
      "viaIR": false,
      "libraries": {}
    }
  }}