Sepolia Testnet

Contract

0xF08F9Ae30e19f2CA9b0876a7Dc57E6695010dE40

Overview

ETH Balance

0 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Value
Fetch Price55731622024-03-27 18:28:1261 days ago1711564092IN
0xF08F9Ae3...95010dE40
0 ETH0.000146521.61477967

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
54661342024-03-11 21:31:4877 days ago1710192708  Contract Creation0 ETH
Loading...
Loading

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

Contract Name:
EbtcFeed

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 6 : EbtcFeed.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

import "./Interfaces/IPriceFeed.sol";
import {IPriceFetcher} from "./Interfaces/IOracleCaller.sol";
import "./Dependencies/AuthNoOwner.sol";

/*
 * PriceFeed for mainnet deployment, it connects to two Chainlink's live feeds, ETH:BTC and
 * stETH:ETH, which are used to aggregate the price feed of stETH:BTC in conjuction.
 * It also allows for a fallback oracle to intervene in case that the primary Chainlink oracle fails.
 *
 * The PriceFeed uses Chainlink as primary oracle and allows for an optional fallback source. It contains logic for
 * switching oracles based on oracle failures, timeouts, and conditions for returning to the primary
 * Chainlink oracle. In addition, it contains the mechanism to add or remove the fallback oracle through governance.
 */
contract EbtcFeed is IPriceFeed, AuthNoOwner {
    string public constant NAME = "EbtcFeed";

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

    address public primaryOracle;
    address public secondaryOracle;

    uint256 constant INVALID_PRICE = 0;
    address constant UNSET_ADDRESS = address(0);
    uint256 constant GAS_LIMIT = 2_000_000;

    // --- Events ---

    event PrimaryOracleUpdated(address indexed _oldOracle, address indexed _newOracle);
    event SecondaryOracleUpdated(address indexed _oldOracle, address indexed _newOracle);

    // NOTE: Could still use Status to signal current FSM

    // --- Dependency setters ---

    /// @notice Sets the addresses of the contracts and initializes the system
    constructor(address _authorityAddress, address _primaryOracle, address _secondaryOracle) {
        _initializeAuthority(_authorityAddress);

        uint256 firstPrice = IPriceFetcher(_primaryOracle).fetchPrice();
        require(firstPrice != INVALID_PRICE, "EbtcFeed: Primary Oracle Must Work");

        _storePrice(firstPrice);

        primaryOracle = _primaryOracle;

        // If secondaryOracle is known at deployment let's add it
        if (_secondaryOracle != UNSET_ADDRESS) {
            uint256 secondaryOraclePrice = IPriceFetcher(_secondaryOracle).fetchPrice();

            if (secondaryOraclePrice != INVALID_PRICE) {
                secondaryOracle = _secondaryOracle;
            }
        }
    }

    /// @notice Allows the owner to replace the primary oracle
    ///     The oracle must work (return non-zero value)
    function setPrimaryOracle(address _newPrimary) external requiresAuth {
        uint256 currentPrice = IPriceFetcher(_newPrimary).fetchPrice();
        require(currentPrice != INVALID_PRICE, "EbtcFeed: Primary Oracle Must Work");

        emit PrimaryOracleUpdated(primaryOracle, _newPrimary);
        primaryOracle = _newPrimary;
    }

    /// @notice Allows the owner to replace the secondary oracle
    ///     The oracle must work (return non-zero value), unless removed
    function setSecondaryOracle(address _newSecondary) external requiresAuth {
        // Allow governance to remove the secondary oracle
        if (_newSecondary != UNSET_ADDRESS) {
            uint256 currentPrice = IPriceFetcher(_newSecondary).fetchPrice();
            require(currentPrice != INVALID_PRICE, "EbtcFeed: Secondary Oracle Must Work");
        }

        emit SecondaryOracleUpdated(secondaryOracle, _newSecondary);
        secondaryOracle = _newSecondary;
    }

    /// @notice Fetch the Latest Valid Price
    ///     Assumes the oracle call will return 0 if the data is invalid
    ///     Any non-zero value will be interpreted as valid
    ///     The security checks must be performed by the OracleCallers
    ///
    ///     Logic Breakdown:
    ///
    ///     If primary works, use that and store it as last good price
    ///
    ///     If not, try using secondary, if secondary works, use that and store it as last good price
    ///
    ///     If neither work, use the last good price
    ///
    ///     @dev All calls are done via `tinfoilCall` to allow the maximum resiliency we are able to provide
    ///     Due to this, a OracleCaller has to be written, which will be responsible for calling the real oracle
    ///     this ensures all interfaces are the same and that the logic here is to handle:
    ///     - Functioning Case
    ///     - All types of DOSes by the Oracles
    function fetchPrice() external override returns (uint256) {
        // Tinfoil Call
        uint256 primaryResponse = tinfoilCall(
            primaryOracle,
            abi.encodeCall(IPriceFetcher.fetchPrice, ())
        );

        if (primaryResponse != INVALID_PRICE) {
            _storePrice(primaryResponse);
            return primaryResponse;
        }

        if (secondaryOracle == UNSET_ADDRESS) {
            return lastGoodPrice; // No fallback, just return latest
        }

        // Let's try secondary
        uint256 secondaryResponse = tinfoilCall(
            secondaryOracle,
            abi.encodeCall(IPriceFetcher.fetchPrice, ())
        );

        if (secondaryResponse != INVALID_PRICE) {
            _storePrice(secondaryResponse);
            return secondaryResponse;
        }

        // No valid price, return last
        // NOTE: We could emit something here as this means both oracles are dead
        return lastGoodPrice;
    }

    /// @notice Stores the latest valid price.
    /// @param _currentPrice The price to be stored.
    function _storePrice(uint256 _currentPrice) internal {
        lastGoodPrice = _currentPrice;
        emit LastGoodPriceUpdated(_currentPrice);
    }

    /// @dev Performs a TinfoilCall, with all known protections
    ///     Against:
    ///     GasGriefing (burning all the gas)
    ///     Return and Revert Bombing (sending insane amounts of data to trigger memory expansion)
    ///     Self-Destruction of contract
    ///
    ///     Also attempts to protect against returning incorrect data
    ///    `excessivelySafeCall` is modified to only load data if the length is the expected one
    ///     This would avoid against receiving gibberish data, most often arrays
    function tinfoilCall(address _target, bytes memory _calldata) public returns (uint256) {
        // Cap gas at 2 MLN, we don't care about 1/64 cause we expect oracles to consume way less than 200k gas
        uint256 gasLeft = gasleft();
        uint256 cappedGas = gasLeft > GAS_LIMIT ? GAS_LIMIT : gasLeft;

        // NOTE: We could also just check for contract existence here to avoid more issues later

        (bool success, bytes memory res) = excessivelySafeCall(_target, cappedGas, 0, 32, _calldata);

        // Check of success and length allows to ignore checking for contract existence
        //  since non-existent contract cannot return value
        if (success && res.length == 32) {
            // Parse return value as uint256
            return abi.decode(res, (uint256));
        }

        return INVALID_PRICE;
    }

    /// @dev MODIFIED excessivelySafeCall to perform generic calls without getting gas bombed
    ///     Modified to only load the response if it has the intended length
    /// @custom:credits to: https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol
    function excessivelySafeCall(
        address _target,
        uint256 _gas,
        uint256 _value,
        uint16 _expectedLength,
        bytes memory _calldata
    ) internal returns (bool, bytes memory) {
        // set up for assembly call
        uint256 _receivedLength; // Length of data we receive
        bool _success;
        bytes memory _returnData = new bytes(_expectedLength);
        // dispatch message to recipient
        // by assembly calling "handle" function
        // we call via assembly to avoid memcopying a very large returndata
        // returned by a malicious contract
        assembly {
            _success := call(
                _gas, // gas
                _target, // recipient
                _value, // ether value
                add(_calldata, 0x20), // inloc
                mload(_calldata), // inlen
                0, // outloc
                0 // outlen
            )
            // limit our copy to 256 bytes
            _receivedLength := returndatasize()
            // NOTE: Read the data only if it's the expected length, else it must be some weird stuff
            if eq(_receivedLength, _expectedLength) {
                // Store the length of the copied bytes
                mstore(_returnData, _receivedLength)
                // copy the bytes from returndata[0:_receivedLength]
                returndatacopy(add(_returnData, 0x20), 0, _receivedLength)
            }
        }
        return (_success, _returnData);
    }
}

File 2 of 6 : AuthNoOwner.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

import {Authority} from "./Authority.sol";

/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Modified by BadgerDAO to remove owner
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
contract AuthNoOwner {
    event AuthorityUpdated(address indexed user, Authority indexed newAuthority);

    Authority private _authority;
    bool private _authorityInitialized;

    modifier requiresAuth() virtual {
        require(isAuthorized(msg.sender, msg.sig), "Auth: UNAUTHORIZED");

        _;
    }

    function authority() public view returns (Authority) {
        return _authority;
    }

    function authorityInitialized() public view returns (bool) {
        return _authorityInitialized;
    }

    function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
        Authority auth = _authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.

        // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
        // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
        return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig));
    }

    /// @notice Changed constructor to initialize to allow flexiblity of constructor vs initializer use
    /// @notice sets authorityInitiailzed flag to ensure only one use of
    function _initializeAuthority(address newAuthority) internal {
        require(address(_authority) == address(0), "Auth: authority is non-zero");
        require(!_authorityInitialized, "Auth: authority already initialized");

        _authority = Authority(newAuthority);
        _authorityInitialized = true;

        emit AuthorityUpdated(address(this), Authority(newAuthority));
    }
}

File 3 of 6 : Authority.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
    function canCall(address user, address target, bytes4 functionSig) external view returns (bool);
}

File 5 of 6 : IOracleCaller.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.17;

import "./IPool.sol";

interface IOracleCaller {
    function getLatestPrice() external view returns (uint256);
}

interface IPriceFetcher {
    function fetchPrice() external returns (uint256);
}

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

pragma solidity 0.8.17;

// Common interface for the Pools.
interface IPool {
    // --- Events ---

    event ETHBalanceUpdated(uint256 _newBalance);
    event EBTCBalanceUpdated(uint256 _newBalance);
    event CollSharesTransferred(address indexed _to, uint256 _amount);

    // --- Functions ---

    function getSystemCollShares() external view returns (uint256);

    function getSystemDebt() external view returns (uint256);

    function increaseSystemDebt(uint256 _amount) external;

    function decreaseSystemDebt(uint256 _amount) external;
}

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

pragma solidity 0.8.17;

interface IPriceFeed {
    // --- Events ---
    event LastGoodPriceUpdated(uint256 _lastGoodPrice);
    event PriceFeedStatusChanged(Status newStatus);
    event FallbackCallerChanged(
        address indexed _oldFallbackCaller,
        address indexed _newFallbackCaller
    );
    event UnhealthyFallbackCaller(address indexed _fallbackCaller, uint256 timestamp);
    event CollateralFeedSourceUpdated(address indexed stEthFeed);

    // --- Structs ---

    struct ChainlinkResponse {
        uint80 roundEthBtcId;
        uint80 roundStEthEthId;
        uint256 answer;
        uint256 timestampEthBtc;
        uint256 timestampStEthEth;
        bool success;
    }

    struct FallbackResponse {
        uint256 answer;
        uint256 timestamp;
        bool success;
    }

    // --- Enum ---

    enum Status {
        chainlinkWorking,
        usingFallbackChainlinkUntrusted,
        bothOraclesUntrusted,
        usingFallbackChainlinkFrozen,
        usingChainlinkFallbackUntrusted
    }

    // --- Function ---
    function fetchPrice() external returns (uint256);
}

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

Contract ABI

[{"inputs":[{"internalType":"address","name":"_authorityAddress","type":"address"},{"internalType":"address","name":"_primaryOracle","type":"address"},{"internalType":"address","name":"_secondaryOracle","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stEthFeed","type":"address"}],"name":"CollateralFeedSourceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_oldFallbackCaller","type":"address"},{"indexed":true,"internalType":"address","name":"_newFallbackCaller","type":"address"}],"name":"FallbackCallerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_lastGoodPrice","type":"uint256"}],"name":"LastGoodPriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum IPriceFeed.Status","name":"newStatus","type":"uint8"}],"name":"PriceFeedStatusChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_oldOracle","type":"address"},{"indexed":true,"internalType":"address","name":"_newOracle","type":"address"}],"name":"PrimaryOracleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_oldOracle","type":"address"},{"indexed":true,"internalType":"address","name":"_newOracle","type":"address"}],"name":"SecondaryOracleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_fallbackCaller","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"UnhealthyFallbackCaller","type":"event"},{"inputs":[],"name":"NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authorityInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fetchPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastGoodPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"primaryOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"secondaryOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newPrimary","type":"address"}],"name":"setPrimaryOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newSecondary","type":"address"}],"name":"setSecondaryOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_target","type":"address"},{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"tinfoilCall","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061009e5760003560e01c8063bd8f238e11610066578063bd8f238e14610143578063bf7e214f14610156578063d6e0c3b114610167578063daeac6611461017c578063f21880661461018f57600080fd5b80630490be83146100a35780630fdb11cf146100bf578063329d4609146100c757806397bc1e1b146100f2578063a3f4df7e1461010f575b600080fd5b6100ac60015481565b6040519081526020015b60405180910390f35b6100ac6101a2565b6003546100da906001600160a01b031681565b6040516001600160a01b0390911681526020016100b6565b600054600160a01b900460ff1660405190151581526020016100b6565b6101366040518060400160405280600881526020016711589d18d199595960c21b81525081565b6040516100b6919061074c565b6002546100da906001600160a01b031681565b6000546001600160a01b03166100da565b61017a6101753660046107b1565b610279565b005b6100ac61018a3660046107e9565b610403565b61017a61019d3660046107b1565b610477565b6002546040805160048152602481019091526020810180516001600160e01b0316630fdb11cf60e01b17905260009182916101e6916001600160a01b031690610403565b905080156101fc576101f7816105eb565b919050565b6003546001600160a01b031661021457505060015490565b6003546040805160048152602481019091526020810180516001600160e01b0316630fdb11cf60e01b179052600091610258916001600160a01b0390911690610403565b9050801561026f57610269816105eb565b92915050565b6001549250505090565b61028f336000356001600160e01b031916610626565b6102d55760405162461bcd60e51b8152602060048201526012602482015271105d5d1a0e8815539055551213d49256915160721b60448201526064015b60405180910390fd5b6001600160a01b038116156103a7576000816001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610326573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061034a91906108ab565b9050806103a55760405162461bcd60e51b8152602060048201526024808201527f45627463466565643a205365636f6e64617279204f7261636c65204d75737420604482015263576f726b60e01b60648201526084016102cc565b505b6003546040516001600160a01b038084169216907f2c4c913779ecebc8ffca138a72c7e93f12666da954410f8c7d110cd235eb322090600090a3600380546001600160a01b0319166001600160a01b0392909216919091179055565b6000805a90506000621e8480821161041b5781610420565b621e84805b90506000806104348784600060208a6106c5565b91509150818015610446575080516020145b1561046a578080602001905181019061045f91906108ab565b945050505050610269565b5060009695505050505050565b61048d336000356001600160e01b031916610626565b6104ce5760405162461bcd60e51b8152602060048201526012602482015271105d5d1a0e8815539055551213d49256915160721b60448201526064016102cc565b6000816001600160a01b0316630fdb11cf6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610510573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053491906108ab565b90508061058e5760405162461bcd60e51b815260206004820152602260248201527f45627463466565643a205072696d617279204f7261636c65204d75737420576f604482015261726b60f01b60648201526084016102cc565b6002546040516001600160a01b038085169216907ff467fa3ce302401d7e491c32f85c0664bd6d1d160b3f0907708bb7cbd9cf2c6f90600090a350600280546001600160a01b0319166001600160a01b0392909216919091179055565b60018190556040518181527f4d29de21de555af78a62fc82dd4bc05e9ae5b0660a37f04729527e0f22780cd39060200160405180910390a150565b600080546001600160a01b031680158015906106bd575060405163b700961360e01b81526001600160a01b0385811660048301523060248301526001600160e01b03198516604483015282169063b700961390606401602060405180830381865afa158015610699573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106bd91906108c4565b949350505050565b6000606060008060008661ffff1667ffffffffffffffff8111156106eb576106eb6107d3565b6040519080825280601f01601f191660200182016040528015610715576020820181803683370190505b5090506000808751602089018b8e8ef191503d925086830361073d57828152826000602083013e5b90999098509650505050505050565b600060208083528351808285015260005b818110156107795785810183015185820160400152820161075d565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b03811681146101f757600080fd5b6000602082840312156107c357600080fd5b6107cc8261079a565b9392505050565b634e487b7160e01b600052604160045260246000fd5b600080604083850312156107fc57600080fd5b6108058361079a565b9150602083013567ffffffffffffffff8082111561082257600080fd5b818501915085601f83011261083657600080fd5b813581811115610848576108486107d3565b604051601f8201601f19908116603f01168101908382118183101715610870576108706107d3565b8160405282815288602084870101111561088957600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000602082840312156108bd57600080fd5b5051919050565b6000602082840312156108d657600080fd5b815180151581146107cc57600080fdfea26469706673582212205e84e830cb7530f695645f96f74d59d46ca214eff2a75e126282e3c5df65900a64736f6c63430008110033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ 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.