S Price: $0.436618 (-0.12%)

Contract Diff Checker

Contract Name:
ChainlinkV3OracleConfig

Contract Source Code:

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import {IERC20Metadata} from "openzeppelin5/token/ERC20/extensions/IERC20Metadata.sol";
import {AggregatorV3Interface} from "chainlink/v0.8/interfaces/AggregatorV3Interface.sol";
import {ISiloOracle} from "silo-core/contracts/interfaces/ISiloOracle.sol";
import {IChainlinkV3Oracle} from "../interfaces/IChainlinkV3Oracle.sol";
import {Layer1OracleConfig} from "../_common/Layer1OracleConfig.sol";

contract ChainlinkV3OracleConfig is Layer1OracleConfig {
    /// @dev Chainlink aggregator
    AggregatorV3Interface internal immutable _AGGREGATOR; // solhint-disable-line var-name-mixedcase

    /// @dev secondary Chainlink aggregator to convert price to quote
    AggregatorV3Interface internal immutable _SECONDARY_AGGREGATOR; // solhint-disable-line var-name-mixedcase

    /// @dev Threshold used to determine if the price returned by the _SECONDARY_AGGREGATOR is valid
    uint256 internal immutable _SECONDARY_HEARTBEAT; // solhint-disable-line var-name-mixedcase

    /// @dev this can be set to true to convert primary price into price denominated in quote
    /// assuming that both AGGREGATORS providing price in the same token
    bool internal immutable _CONVERT_TO_QUOTE; // solhint-disable-line var-name-mixedcase

    /// @dev all verification should be done by factory
    constructor(
        IChainlinkV3Oracle.ChainlinkV3DeploymentConfig memory _config,
        uint256 _normalizationDivider,
        uint256 _normalizationMultiplier
    )
        Layer1OracleConfig(
            _config.baseToken,
            _config.quoteToken,
            _config.primaryHeartbeat,
            _normalizationDivider,
            _normalizationMultiplier
        )
    {
        _AGGREGATOR = _config.primaryAggregator;
        _SECONDARY_AGGREGATOR = _config.secondaryAggregator;
        _SECONDARY_HEARTBEAT = _config.secondaryHeartbeat;
        _CONVERT_TO_QUOTE = address(_config.secondaryAggregator) != address(0);
    }

    function getConfig() external view virtual returns (IChainlinkV3Oracle.ChainlinkV3Config memory config) {
        config.primaryAggregator = _AGGREGATOR;
        config.secondaryAggregator = _SECONDARY_AGGREGATOR;
        config.primaryHeartbeat = _HEARTBEAT;
        config.secondaryHeartbeat = _SECONDARY_HEARTBEAT;
        config.normalizationDivider = _DECIMALS_NORMALIZATION_DIVIDER;
        config.normalizationMultiplier = _DECIMALS_NORMALIZATION_MULTIPLIER;
        config.baseToken = _BASE_TOKEN;
        config.quoteToken = _QUOTE_TOKEN;
        config.convertToQuote = _CONVERT_TO_QUOTE;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

  function description() external view returns (string memory);

  function version() external view returns (uint256);

  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);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

interface ISiloOracle {
    /// @notice Hook function to call before `quote` function reads price
    /// @dev This hook function can be used to change state right before the price is read. For example it can be used
    ///      for curve read only reentrancy protection. In majority of implementations this will be an empty function.
    ///      WARNING: reverts are propagated to Silo so if `beforeQuote` reverts, Silo reverts as well.
    /// @param _baseToken Address of priced token
    function beforeQuote(address _baseToken) external;

    /// @return quoteAmount Returns quote price for _baseAmount of _baseToken
    /// @param _baseAmount Amount of priced token
    /// @param _baseToken Address of priced token
    function quote(uint256 _baseAmount, address _baseToken) external view returns (uint256 quoteAmount);

    /// @return address of token in which quote (price) is denominated
    function quoteToken() external view returns (address);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IERC20Metadata} from "openzeppelin5/token/ERC20/extensions/IERC20Metadata.sol";
import {AggregatorV3Interface} from "chainlink/v0.8/interfaces/AggregatorV3Interface.sol";
import {ChainlinkV3OracleConfig} from "../chainlinkV3/ChainlinkV3OracleConfig.sol";

interface IChainlinkV3Oracle {
    /// @dev config based on which new oracle will be deployed
    /// @notice there is no way to check if aggregators match tokens, so it is users job to verify config.
    /// @param primaryAggregator used to read price from chainlink, if it can not provide price in quote token,
    /// then you have to setup secondary one that will do the job
    /// @param secondaryAggregator if set, it is used translate primary price into quote price eg:
    /// primary price is ABC/USD and secondary is ETH/USD, then result will be price in ABC/ETH
    /// @param baseToken base token address, it must have decimals() method available
    /// @param quoteToken quote toke address, it must have decimals() method available
    /// @param primaryHeartbeat heartbeat of primary price
    /// @param secondaryHeartbeat heartbeat of secondary price
    struct ChainlinkV3DeploymentConfig {
        IERC20Metadata baseToken;
        IERC20Metadata quoteToken;
        AggregatorV3Interface primaryAggregator;
        uint32 primaryHeartbeat;
        AggregatorV3Interface secondaryAggregator;
        uint32 secondaryHeartbeat;
    }

    /// @dev config based on which new oracle will be deployed
    /// @notice there is no way to check if aggregators match tokens, so it is users job to verify config.
    /// @param primaryAggregator used to read price from chainlink, if it can not provide price in quote token,
    /// then you have to setup secondary one that will do the job
    /// @param secondaryAggregator if set, it is used translate primary price into quote price eg:
    /// primary price is ABC/USD and secondary is ETH/USD, then result will be price in ABC/ETH
    /// @param baseToken base token address, it must have decimals() method available
    /// @param quoteToken quote toke address, it must have decimals() method available
    /// @param primaryHeartbeat heartbeat of primary price
    /// @param secondaryHeartbeat heartbeat of secondary price
    struct ChainlinkV3Config {
        AggregatorV3Interface primaryAggregator;
        AggregatorV3Interface secondaryAggregator;
        uint256 primaryHeartbeat;
        uint256 secondaryHeartbeat;
        uint256 normalizationDivider;
        uint256 normalizationMultiplier;
        IERC20Metadata baseToken;
        IERC20Metadata quoteToken;
        bool convertToQuote;
    }

    event ChainlinkV3ConfigDeployed(ChainlinkV3OracleConfig configAddress);

    event NewAggregator(address indexed asset, AggregatorV3Interface indexed aggregator, bool convertToQuote);
    event NewHeartbeat(address indexed asset, uint256 heartbeat);
    event NewQuoteAggregatorHeartbeat(uint256 heartbeat);
    event AggregatorDisabled(address indexed asset, AggregatorV3Interface indexed aggregator);

    error AddressZero();
    error InvalidPrice();
    error InvalidSecondPrice();
    error BaseAmountOverflow();
    error TokensAreTheSame();
    error AggregatorsAreTheSame();

    error QuoteTokenNotMatchEth();
    error InvalidEthAggregatorDecimals();
    error InvalidHeartbeat();
    error InvalidEthHeartbeat();

    error AssetNotSupported();
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {IERC20Metadata} from "openzeppelin5/token/ERC20/extensions/IERC20Metadata.sol";

/// @notice to keep config contract size low (this is the one that will be deployed each time)
/// factory contract take over verification. You should not deploy or use config that was not created by factory.
/// @dev This is common config for Layer1 oracles
abstract contract Layer1OracleConfig {
    /// @dev price must be updated at least once every `_HEARTBEAT` seconds, otherwise something is wrong
    uint256 internal immutable _HEARTBEAT; // solhint-disable-line var-name-mixedcase

    /// @dev constant used for normalising price
    uint256 internal immutable _DECIMALS_NORMALIZATION_DIVIDER; // solhint-disable-line var-name-mixedcase

    /// @dev constant used for normalising price
    uint256 internal immutable _DECIMALS_NORMALIZATION_MULTIPLIER; // solhint-disable-line var-name-mixedcase

    IERC20Metadata internal immutable _BASE_TOKEN; // solhint-disable-line var-name-mixedcase
    IERC20Metadata internal immutable _QUOTE_TOKEN; // solhint-disable-line var-name-mixedcase

    /// @dev all verification should be done by factory
    constructor(
        IERC20Metadata _baseToken,
        IERC20Metadata _quoteToken,
        uint256 _heartbeat,
        uint256 _normalizationDivider,
        uint256 _normalizationMultiplier
    ) {
        _DECIMALS_NORMALIZATION_DIVIDER = _normalizationDivider;
        _DECIMALS_NORMALIZATION_MULTIPLIER = _normalizationMultiplier;

        _BASE_TOKEN = _baseToken;
        _QUOTE_TOKEN = _quoteToken;

        _HEARTBEAT = _heartbeat;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @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);

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

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

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

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

Please enter a contract address above to load the contract details and source code.

Context size (optional):