ETH Price: $3,112.59 (-0.10%)

Contract

0x83aE3931C5D03773755311372c0737F856657a43
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Transfer Ownersh...184248362023-10-25 4:19:11391 days ago1698207551IN
Threshold Network : Pricefeed Controller
0 ETH0.0003090810.76122295
Set Addresses184248322023-10-25 4:18:23391 days ago1698207503IN
Threshold Network : Pricefeed Controller
0 ETH0.0015165511.84484242
0x60a06040184246332023-10-25 3:38:23391 days ago1698205103IN
 Contract Creation
0 ETH0.0190685515.29129773

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xd7C0f19C...dBBafc370
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
PriceFeed

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 100 runs

Other Settings:
default evmVersion
File 1 of 8 : PriceFeed.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import "./Interfaces/IPriceFeed.sol";
import "./Interfaces/ITellorCaller.sol";
import "./Dependencies/AggregatorV3Interface.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";
import "./Dependencies/BaseMath.sol";
import "./Dependencies/LiquityMath.sol";

/*
* PriceFeed for mainnet deployment, to be connected to Chainlink's live collateral:USD aggregator reference
* contract, and a wrapper contract TellorCaller, which connects to TellorMaster contract.
*
* The PriceFeed uses Chainlink as primary oracle, and Tellor as fallback. It contains logic for
* switching oracles based on oracle failures, timeouts, and conditions for returning to the primary
* Chainlink oracle.
*/
contract PriceFeed is Ownable, CheckContract, BaseMath, IPriceFeed {

    string constant public NAME = "PriceFeed";

    AggregatorV3Interface public priceAggregator;  // Mainnet Chainlink aggregator
    ITellorCaller public tellorCaller;  // Wrapper contract that calls the Tellor system
    uint256 public immutable tellorDigits;

    // Use to convert a price answer to an 18-digit precision uint
    uint256 constant public TARGET_DIGITS = 18;

    // Maximum time period allowed since Chainlink/Tellor's latest round data timestamp, beyond which Chainlink/Tellor is considered frozen.
    uint256 constant public TIMEOUT = 14400;  // 4 hours: 60 * 60 * 4

    // Maximum deviation allowed between two consecutive Chainlink oracle prices. 18-digit precision.
    uint256 constant public MAX_PRICE_DEVIATION_FROM_PREVIOUS_ROUND =  5e17; // 50%

    /*
    * The maximum relative price difference between two oracle responses allowed in order for the PriceFeed
    * to return to using the Chainlink oracle. 18-digit precision.
    */
    uint256 constant public MAX_PRICE_DIFFERENCE_BETWEEN_ORACLES = 5e16; // 5%

    // The last good price seen from an oracle by Liquity
    uint256 public lastGoodPrice;

    struct ChainlinkResponse {
        uint80 roundId;
        int256 answer;
        uint256 timestamp;
        bool success;
        uint8 decimals;
    }

    struct TellorResponse {
        bool ifRetrieve;
        uint256 value;
        uint256 timestamp;
        bool success;
    }

    enum Status {
        chainlinkWorking,
        usingTellorChainlinkUntrusted,
        bothOraclesUntrusted,
        usingTellorChainlinkFrozen,
        usingChainlinkTellorUntrusted
    }

    // The current status of the PricFeed, which determines the conditions for the next price fetch attempt
    Status public status;

    event PriceFeedStatusChanged(Status newStatus);

    constructor (uint256 _tellorDigits) {
        require(_tellorDigits > 0 && _tellorDigits <= TARGET_DIGITS, "PriceFeed: wrong decimals for Tellor");
        tellorDigits = _tellorDigits;
    }

    // --- Dependency setters ---

    function setAddresses(
        address _priceAggregatorAddress,
        address _tellorCallerAddress
    )
        external
        onlyOwner
    {
        require(address(priceAggregator) == address(0), "PriceFeed: contacts already set");

        checkContract(_priceAggregatorAddress);
        checkContract(_tellorCallerAddress);

        priceAggregator = AggregatorV3Interface(_priceAggregatorAddress);
        tellorCaller = ITellorCaller(_tellorCallerAddress);

        // Explicitly set initial system status
        status = Status.chainlinkWorking;

        // Get an initial price from Chainlink to serve as first reference for lastGoodPrice
        ChainlinkResponse memory chainlinkResponse = _getCurrentChainlinkResponse();
        ChainlinkResponse memory prevChainlinkResponse = _getPrevChainlinkResponse(chainlinkResponse.roundId, chainlinkResponse.decimals);

        require(!_chainlinkIsBroken(chainlinkResponse, prevChainlinkResponse) && !_chainlinkIsFrozen(chainlinkResponse),
            "PriceFeed: Chainlink must be working and current");

        _storeChainlinkPrice(chainlinkResponse);
    }

    // --- Functions ---

    /*
    * fetchPrice():
    * Returns the latest price obtained from the Oracle. Called by Liquity functions that require a current price.
    *
    * Also callable by anyone externally.
    *
    * Non-view function - it stores the last good price seen by Liquity.
    *
    * Uses a main oracle (Chainlink) and a fallback oracle (Tellor) in case Chainlink fails. If both fail,
    * it uses the last good price seen by Liquity.
    *
    */
    function fetchPrice() external override returns (uint) {
        // Get current and previous price data from Chainlink, and current price data from Tellor
        ChainlinkResponse memory chainlinkResponse = _getCurrentChainlinkResponse();
        ChainlinkResponse memory prevChainlinkResponse = _getPrevChainlinkResponse(chainlinkResponse.roundId, chainlinkResponse.decimals);
        TellorResponse memory tellorResponse = _getCurrentTellorResponse();

        // --- CASE 1: System fetched last price from Chainlink  ---
        if (status == Status.chainlinkWorking) {
            // If Chainlink is broken, try Tellor
            if (_chainlinkIsBroken(chainlinkResponse, prevChainlinkResponse)) {
                // If Tellor is broken then both oracles are untrusted, so return the last good price
                if (_tellorIsBroken(tellorResponse)) {
                    _changeStatus(Status.bothOraclesUntrusted);
                    return lastGoodPrice;
                }
                /*
                * If Tellor is only frozen but otherwise returning valid data, return the last good price.
                * Tellor may need to be tipped to return current data.
                */
                if (_tellorIsFrozen(tellorResponse)) {
                    _changeStatus(Status.usingTellorChainlinkUntrusted);
                    return lastGoodPrice;
                }

                // If Chainlink is broken and Tellor is working, switch to Tellor and return current Tellor price
                _changeStatus(Status.usingTellorChainlinkUntrusted);
                return _storeTellorPrice(tellorResponse);
            }

            // If Chainlink is frozen, try Tellor
            if (_chainlinkIsFrozen(chainlinkResponse)) {
                // If Tellor is broken too, remember Tellor broke, and return last good price
                if (_tellorIsBroken(tellorResponse)) {
                    _changeStatus(Status.usingChainlinkTellorUntrusted);
                    return lastGoodPrice;
                }

                // If Tellor is frozen or working, remember Chainlink froze, and switch to Tellor
                _changeStatus(Status.usingTellorChainlinkFrozen);

                if (_tellorIsFrozen(tellorResponse)) {return lastGoodPrice;}

                // If Tellor is working, use it
                return _storeTellorPrice(tellorResponse);
            }

            // If Chainlink price has changed by > 50% between two consecutive rounds, compare it to Tellor's price
            if (_chainlinkPriceChangeAboveMax(chainlinkResponse, prevChainlinkResponse)) {
                // If Tellor is broken, both oracles are untrusted, and return last good price
                 if (_tellorIsBroken(tellorResponse)) {
                    _changeStatus(Status.bothOraclesUntrusted);
                    return lastGoodPrice;
                }

                // If Tellor is frozen, switch to Tellor and return last good price
                if (_tellorIsFrozen(tellorResponse)) {
                    _changeStatus(Status.usingTellorChainlinkUntrusted);
                    return lastGoodPrice;
                }

                /*
                * If Tellor is live and both oracles have a similar price, conclude that Chainlink's large price deviation between
                * two consecutive rounds was likely a legitmate market price movement, and so continue using Chainlink
                */
                if (_bothOraclesSimilarPrice(chainlinkResponse, tellorResponse)) {
                    return _storeChainlinkPrice(chainlinkResponse);
                }

                // If Tellor is live but the oracles differ too much in price, conclude that Chainlink's initial price deviation was
                // an oracle failure. Switch to Tellor, and use Tellor price
                _changeStatus(Status.usingTellorChainlinkUntrusted);
                return _storeTellorPrice(tellorResponse);
            }

            // If Chainlink is working and Tellor is broken, remember Tellor is broken
            if (_tellorIsBroken(tellorResponse)) {
                _changeStatus(Status.usingChainlinkTellorUntrusted);
            }

            // If Chainlink is working, return Chainlink current price (no status change)
            return _storeChainlinkPrice(chainlinkResponse);
        }

        // --- CASE 2: The system fetched last price from Tellor ---
        if (status == Status.usingTellorChainlinkUntrusted) {
            // If both Tellor and Chainlink are live, unbroken, and reporting similar prices, switch back to Chainlink
            if (_bothOraclesLiveAndUnbrokenAndSimilarPrice(chainlinkResponse, prevChainlinkResponse, tellorResponse)) {
                _changeStatus(Status.chainlinkWorking);
                return _storeChainlinkPrice(chainlinkResponse);
            }

            if (_tellorIsBroken(tellorResponse)) {
                _changeStatus(Status.bothOraclesUntrusted);
                return lastGoodPrice;
            }

            /*
            * If Tellor is only frozen but otherwise returning valid data, just return the last good price.
            * Tellor may need to be tipped to return current data.
            */
            if (_tellorIsFrozen(tellorResponse)) {return lastGoodPrice;}

            // Otherwise, use Tellor price
            return _storeTellorPrice(tellorResponse);
        }

        // --- CASE 3: Both oracles were untrusted at the last price fetch ---
        if (status == Status.bothOraclesUntrusted) {
            /*
            * If both oracles are now live, unbroken and similar price, we assume that they are reporting
            * accurately, and so we switch back to Chainlink.
            */
            if (_bothOraclesLiveAndUnbrokenAndSimilarPrice(chainlinkResponse, prevChainlinkResponse, tellorResponse)) {
                _changeStatus(Status.chainlinkWorking);
                return _storeChainlinkPrice(chainlinkResponse);
            }

            // Otherwise, return the last good price - both oracles are still untrusted (no status change)
            return lastGoodPrice;
        }

        // --- CASE 4: Using Tellor, and Chainlink is frozen ---
        if (status == Status.usingTellorChainlinkFrozen) {
            if (_chainlinkIsBroken(chainlinkResponse, prevChainlinkResponse)) {
                // If both Oracles are broken, return last good price
                if (_tellorIsBroken(tellorResponse)) {
                    _changeStatus(Status.bothOraclesUntrusted);
                    return lastGoodPrice;
                }

                // If Chainlink is broken, remember it and switch to using Tellor
                _changeStatus(Status.usingTellorChainlinkUntrusted);

                if (_tellorIsFrozen(tellorResponse)) {return lastGoodPrice;}

                // If Tellor is working, return Tellor current price
                return _storeTellorPrice(tellorResponse);
            }

            if (_chainlinkIsFrozen(chainlinkResponse)) {
                // if Chainlink is frozen and Tellor is broken, remember Tellor broke, and return last good price
                if (_tellorIsBroken(tellorResponse)) {
                    _changeStatus(Status.usingChainlinkTellorUntrusted);
                    return lastGoodPrice;
                }

                // If both are frozen, just use lastGoodPrice
                if (_tellorIsFrozen(tellorResponse)) {return lastGoodPrice;}

                // if Chainlink is frozen and Tellor is working, keep using Tellor (no status change)
                return _storeTellorPrice(tellorResponse);
            }

            // if Chainlink is live and Tellor is broken, remember Tellor broke, and return Chainlink price
            if (_tellorIsBroken(tellorResponse)) {
                _changeStatus(Status.usingChainlinkTellorUntrusted);
                return _storeChainlinkPrice(chainlinkResponse);
            }

             // If Chainlink is live and Tellor is frozen, just use last good price (no status change) since we have no basis for comparison
            if (_tellorIsFrozen(tellorResponse)) {return lastGoodPrice;}

            // If Chainlink is live and Tellor is working, compare prices. Switch to Chainlink
            // if prices are within 5%, and return Chainlink price.
            if (_bothOraclesSimilarPrice(chainlinkResponse, tellorResponse)) {
                _changeStatus(Status.chainlinkWorking);
                return _storeChainlinkPrice(chainlinkResponse);
            }

            // Otherwise if Chainlink is live but price not within 5% of Tellor, distrust Chainlink, and return Tellor price
            _changeStatus(Status.usingTellorChainlinkUntrusted);
            return _storeTellorPrice(tellorResponse);
        }

        // --- CASE 5: Using Chainlink, Tellor is untrusted ---
        if (status == Status.usingChainlinkTellorUntrusted) {
            // If Chainlink breaks, now both oracles are untrusted
            if (_chainlinkIsBroken(chainlinkResponse, prevChainlinkResponse)) {
                _changeStatus(Status.bothOraclesUntrusted);
                return lastGoodPrice;
            }

            // If Chainlink is frozen, return last good price (no status change)
            if (_chainlinkIsFrozen(chainlinkResponse)) {
                return lastGoodPrice;
            }

            // If Chainlink and Tellor are both live, unbroken and similar price, switch back to chainlinkWorking and return Chainlink price
            if (_bothOraclesLiveAndUnbrokenAndSimilarPrice(chainlinkResponse, prevChainlinkResponse, tellorResponse)) {
                _changeStatus(Status.chainlinkWorking);
                return _storeChainlinkPrice(chainlinkResponse);
            }

            // If Chainlink is live but deviated >50% from it's previous price and Tellor is still untrusted, switch
            // to bothOraclesUntrusted and return last good price
            if (_chainlinkPriceChangeAboveMax(chainlinkResponse, prevChainlinkResponse)) {
                _changeStatus(Status.bothOraclesUntrusted);
                return lastGoodPrice;
            }

            // Otherwise if Chainlink is live and deviated <50% from it's previous price and Tellor is still untrusted,
            // return Chainlink price (no status change)
            return _storeChainlinkPrice(chainlinkResponse);
        }
        return lastGoodPrice;
    }

    // --- Helper functions ---

    /* Chainlink is considered broken if its current or previous round data is in any way bad. We check the previous round
    * for two reasons:
    *
    * 1) It is necessary data for the price deviation check in case 1,
    * and
    * 2) Chainlink is the PriceFeed's preferred primary oracle - having two consecutive valid round responses adds
    * peace of mind when using or returning to Chainlink.
    */
    function _chainlinkIsBroken(ChainlinkResponse memory _currentResponse, ChainlinkResponse memory _prevResponse) internal view returns (bool) {
        return _badChainlinkResponse(_currentResponse) || _badChainlinkResponse(_prevResponse);
    }

    function _badChainlinkResponse(ChainlinkResponse memory _response) internal view returns (bool) {
         // Check for response call reverted
        if (!_response.success) {return true;}
        // Check for an invalid roundId that is 0
        if (_response.roundId == 0) {return true;}
        // Check for an invalid timeStamp that is 0, or in the future
        if (_response.timestamp == 0 || _response.timestamp > block.timestamp) {return true;}
        // Check for non-positive price
        if (_response.answer <= 0) {return true;}

        return false;
    }

    function _chainlinkIsFrozen(ChainlinkResponse memory _response) internal view returns (bool) {
        return block.timestamp - _response.timestamp > TIMEOUT;
    }

    function _chainlinkPriceChangeAboveMax(ChainlinkResponse memory _currentResponse, ChainlinkResponse memory _prevResponse) internal pure returns (bool) {
        uint256 currentScaledPrice = _scaleChainlinkPriceByDigits(uint256(_currentResponse.answer), _currentResponse.decimals);
        uint256 prevScaledPrice = _scaleChainlinkPriceByDigits(uint256(_prevResponse.answer), _prevResponse.decimals);

        uint256 minPrice = LiquityMath._min(currentScaledPrice, prevScaledPrice);
        uint256 maxPrice = LiquityMath._max(currentScaledPrice, prevScaledPrice);

        /*
        * Use the larger price as the denominator:
        * - If price decreased, the percentage deviation is in relation to the the previous price.
        * - If price increased, the percentage deviation is in relation to the current price.
        */
        uint256 percentDeviation = (maxPrice - minPrice) * DECIMAL_PRECISION / maxPrice;

        // Return true if price has more than doubled, or more than halved.
        return percentDeviation > MAX_PRICE_DEVIATION_FROM_PREVIOUS_ROUND;
    }

    function _tellorIsBroken(TellorResponse memory _response) internal view returns (bool) {
        // Check for response call reverted
        if (!_response.success) {return true;}
        // Check for an invalid timeStamp that is 0, or in the future
        if (_response.timestamp == 0 || _response.timestamp > block.timestamp) {return true;}
        // Check for zero price
        if (_response.value == 0) {return true;}

        return false;
    }

     function _tellorIsFrozen(TellorResponse  memory _tellorResponse) internal view returns (bool) {
        return block.timestamp - _tellorResponse.timestamp > TIMEOUT;
    }

    function _bothOraclesLiveAndUnbrokenAndSimilarPrice
    (
        ChainlinkResponse memory _chainlinkResponse,
        ChainlinkResponse memory _prevChainlinkResponse,
        TellorResponse memory _tellorResponse
    )
        internal
        view
        returns (bool)
    {
        // Return false if either oracle is broken or frozen
        if
        (
            _tellorIsBroken(_tellorResponse) ||
            _tellorIsFrozen(_tellorResponse) ||
            _chainlinkIsBroken(_chainlinkResponse, _prevChainlinkResponse) ||
            _chainlinkIsFrozen(_chainlinkResponse)
        )
        {
            return false;
        }

        return _bothOraclesSimilarPrice(_chainlinkResponse, _tellorResponse);
    }

    function _bothOraclesSimilarPrice( ChainlinkResponse memory _chainlinkResponse, TellorResponse memory _tellorResponse) internal view returns (bool) {
        uint256 scaledChainlinkPrice = _scaleChainlinkPriceByDigits(uint256(_chainlinkResponse.answer), _chainlinkResponse.decimals);
        uint256 scaledTellorPrice = _scaleTellorPriceByDigits(_tellorResponse.value);

        // Get the relative price difference between the oracles. Use the lower price as the denominator, i.e. the reference for the calculation.
        uint256 minPrice = LiquityMath._min(scaledTellorPrice, scaledChainlinkPrice);
        uint256 maxPrice = LiquityMath._max(scaledTellorPrice, scaledChainlinkPrice);
        uint256 percentPriceDifference = (maxPrice - minPrice) * DECIMAL_PRECISION / minPrice;

        /*
        * Return true if the relative price difference is <= 3%: if so, we assume both oracles are probably reporting
        * the honest market price, as it is unlikely that both have been broken/hacked and are still in-sync.
        */
        return percentPriceDifference <= MAX_PRICE_DIFFERENCE_BETWEEN_ORACLES;
    }

    function _scaleChainlinkPriceByDigits(uint256 _price, uint256 _answerDigits) internal pure returns (uint) {
        /*
        * Convert the price returned by the Chainlink oracle to an 18-digit decimal for use by Liquity.
        * At date of Liquity launch, Chainlink uses an 8-digit price, but we also handle the possibility of
        * future changes.
        *
        */
        uint256 price;
        if (_answerDigits >= TARGET_DIGITS) {
            // Scale the returned price value down to Liquity's target precision
            price = _price / (10 ** (_answerDigits - TARGET_DIGITS));
        }
        else if (_answerDigits < TARGET_DIGITS) {
            // Scale the returned price value up to Liquity's target precision
            price = _price * (10 ** (TARGET_DIGITS - _answerDigits));
        }
        return price;
    }

    function _scaleTellorPriceByDigits(uint256 _price) internal view returns (uint) {
        return _price * (10**(TARGET_DIGITS - tellorDigits));
    }

    function _changeStatus(Status _status) internal {
        status = _status;
        emit PriceFeedStatusChanged(_status);
    }

    function _storePrice(uint256 _currentPrice) internal {
        lastGoodPrice = _currentPrice;
        emit LastGoodPriceUpdated(_currentPrice);
    }

     function _storeTellorPrice(TellorResponse memory _tellorResponse) internal returns (uint) {
        uint256 scaledTellorPrice = _scaleTellorPriceByDigits(_tellorResponse.value);
        _storePrice(scaledTellorPrice);

        return scaledTellorPrice;
    }

    function _storeChainlinkPrice(ChainlinkResponse memory _chainlinkResponse) internal returns (uint) {
        uint256 scaledChainlinkPrice = _scaleChainlinkPriceByDigits(uint256(_chainlinkResponse.answer), _chainlinkResponse.decimals);
        _storePrice(scaledChainlinkPrice);

        return scaledChainlinkPrice;
    }

    // --- Oracle response wrapper functions ---

    function _getCurrentTellorResponse() internal returns (TellorResponse memory tellorResponse) {
        try tellorCaller.getTellorCurrentValue() returns
        (
            bool ifRetrieve,
            uint256 value,
            uint256 _timestampRetrieved
        )
        {
            // If call to Tellor succeeds, return the response and success = true
            tellorResponse.ifRetrieve = ifRetrieve;
            tellorResponse.value = value;
            tellorResponse.timestamp = _timestampRetrieved;
            tellorResponse.success = true;

            return (tellorResponse);
        }catch {
             // If call to Tellor reverts, return a zero response with success = false
            return (tellorResponse);
        }
    }

    function _getCurrentChainlinkResponse() internal view returns (ChainlinkResponse memory chainlinkResponse) {
        // First, try to get current decimal precision:
        try priceAggregator.decimals() returns (uint8 decimals) {
            // If call to Chainlink succeeds, record the current decimal precision
            chainlinkResponse.decimals = decimals;
        } catch {
            // If call to Chainlink aggregator reverts, return a zero response with success = false
            return chainlinkResponse;
        }

        // Secondly, try to get latest price data:
        try priceAggregator.latestRoundData() returns
        (
            uint80 roundId,
            int256 answer,
            uint256 /* startedAt */,
            uint256 timestamp,
            uint80 /* answeredInRound */
        )
        {
            // If call to Chainlink succeeds, return the response and success = true
            chainlinkResponse.roundId = roundId;
            chainlinkResponse.answer = answer;
            chainlinkResponse.timestamp = timestamp;
            chainlinkResponse.success = true;
            return chainlinkResponse;
        } catch {
            // If call to Chainlink aggregator reverts, return a zero response with success = false
            return chainlinkResponse;
        }
    }

    function _getPrevChainlinkResponse(uint80 _currentRoundId, uint8 _currentDecimals) internal view returns (ChainlinkResponse memory prevChainlinkResponse) {
        /*
        * NOTE: Chainlink only offers a current decimals() value - there is no way to obtain the decimal precision used in a
        * previous round.  We assume the decimals used in the previous round are the same as the current round.
        */
        if (_currentRoundId == 0) {
      			return prevChainlinkResponse;
    		}
        // Try to get the price data from the previous round:
        try priceAggregator.getRoundData(_currentRoundId - 1) returns
        (
            uint80 roundId,
            int256 answer,
            uint256 /* startedAt */,
            uint256 timestamp,
            uint80 /* answeredInRound */
        )
        {
            // If call to Chainlink succeeds, return the response and success = true
            prevChainlinkResponse.roundId = roundId;
            prevChainlinkResponse.answer = answer;
            prevChainlinkResponse.timestamp = timestamp;
            prevChainlinkResponse.decimals = _currentDecimals;
            prevChainlinkResponse.success = true;
            return prevChainlinkResponse;
        } catch {
            // If call to Chainlink aggregator reverts, return a zero response with success = false
            return prevChainlinkResponse;
        }
    }

    /*
    * forceExitBothUntrustedStatus()
    * DAO can help one oracle return back online only if previously both were untrusted.
    * Function reverts if both oracles are still broken.
    * In case when both oracles are online but have different prices then caller can control 
    * which will be checked first: ChainLink if tryTellorFirst==false, Tellor otherwise
    */ 
    function forceExitBothUntrustedStatus(bool tryTellorFirst) external onlyOwner {
        require(status == Status.bothOraclesUntrusted, "PriceFeed: at least one oracle is working");

        ChainlinkResponse memory chainlinkResponse = _getCurrentChainlinkResponse();
        ChainlinkResponse memory prevChainlinkResponse = _getPrevChainlinkResponse(chainlinkResponse.roundId, chainlinkResponse.decimals);
        TellorResponse memory tellorResponse = _getCurrentTellorResponse();

        // check Tellor first only if flag was true, exit function in case of success
        if
        (
            tryTellorFirst &&
            _changeStatusIfTellorLiveAndUnbroken(tellorResponse)
        )
        {
            return;
        }

        // check ChainLink feed
        if 
        (
            !_chainlinkIsBroken(chainlinkResponse, prevChainlinkResponse) &&
            !_chainlinkIsFrozen(chainlinkResponse) &&
            !_chainlinkPriceChangeAboveMax(chainlinkResponse, prevChainlinkResponse)
        ) 
        {
            _changeStatus(Status.usingChainlinkTellorUntrusted);
            _storeChainlinkPrice(chainlinkResponse);
            return;
        }

        // if Tellor was already checked or check is false then revert transaction
        if
        (
            !tryTellorFirst &&
            _changeStatusIfTellorLiveAndUnbroken(tellorResponse)
        )
        {
            return;
        }

        // Transaction is successful only if one oracle returned back online
        revert("PriceFeed: both oracles are still untrusted");
    }

    function _changeStatusIfTellorLiveAndUnbroken
    (
        TellorResponse memory _tellorResponse
    )
        internal
        returns (bool)
    {
        // Return true if Tellor is back online
        if
        (
            !_tellorIsBroken(_tellorResponse) &&
            !_tellorIsFrozen(_tellorResponse)
        )
        {
            _changeStatus(Status.usingTellorChainlinkUntrusted);
            _storeTellorPrice(_tellorResponse);
            return true;
        }

        return false;
    }
}

File 2 of 8 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
// Code from https://github.com/smartcontractkit/chainlink/blob/master/evm-contracts/src/v0.6/interfaces/AggregatorV3Interface.sol

pragma solidity ^0.8.17;

interface AggregatorV3Interface {

  function decimals() external view returns (uint8);
  function description() external view returns (string memory);
  function version() external view returns (uint256);

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}

File 3 of 8 : BaseMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;


contract BaseMath {
    uint256 constant public DECIMAL_PRECISION = 1e18;
}

File 4 of 8 : CheckContract.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;


contract CheckContract {
    /**
     * Check that the account is an already deployed non-destroyed contract.
     * See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L12
     */
    function checkContract(address _account) internal view {
        require(_account != address(0), "Account cannot be zero address");

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(_account) }
        require(size > 0, "Account code size cannot be zero");
    }
}

File 5 of 8 : LiquityMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

library LiquityMath {

    uint256 internal constant DECIMAL_PRECISION = 1e18;

    /* Precision for Nominal ICR (independent of price). Rationale for the value:
     *
     * - Making it “too high” could lead to overflows.
     * - Making it “too low” could lead to an ICR equal to zero, due to truncation from Solidity floor division.
     *
     * This value of 1e20 is chosen for safety: the NICR will only overflow for numerator > ~1e39 ETH,
     * and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator.
     *
     */
    uint256 internal constant NICR_PRECISION = 1e20;

    function _min(uint256 _a, uint256 _b) internal pure returns (uint) {
        return (_a < _b) ? _a : _b;
    }

    function _max(uint256 _a, uint256 _b) internal pure returns (uint) {
        return (_a >= _b) ? _a : _b;
    }

    /*
    * Multiply two decimal numbers and use normal rounding rules:
    * -round product up if 19'th mantissa digit >= 5
    * -round product down if 19'th mantissa digit < 5
    *
    * Used only inside the exponentiation, _decPow().
    */
    function decMul(uint256 x, uint256 y) internal pure returns (uint256 decProd) {
        uint256 prod_xy = x * y;

        decProd = (prod_xy + (DECIMAL_PRECISION / 2)) / DECIMAL_PRECISION;
    }

    /*
    * _decPow: Exponentiation function for 18-digit decimal base, and integer exponent n.
    *
    * Uses the efficient "exponentiation by squaring" algorithm. O(log(n)) complexity.
    *
    * Called by one function that represent time in units of minutes:
    * 1) TroveManager._calcDecayedBaseRate
    *
    * The exponent is capped to avoid reverting due to overflow. The cap 525600000 equals
    * "minutes in 1000 years": 60 * 24 * 365 * 1000
    *
    * If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be
    * negligibly different from just passing the cap, since:
    *
    * In function 1), the decayed base rate will be 0 for 1000 years or > 1000 years
    * In function 2), the difference in tokens issued at 1000 years and any time > 1000 years, will be negligible
    */
    function _decPow(uint256 _base, uint256 _minutes) internal pure returns (uint) {

        if (_minutes > 525600000) {_minutes = 525600000;}  // cap to avoid overflow

        if (_minutes == 0) {return DECIMAL_PRECISION;}

        uint256 y = DECIMAL_PRECISION;
        uint256 x = _base;
        uint256 n = _minutes;

        // Exponentiation-by-squaring
        while (n > 1) {
            if (n % 2 == 0) {
                x = decMul(x, x);
                n = n / 2;
            } else { // if (n % 2 != 0)
                y = decMul(x, y);
                x = decMul(x, x);
                n = (n - 1) / 2;
            }
        }

        return decMul(x, y);
  }

    function _getAbsoluteDifference(uint256 _a, uint256 _b) internal pure returns (uint) {
        return (_a >= _b) ? _a - _b : _b - _a;
    }

    function _computeNominalCR(uint256 _coll, uint256 _debt) internal pure returns (uint) {
        if (_debt > 0) {
            return _coll * NICR_PRECISION / _debt;
        }
        // Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR.
        else { // if (_debt == 0)
            return type(uint256).max;
        }
    }

    function _computeCR(uint256 _coll, uint256 _debt, uint256 _price) internal pure returns (uint) {
        if (_debt > 0) {
            uint256 newCollRatio = _coll * _price / _debt;

            return newCollRatio;
        }
        // Return the maximal value for uint256 if the Trove has a debt of 0. Represents "infinite" CR.
        else { // if (_debt == 0)
            return type(uint256).max;
        }
    }
}

File 6 of 8 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

/**
 * Based on OpenZeppelin's Ownable contract:
 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
 *
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == _owner;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     *
     * NOTE: This function is not safe, as it doesn’t check owner is calling it.
     * Make sure you check it before calling it.
     */
    function _renounceOwnership() internal {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 7 of 8 : IPriceFeed.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

interface IPriceFeed {

    // --- Events ---
    event LastGoodPriceUpdated(uint256 _lastGoodPrice);
   
    // --- Function ---
    function fetchPrice() external returns (uint);
}

File 8 of 8 : ITellorCaller.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

interface ITellorCaller {
    function getTellorCurrentValue() external returns (bool, uint256, uint256);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 100
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"_tellorDigits","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_lastGoodPrice","type":"uint256"}],"name":"LastGoodPriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum PriceFeed.Status","name":"newStatus","type":"uint8"}],"name":"PriceFeedStatusChanged","type":"event"},{"inputs":[],"name":"DECIMAL_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PRICE_DEVIATION_FROM_PREVIOUS_ROUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PRICE_DIFFERENCE_BETWEEN_ORACLES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TARGET_DIGITS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TIMEOUT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fetchPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"tryTellorFirst","type":"bool"}],"name":"forceExitBothUntrustedStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastGoodPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceAggregator","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_priceAggregatorAddress","type":"address"},{"internalType":"address","name":"_tellorCallerAddress","type":"address"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"status","outputs":[{"internalType":"enum PriceFeed.Status","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tellorCaller","outputs":[{"internalType":"contract ITellorCaller","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tellorDigits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100eb5760003560e01c80637c61e3bf116100925780637c61e3bf146101a55780638da5cb5b146101b85780638f32d59b146101c957806390107afe146101e15780639150cac0146101f6578063a20baee614610209578063a3f4df7e14610218578063f2fde38b1461024d578063f56f48f21461026057600080fd5b80630490be83146100f05780630fdb11cf1461010c5780631a1fe653146101145780631be5c92f1461013b578063200d2ed2146101435780633078fff51461015d57806345079cb41461018857806358a6aa8814610196575b600080fd5b6100f960035481565b6040519081526020015b60405180910390f35b6100f9610269565b6100f97f000000000000000000000000000000000000000000000000000000000000001281565b6100f9601281565b6004546101509060ff1681565b6040516101039190611168565b600154610170906001600160a01b031681565b6040516001600160a01b039091168152602001610103565b6100f966b1a2bc2ec5000081565b6100f96706f05b59d3b2000081565b600254610170906001600160a01b031681565b6000546001600160a01b0316610170565b6101d16105a4565b6040519015158152602001610103565b6101f46101ef3660046111ac565b6105b5565b005b6101f46102043660046111ed565b61073b565b6100f9670de0b6b3a764000081565b61024060405180604001604052806009815260200168141c9a58d95199595960ba1b81525081565b604051610103919061120a565b6101f461025b366004611258565b6108e5565b6100f961384081565b600080610274610977565b9050600061028a82600001518360800151610a96565b90506000610296610b60565b905060006004805460ff16908111156102b1576102b1611152565b036103dc576102c08383610c07565b1561031b576102ce81610c28565b156102e8576102dd6002610c78565b600354935050505090565b6102f181610cd5565b15610300576102dd6001610c78565b61030a6001610c78565b61031381610cf1565b935050505090565b61032483610cd5565b156103645761033281610c28565b15610341576102dd6004610c78565b61034b6003610c78565b61035481610cd5565b1561030a57600354935050505090565b61036e8383610d0c565b156103bb5761037c81610c28565b1561038b576102dd6002610c78565b61039481610cd5565b156103a3576102dd6001610c78565b6103ad8382610d9b565b156103005761031383610e22565b6103c481610c28565b156103d3576103d36004610c78565b61031383610e22565b60016004805460ff16908111156103f5576103f5611152565b0361042c57610405838383610e3a565b15610414576103d36000610c78565b61041d81610c28565b1561034b576102dd6002610c78565b60026004805460ff169081111561044557610445611152565b0361046457610455838383610e3a565b156102dd576103d36000610c78565b60036004805460ff169081111561047d5761047d611152565b036105235761048c8383610c07565b156104b35761049a81610c28565b156104a9576102dd6002610c78565b61034b6001610c78565b6104bc83610cd5565b156104d9576104ca81610c28565b1561034b576102dd6004610c78565b6104e281610c28565b156104f1576103d36004610c78565b6104fa81610cd5565b1561050a57600354935050505090565b6105148382610d9b565b15610300576103d36000610c78565b6004805460ff168181111561053a5761053a611152565b036102dd576105498383610c07565b15610558576102dd6002610c78565b61056183610cd5565b1561057157600354935050505090565b61057c838383610e3a565b1561058b576103d36000610c78565b6105958383610d0c565b156103d3576102dd6002610c78565b6000546001600160a01b0316331490565b6105bd6105a4565b6105e25760405162461bcd60e51b81526004016105d990611273565b60405180910390fd5b6001546001600160a01b03161561063b5760405162461bcd60e51b815260206004820152601f60248201527f5072696365466565643a20636f6e746163747320616c7265616479207365740060448201526064016105d9565b61064482610e92565b61064d81610e92565b600180546001600160a01b038481166001600160a01b03199283161790925560028054928416929091169190911790556004805460ff191690556000610691610977565b905060006106a782600001518360800151610a96565b90506106b38282610c07565b1580156106c657506106c482610cd5565b155b61072b5760405162461bcd60e51b815260206004820152603060248201527f5072696365466565643a20436861696e6c696e6b206d75737420626520776f7260448201526f1ada5b99c8185b990818dd5c9c995b9d60821b60648201526084016105d9565b61073482610e22565b5050505050565b6107436105a4565b61075f5760405162461bcd60e51b81526004016105d990611273565b60026004805460ff169081111561077857610778611152565b146107d75760405162461bcd60e51b815260206004820152602960248201527f5072696365466565643a206174206c65617374206f6e65206f7261636c6520696044820152687320776f726b696e6760b81b60648201526084016105d9565b60006107e1610977565b905060006107f782600001518360800151610a96565b90506000610803610b60565b9050838015610816575061081681610f3b565b156108215750505050565b61082b8383610c07565b15801561083e575061083c83610cd5565b155b8015610851575061084f8383610d0c565b155b15610869576108606004610c78565b61073483610e22565b8315801561087b575061087b81610f3b565b156108865750505050565b60405162461bcd60e51b815260206004820152602b60248201527f5072696365466565643a20626f7468206f7261636c657320617265207374696c60448201526a1b081d5b9d1c9d5cdd195960aa1b60648201526084016105d9565b50565b6108ed6105a4565b6109095760405162461bcd60e51b81526004016105d990611273565b6001600160a01b03811661096e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016105d9565b6108e281610f7a565b61097f611124565b600160009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156109ee575060408051601f3d908101601f191682019092526109eb918101906112a8565b60015b6109f55790565b60ff166080820152600160009054906101000a90046001600160a01b03166001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa925050508015610a6c575060408051601f3d908101601f19168201909252610a69918101906112e2565b60015b610a735790565b506001600160501b03909316845250602083015260408201526001606082015290565b610a9e611124565b6001600160501b03831615610b5a57600180546001600160a01b031690639a6fc8f590610acb9086611348565b6040516001600160e01b031960e084901b1681526001600160501b03909116600482015260240160a060405180830381865afa925050508015610b2b575060408051601f3d908101601f19168201909252610b28918101906112e2565b60015b15610b5a57506001600160501b039093168452506020830152604082015260ff82166080820152600160608201525b92915050565b6040805160808101825260008082526020820181905281830181905260608083018290526002548451631328670560e31b8152945193946001600160a01b03909116936399433828936004808401949383900301908290875af1925050508015610be7575060408051601f3d908101601f19168201909252610be49181019061136f565b60015b610bee5790565b9115158352602083015260408201526001606082015290565b6000610c1283610fca565b80610c215750610c2182610fca565b9392505050565b60008160600151610c3b57506001919050565b60408201511580610c4f5750428260400151115b15610c5c57506001919050565b8160200151600003610c7057506001919050565b506000919050565b6004805482919060ff191660018383811115610c9657610c96611152565b02179055507f5c57579a8214fe4f710c1c56fa829f045b9fa6d225a744225a30c32188064d4e81604051610cca9190611168565b60405180910390a150565b6000613840826040015142610cea91906113a6565b1192915050565b600080610d01836020015161102c565b9050610b5a8161106e565b600080610d248460200151856080015160ff166110a3565b90506000610d3d8460200151856080015160ff166110a3565b90506000610d4b83836110fe565b90506000610d598484611114565b9050600081670de0b6b3a7640000610d7185836113a6565b610d7b91906113b9565b610d8591906113d0565b6706f05b59d3b200001098975050505050505050565b600080610db38460200151856080015160ff166110a3565b90506000610dc4846020015161102c565b90506000610dd282846110fe565b90506000610de08385611114565b9050600082670de0b6b3a7640000610df882856113a6565b610e0291906113b9565b610e0c91906113d0565b66b1a2bc2ec50000101598975050505050505050565b600080610d018360200151846080015160ff166110a3565b6000610e4582610c28565b80610e545750610e5482610cd5565b80610e645750610e648484610c07565b80610e735750610e7384610cd5565b15610e8057506000610c21565b610e8a8483610d9b565b949350505050565b6001600160a01b038116610ee85760405162461bcd60e51b815260206004820152601e60248201527f4163636f756e742063616e6e6f74206265207a65726f2061646472657373000060448201526064016105d9565b803b80610f375760405162461bcd60e51b815260206004820181905260248201527f4163636f756e7420636f64652073697a652063616e6e6f74206265207a65726f60448201526064016105d9565b5050565b6000610f4682610c28565b158015610f595750610f5782610cd5565b155b15610c7057610f686001610c78565b610f7182610cf1565b50600192915050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008160600151610fdd57506001919050565b81516001600160501b0316600003610ff757506001919050565b6040820151158061100b5750428260400151115b1561101857506001919050565b6000826020015113610c7057506001919050565b60006110597f000000000000000000000000000000000000000000000000000000000000001260126113a6565b61106490600a6114d6565b610b5a90836113b9565b60038190556040518181527f4d29de21de555af78a62fc82dd4bc05e9ae5b0660a37f04729527e0f22780cd390602001610cca565b600080601283106110d5576110b96012846113a6565b6110c490600a6114d6565b6110ce90856113d0565b9050610c21565b6012831015610c21576110e98360126113a6565b6110f490600a6114d6565b610e8a90856113b9565b600081831061110d5781610c21565b5090919050565b60008183101561110d5781610c21565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b634e487b7160e01b600052602160045260246000fd5b602081016005831061118a57634e487b7160e01b600052602160045260246000fd5b91905290565b80356001600160a01b03811681146111a757600080fd5b919050565b600080604083850312156111bf57600080fd5b6111c883611190565b91506111d660208401611190565b90509250929050565b80151581146108e257600080fd5b6000602082840312156111ff57600080fd5b8135610c21816111df565b600060208083528351808285015260005b818110156112375785810183015185820160400152820161121b565b506000604082860101526040601f19601f8301168501019250505092915050565b60006020828403121561126a57600080fd5b610c2182611190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6000602082840312156112ba57600080fd5b815160ff81168114610c2157600080fd5b80516001600160501b03811681146111a757600080fd5b600080600080600060a086880312156112fa57600080fd5b611303866112cb565b9450602086015193506040860151925060608601519150611326608087016112cb565b90509295509295909350565b634e487b7160e01b600052601160045260246000fd5b6001600160501b0382811682821603908082111561136857611368611332565b5092915050565b60008060006060848603121561138457600080fd5b835161138f816111df565b602085015160409095015190969495509392505050565b81810381811115610b5a57610b5a611332565b8082028115828204841417610b5a57610b5a611332565b6000826113ed57634e487b7160e01b600052601260045260246000fd5b500490565b600181815b8085111561142d57816000190482111561141357611413611332565b8085161561142057918102915b93841c93908002906113f7565b509250929050565b60008261144457506001610b5a565b8161145157506000610b5a565b816001811461146757600281146114715761148d565b6001915050610b5a565b60ff84111561148257611482611332565b50506001821b610b5a565b5060208310610133831016604e8410600b84101617156114b0575081810a610b5a565b6114ba83836113f2565b80600019048211156114ce576114ce611332565b029392505050565b6000610c21838361143556fea2646970667358221220a1524d4c528c5328ccbbcd09cda3431a0bff40cc2076aaec4c9d8e784e0ff00664736f6c63430008110033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ 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.