Overview
S Balance
S Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Create | 5130674 | 16 days ago | IN | 0 S | 0.01818325 |
Latest 3 internal transactions
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
5130674 | 16 days ago | Contract Creation | 0 S | |||
5130674 | 16 days ago | Contract Creation | 0 S | |||
5127485 | 16 days ago | Contract Creation | 0 S |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
ChainlinkV3OracleFactory
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity 0.8.28; import {IERC20Metadata} from "openzeppelin5/token/ERC20/extensions/IERC20Metadata.sol"; import {Clones} from "openzeppelin5/proxy/Clones.sol"; import {AggregatorV3Interface} from "chainlink/v0.8/interfaces/AggregatorV3Interface.sol"; import {OracleFactory} from "../_common/OracleFactory.sol"; import {IChainlinkV3Oracle} from "../interfaces/IChainlinkV3Oracle.sol"; import {ChainlinkV3Oracle} from "../chainlinkV3/ChainlinkV3Oracle.sol"; import {ChainlinkV3OracleConfig} from "../chainlinkV3/ChainlinkV3OracleConfig.sol"; import {OracleNormalization} from "../lib/OracleNormalization.sol"; contract ChainlinkV3OracleFactory is OracleFactory { constructor() OracleFactory(address(new ChainlinkV3Oracle())) { // noting to configure } function create(IChainlinkV3Oracle.ChainlinkV3DeploymentConfig memory _config) external virtual returns (ChainlinkV3Oracle oracle) { bytes32 id = hashConfig(_config); ChainlinkV3OracleConfig oracleConfig = ChainlinkV3OracleConfig(getConfigAddress[id]); if (address(oracleConfig) != address(0)) { // config already exists, so oracle exists as well return ChainlinkV3Oracle(getOracleAddress[address(oracleConfig)]); } verifyConfig(_config); verifyHeartbeat(_config); oracleConfig = new ChainlinkV3OracleConfig(_config); oracle = ChainlinkV3Oracle(Clones.clone(ORACLE_IMPLEMENTATION)); _saveOracle(address(oracle), address(oracleConfig), id); oracle.initialize(oracleConfig); } function hashConfig(IChainlinkV3Oracle.ChainlinkV3DeploymentConfig memory _config) public virtual view returns (bytes32 configId) { configId = keccak256(abi.encode(_config)); } function verifyConfig(IChainlinkV3Oracle.ChainlinkV3DeploymentConfig memory _config) public view virtual returns (uint256 secondaryPriceDecimals) { if (address(_config.quoteToken) == address(0)) revert IChainlinkV3Oracle.AddressZero(); if (address(_config.baseToken) == address(0)) revert IChainlinkV3Oracle.AddressZero(); if (address(_config.quoteToken) == address(_config.baseToken)) revert IChainlinkV3Oracle.TokensAreTheSame(); if (address(_config.primaryAggregator) == address(0)) revert IChainlinkV3Oracle.AddressZero(); if (address(_config.primaryAggregator) == address(_config.secondaryAggregator)) { revert IChainlinkV3Oracle.AggregatorsAreTheSame(); } if (address(_config.secondaryAggregator) != address(0)) { secondaryPriceDecimals = _config.secondaryAggregator.decimals(); } if (_config.normalizationDivider > 1e36) revert IChainlinkV3Oracle.HugeDivider(); if (_config.normalizationMultiplier > 1e36) revert IChainlinkV3Oracle.HugeMultiplier(); if (_config.normalizationDivider == 0 && _config.normalizationMultiplier == 0) { revert IChainlinkV3Oracle.MultiplierAndDividerZero(); } } /// @dev heartbeat restrictions are arbitrary /// @notice Chainlink's heartbeat is "always" less than a day, except when they late function verifyHeartbeat(IChainlinkV3Oracle.ChainlinkV3DeploymentConfig memory _config) public pure virtual { if (_config.primaryHeartbeat < 30 seconds || _config.primaryHeartbeat > 2 days) { revert IChainlinkV3Oracle.InvalidHeartbeat(); } if (address(_config.secondaryAggregator) == address(0)) { if (_config.secondaryHeartbeat != 0) revert IChainlinkV3Oracle.InvalidEthHeartbeat(); } else { if (_config.secondaryHeartbeat < 30 seconds || _config.secondaryHeartbeat > 2 days) { revert IChainlinkV3Oracle.InvalidEthHeartbeat(); } } } }
// 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 // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Clones.sol) pragma solidity ^0.8.20; import {Errors} from "../utils/Errors.sol"; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. */ library Clones { /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create opcode, which should never revert. */ function clone(address implementation) internal returns (address instance) { return clone(implementation, 0); } /** * @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency * to the new contract. * * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory) * to always have enough balance for new deployments. Consider exposing this function under a payable method. */ function clone(address implementation, uint256 value) internal returns (address instance) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } /// @solidity memory-safe-assembly assembly { // Stores the bytecode after address mstore(0x20, 0x5af43d82803e903d91602b57fd5bf3) // implementation address mstore(0x11, implementation) // Packs the first 3 bytes of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0x88, implementation), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) instance := create(value, 0x09, 0x37) } if (instance == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple time will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { return cloneDeterministic(implementation, salt, 0); } /** * @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with * a `value` parameter to send native currency to the new contract. * * NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory) * to always have enough balance for new deployments. Consider exposing this function under a payable method. */ function cloneDeterministic( address implementation, bytes32 salt, uint256 value ) internal returns (address instance) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } /// @solidity memory-safe-assembly assembly { // Stores the bytecode after address mstore(0x20, 0x5af43d82803e903d91602b57fd5bf3) // implementation address mstore(0x11, implementation) // Packs the first 3 bytes of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0x88, implementation), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) instance := create2(value, 0x09, 0x37, salt) } if (instance == address(0)) { revert Errors.FailedDeployment(); } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := keccak256(add(ptr, 0x43), 0x55) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt ) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } }
// 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: BUSL-1.1 pragma solidity >=0.7.6 <0.9.0; abstract contract OracleFactory { /// @dev implementation that will be cloned address public immutable ORACLE_IMPLEMENTATION; // solhint-disable-line var-name-mixedcase /// @dev hash(config) => oracle contract /// oracle ID is determine by initial configuration, the logic is the same, so config is the only difference /// that's why we can use it as ID, at the same time we can detect duplicated and save gas by reusing same config /// multiple times mapping(bytes32 => address) public getConfigAddress; /// @dev config address => oracle address mapping(address => address) public getOracleAddress; /// @dev config ID and config address should be easily accessible directly from oracle contract event NewOracle(address indexed oracle); constructor(address _oracleImplementation) { if (_oracleImplementation == address(0)) revert("ZeroAddress"); ORACLE_IMPLEMENTATION = _oracleImplementation; } /// @dev execute this method from target factory, to save ID and update mappings /// @param _newOracle new oracle address /// @param _newConfig oracle config address /// @param _configId oracle config ID, hash(config) function _saveOracle(address _newOracle, address _newConfig, bytes32 _configId) internal virtual { if (getConfigAddress[_configId] != address(0)) revert("ConfigAlreadyExist"); getConfigAddress[_configId] = _newConfig; // config and oracle is 1:1 so no need to check if oracle exists getOracleAddress[_newConfig] = _newOracle; emit NewOracle(_newOracle); } }
// 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 /// @param normalizationDivider divider that will be used in oracle to normalize price /// @param normalizationMultiplier multiplier that will be used in oracle to normalize price /// @param invertSecondPrice in case we using second price, this flag will tell us if we need to 1/secondPrice struct ChainlinkV3DeploymentConfig { IERC20Metadata baseToken; IERC20Metadata quoteToken; AggregatorV3Interface primaryAggregator; uint32 primaryHeartbeat; AggregatorV3Interface secondaryAggregator; uint32 secondaryHeartbeat; uint256 normalizationDivider; uint256 normalizationMultiplier; bool invertSecondPrice; } /// @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 /// @param invertSecondPrice in case we using second price, this flag will tell us if we need to 1/secondPrice struct ChainlinkV3Config { AggregatorV3Interface primaryAggregator; AggregatorV3Interface secondaryAggregator; uint256 primaryHeartbeat; uint256 secondaryHeartbeat; uint256 normalizationDivider; uint256 normalizationMultiplier; IERC20Metadata baseToken; IERC20Metadata quoteToken; bool convertToQuote; bool invertSecondPrice; } 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 ZeroQuote(); error InvalidSecondPrice(); error BaseAmountOverflow(); error TokensAreTheSame(); error AggregatorsAreTheSame(); error QuoteTokenNotMatchEth(); error InvalidEthAggregatorDecimals(); error InvalidHeartbeat(); error InvalidEthHeartbeat(); error AssetNotSupported(); error HugeDivider(); error HugeMultiplier(); error MultiplierAndDividerZero(); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity 0.8.28; import {Initializable} from "openzeppelin5-upgradeable/proxy/utils/Initializable.sol"; 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 {OracleNormalization} from "../lib/OracleNormalization.sol"; import {ChainlinkV3OracleConfig} from "./ChainlinkV3OracleConfig.sol"; import {IChainlinkV3Oracle} from "../interfaces/IChainlinkV3Oracle.sol"; contract ChainlinkV3Oracle is IChainlinkV3Oracle, ISiloOracle, Initializable { ChainlinkV3OracleConfig public oracleConfig; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } /// @notice validation of config is checked in factory, therefore you should not deploy and initialize directly /// use factory always. function initialize(ChainlinkV3OracleConfig _configAddress) external virtual initializer { oracleConfig = _configAddress; emit ChainlinkV3ConfigDeployed(_configAddress); } /// @inheritdoc ISiloOracle function quote(uint256 _baseAmount, address _baseToken) external view virtual returns (uint256 quoteAmount) { ChainlinkV3Config memory config = oracleConfig.getConfig(); if (_baseToken != address(config.baseToken)) revert AssetNotSupported(); if (_baseAmount > type(uint128).max) revert BaseAmountOverflow(); (bool success, uint256 price) = _getAggregatorPrice(config.primaryAggregator, config.primaryHeartbeat); if (!success) revert InvalidPrice(); if (!config.convertToQuote) { quoteAmount = OracleNormalization.normalizePrice( _baseAmount, price, config.normalizationDivider, config.normalizationMultiplier ); if (quoteAmount == 0) revert ZeroQuote(); return quoteAmount; } ( bool secondSuccess, uint256 secondPrice ) = _getAggregatorPrice(config.secondaryAggregator, config.secondaryHeartbeat); if (!secondSuccess) revert InvalidSecondPrice(); quoteAmount = OracleNormalization.normalizePrices( _baseAmount, price, secondPrice, config.normalizationDivider, config.normalizationMultiplier, config.invertSecondPrice ); if (quoteAmount == 0) revert ZeroQuote(); return quoteAmount; } /// @dev Returns price directly from aggregator, this method is mostly for debug purposes function getAggregatorPrice(bool _primary) external view virtual returns (bool success, uint256 price) { IChainlinkV3Oracle.ChainlinkV3Config memory config = oracleConfig.getConfig(); return _primary ? _getAggregatorPrice(config.primaryAggregator, config.primaryHeartbeat) : _getAggregatorPrice(config.secondaryAggregator, config.secondaryHeartbeat); } /// @inheritdoc ISiloOracle function quoteToken() external view virtual returns (address) { IChainlinkV3Oracle.ChainlinkV3Config memory config = oracleConfig.getConfig(); return address(config.quoteToken); } function beforeQuote(address) external pure virtual override { // nothing to execute } function _getAggregatorPrice(AggregatorV3Interface _aggregator, uint256 _heartbeat) internal view virtual returns (bool success, uint256 price) { ( /*uint80 roundID*/, int256 aggregatorPrice, /*uint256 startedAt*/, uint256 priceTimestamp, /*uint80 answeredInRound*/ ) = _aggregator.latestRoundData(); // price must be updated at least once every _heartbeat, otherwise something is wrong uint256 oldestAcceptedPriceTimestamp; // block.timestamp is more than HEARTBEAT, so we can not underflow unchecked { oldestAcceptedPriceTimestamp = block.timestamp - _heartbeat; } if (aggregatorPrice > 0 && priceTimestamp > oldestAcceptedPriceTimestamp) { return (true, uint256(aggregatorPrice)); } return (false, 0); } }
// 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 If TRUE price will be 1/price bool internal immutable _INVERT_SECONDARY_PRICE; // solhint-disable-line var-name-mixedcase /// @dev all verification should be done by factory constructor(IChainlinkV3Oracle.ChainlinkV3DeploymentConfig memory _config) Layer1OracleConfig( _config.baseToken, _config.quoteToken, _config.primaryHeartbeat, _config.normalizationDivider, _config.normalizationMultiplier ) { _AGGREGATOR = _config.primaryAggregator; _SECONDARY_AGGREGATOR = _config.secondaryAggregator; _SECONDARY_HEARTBEAT = _config.secondaryHeartbeat; _CONVERT_TO_QUOTE = address(_config.secondaryAggregator) != address(0); _INVERT_SECONDARY_PRICE = _config.invertSecondPrice; } 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; config.invertSecondPrice = _INVERT_SECONDARY_PRICE; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.20; import {IERC20Metadata} from "openzeppelin5/token/ERC20/extensions/IERC20Metadata.sol"; import {TokenHelper} from "silo-core/contracts/lib/TokenHelper.sol"; /// @notice please read carefully unchecked comments, there are some requirements tht must be met in order to not /// over/under flow /// @dev Rounding error policy. /// We're always rounding down by using build-in solidity way for division. /// /// During normalization we're executing division by `_normalizationDivider` (unless there is multiplicator) /// and `_secondPrice` (in case second price exist). You can expect rounding errors to be in exclusive range of (0, 1) /// when doing division. What does it means? This means, that you can be short by up to 1 wei on result. /// eg. when normalising 12_345 (value with 3 decimals) to 2 decimals representation you lose last digit and end result /// will be 12_34. /// What are consequences for protocol? /// Eg. if 987 of tokens A is worth 12.34 tokens B (after normalization), by losing 0.005 we made tokens A worth a bit /// more than they really are. If we would round up, then tokens A would be a bit less expensive. /// Keep in mind we are talking tiny values. There is no argument that can tell which approach is correct. /// Considering that prices themselves are changing constantly (if you think about it, they are just random numbers /// close to previous value) and even TWAP price can be manipulated up to some level, if we compare this to rounding /// error, the rounding error has no meaning at all. /// Most important part is: how are we using prices in Silo and how rounding error affects the system? /// We're using prices to calculate LTV. We're deciding how much of token you can borrow but once you borrow you need to /// repay that amount (plus interest). Price of the token has no influence on how much you repay. /// Price change by 1 wei can also trigger liquidation, but it will be like switching from eg. 9,99999999999% => 10%. /// Summing up, rounding error can affect: /// - max amount of tokens one can borrow /// - when liquidation happen /// nn both cases we are talking about 1 wei of difference and this really does not matter to the protocol. /// It cannot make user to repay less than he borrow and it cannot affect any other operations like deposit, /// withdraw in a way, that you get less/more tokens. /// That said, choosing rounding policy is arbitrary decision and our decision is to use default rounding down. library OracleNormalization { error Overflow(); /// @notice if you call normalizePrice directly you can create overflow /// @param _baseAmount amount of base token (can not be higher than uint128!) /// @param _assetPrice price returned by oracle (can not be higher than uint128!) /// @param _normalizationDivider constant that allows to translate output price to expected decimals /// @param _normalizationMultiplier constant that allows to translate output price to expected decimals /// @return assetPrice uint256 18 decimals price function normalizePrice( uint256 _baseAmount, uint256 _assetPrice, uint256 _normalizationDivider, uint256 _normalizationMultiplier ) internal pure returns (uint256 assetPrice) { if (_normalizationMultiplier == 0) { // `_baseAmount * _assetPrice` is safe because we multiply uint128 * uint128 // - _baseAmount is checked in `_quote` // - _assetPrice is uint128 // div is safe unchecked { return _baseAmount * _assetPrice / _normalizationDivider; } } uint256 mul; // this is save, check explanation above unchecked { mul = _baseAmount * _assetPrice; } return mul * _normalizationMultiplier; } /// @notice if you call normalizePrice directly you can create overflow /// @param _baseAmount amount of base token (can not be higher than uint128!) /// @param _assetPrice price returned by oracle (can not be higher than uint128!) /// @param _secondPrice price of quote token denominated in same token as _assetPrice /// (can not be higher than uint128!) /// @param _normalizationDivider constant that allows to translate output price to expected decimals /// @param _normalizationMultiplier constant that allows to translate output price to expected decimals /// @param _invertSecondPrice if TRUE we have to 1/secondPrice /// @return assetPrice uint256 18 decimals price function normalizePrices( uint256 _baseAmount, uint256 _assetPrice, uint256 _secondPrice, uint256 _normalizationDivider, uint256 _normalizationMultiplier, bool _invertSecondPrice ) internal pure returns (uint256 assetPrice) { if (_normalizationMultiplier == 0) { // `_baseAmount * _assetPrice` is safe because we multiply uint128 * uint128 // - _baseAmount is checked in `_quote`, that checks covers `*1e8`, so we sure it is up to uint128 // - _assetPrice is uint128 // however if you call normalizePrice directly (because it is open method) you can create overflow // div is safe unchecked { assetPrice = _baseAmount * _assetPrice; assetPrice = _invertSecondPrice ? assetPrice / _secondPrice : assetPrice * _secondPrice; assetPrice = assetPrice / _normalizationDivider; } return assetPrice; } // this is save, check explanation above unchecked { assetPrice = _baseAmount * _assetPrice; } assetPrice = assetPrice * _normalizationMultiplier; return _invertSecondPrice ? assetPrice / _secondPrice : assetPrice * _secondPrice; } }
// 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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// 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.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: BUSL-1.1 pragma solidity ^0.8.28; import {IERC20Metadata} from "openzeppelin5/token/ERC20/extensions/IERC20Metadata.sol"; import {IsContract} from "./IsContract.sol"; library TokenHelper { uint256 private constant _BYTES32_SIZE = 32; error TokenIsNotAContract(); function assertAndGetDecimals(address _token) internal view returns (uint256) { (bool hasMetadata, bytes memory data) = _tokenMetadataCall(_token, abi.encodeCall(IERC20Metadata.decimals, ())); // decimals() is optional in the ERC20 standard, so if metadata is not accessible // we assume there are no decimals and use 0. if (!hasMetadata) { return 0; } return abi.decode(data, (uint8)); } /// @dev Returns the symbol for the provided ERC20 token. /// An empty string is returned if the call to the token didn't succeed. /// @param _token address of the token to get the symbol for /// @return assetSymbol the token symbol function symbol(address _token) internal view returns (string memory assetSymbol) { (bool hasMetadata, bytes memory data) = _tokenMetadataCall(_token, abi.encodeCall(IERC20Metadata.symbol, ())); if (!hasMetadata || data.length == 0) { return "?"; } else if (data.length == _BYTES32_SIZE) { return string(removeZeros(data)); } else { return abi.decode(data, (string)); } } /// @dev Removes bytes with value equal to 0 from the provided byte array. /// @param _data byte array from which to remove zeroes /// @return result byte array with zeroes removed function removeZeros(bytes memory _data) internal pure returns (bytes memory result) { uint256 n = _data.length; for (uint256 i; i < n; i++) { if (_data[i] == 0) continue; result = abi.encodePacked(result, _data[i]); } } /// @dev Performs a staticcall to the token to get its metadata (symbol, decimals, name) function _tokenMetadataCall(address _token, bytes memory _data) private view returns (bool, bytes memory) { // We need to do this before the call, otherwise the call will succeed even for EOAs require(IsContract.isContract(_token), TokenIsNotAContract()); (bool success, bytes memory result) = _token.staticcall(_data); // If the call reverted we assume the token doesn't follow the metadata extension if (!success) { return (false, ""); } return (true, result); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.24; library IsContract { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address _account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return _account.code.length > 0; } }
{ "remappings": [ "forge-std/=gitmodules/forge-std/src/", "silo-foundry-utils/=gitmodules/silo-foundry-utils/contracts/", "properties/=gitmodules/crytic/properties/contracts/", "silo-core/=silo-core/", "silo-oracles/=silo-oracles/", "silo-vaults/=silo-vaults/", "ve-silo/=ve-silo/", "@openzeppelin/=gitmodules/openzeppelin-contracts-5/contracts/", "morpho-blue/=gitmodules/morpho-blue/src/", "openzeppelin5/=gitmodules/openzeppelin-contracts-5/contracts/", "openzeppelin5-upgradeable/=gitmodules/openzeppelin-contracts-upgradeable-5/contracts/", "chainlink/=gitmodules/chainlink/contracts/src/", "chainlink-ccip/=gitmodules/chainlink-ccip/contracts/src/", "uniswap/=gitmodules/uniswap/", "@uniswap/v3-core/=gitmodules/uniswap/v3-core/", "balancer-labs/v2-solidity-utils/=external/balancer-v2-monorepo/pkg/solidity-utils/contracts/", "balancer-labs/v2-interfaces/=external/balancer-v2-monorepo/pkg/interfaces/contracts/", "balancer-labs/v2-liquidity-mining/=external/balancer-v2-monorepo/pkg/liquidity-mining/contracts/", "pyth-sdk-solidity/=gitmodules/pyth-sdk-solidity/target_chains/ethereum/sdk/solidity/", "@balancer-labs/=node_modules/@balancer-labs/", "@ensdomains/=node_modules/@ensdomains/", "@openzeppelin/contracts-upgradeable/=gitmodules/openzeppelin-contracts-upgradeable-5/contracts/", "@openzeppelin/contracts/=gitmodules/openzeppelin-contracts-5/contracts/", "@solidity-parser/=node_modules/@solidity-parser/", "ERC4626/=gitmodules/crytic/properties/lib/ERC4626/contracts/", "aave/=gitmodules/aave/", "createx/=gitmodules/pyth-sdk-solidity/lazer/contracts/evm/lib/createx/src/", "crytic/=gitmodules/crytic/", "ds-test/=gitmodules/openzeppelin-contracts-5/lib/forge-std/lib/ds-test/src/", "erc4626-tests/=gitmodules/openzeppelin-contracts-5/lib/erc4626-tests/", "halmos-cheatcodes/=gitmodules/morpho-blue/lib/halmos-cheatcodes/src/", "hardhat/=node_modules/hardhat/", "layer-zero-examples/=gitmodules/layer-zero-examples/contracts/", "lz_gauges/=gitmodules/lz_gauges/contracts/", "openzeppelin-contracts-5/=gitmodules/openzeppelin-contracts-5/", "openzeppelin-contracts-upgradeable-5/=gitmodules/openzeppelin-contracts-upgradeable-5/", "openzeppelin-contracts-upgradeable/=gitmodules/openzeppelin-contracts-upgradeable/", "[email protected]/=gitmodules/[email protected]/contracts/", "openzeppelin-contracts/=gitmodules/openzeppelin-contracts/", "openzeppelin/=gitmodules/openzeppelin-contracts-upgradeable/contracts/", "prettier-plugin-solidity/=node_modules/prettier-plugin-solidity/", "proposals/=node_modules/proposals/", "solady/=gitmodules/pyth-sdk-solidity/lazer/contracts/evm/lib/createx/lib/solady/", "solmate/=gitmodules/crytic/properties/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": "cancun", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[],"name":"AggregatorsAreTheSame","type":"error"},{"inputs":[],"name":"FailedDeployment","type":"error"},{"inputs":[],"name":"HugeDivider","type":"error"},{"inputs":[],"name":"HugeMultiplier","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidEthHeartbeat","type":"error"},{"inputs":[],"name":"InvalidHeartbeat","type":"error"},{"inputs":[],"name":"MultiplierAndDividerZero","type":"error"},{"inputs":[],"name":"TokensAreTheSame","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oracle","type":"address"}],"name":"NewOracle","type":"event"},{"inputs":[],"name":"ORACLE_IMPLEMENTATION","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20Metadata","name":"baseToken","type":"address"},{"internalType":"contract IERC20Metadata","name":"quoteToken","type":"address"},{"internalType":"contract AggregatorV3Interface","name":"primaryAggregator","type":"address"},{"internalType":"uint32","name":"primaryHeartbeat","type":"uint32"},{"internalType":"contract AggregatorV3Interface","name":"secondaryAggregator","type":"address"},{"internalType":"uint32","name":"secondaryHeartbeat","type":"uint32"},{"internalType":"uint256","name":"normalizationDivider","type":"uint256"},{"internalType":"uint256","name":"normalizationMultiplier","type":"uint256"},{"internalType":"bool","name":"invertSecondPrice","type":"bool"}],"internalType":"struct IChainlinkV3Oracle.ChainlinkV3DeploymentConfig","name":"_config","type":"tuple"}],"name":"create","outputs":[{"internalType":"contract ChainlinkV3Oracle","name":"oracle","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"getConfigAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getOracleAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20Metadata","name":"baseToken","type":"address"},{"internalType":"contract IERC20Metadata","name":"quoteToken","type":"address"},{"internalType":"contract AggregatorV3Interface","name":"primaryAggregator","type":"address"},{"internalType":"uint32","name":"primaryHeartbeat","type":"uint32"},{"internalType":"contract AggregatorV3Interface","name":"secondaryAggregator","type":"address"},{"internalType":"uint32","name":"secondaryHeartbeat","type":"uint32"},{"internalType":"uint256","name":"normalizationDivider","type":"uint256"},{"internalType":"uint256","name":"normalizationMultiplier","type":"uint256"},{"internalType":"bool","name":"invertSecondPrice","type":"bool"}],"internalType":"struct IChainlinkV3Oracle.ChainlinkV3DeploymentConfig","name":"_config","type":"tuple"}],"name":"hashConfig","outputs":[{"internalType":"bytes32","name":"configId","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20Metadata","name":"baseToken","type":"address"},{"internalType":"contract IERC20Metadata","name":"quoteToken","type":"address"},{"internalType":"contract AggregatorV3Interface","name":"primaryAggregator","type":"address"},{"internalType":"uint32","name":"primaryHeartbeat","type":"uint32"},{"internalType":"contract AggregatorV3Interface","name":"secondaryAggregator","type":"address"},{"internalType":"uint32","name":"secondaryHeartbeat","type":"uint32"},{"internalType":"uint256","name":"normalizationDivider","type":"uint256"},{"internalType":"uint256","name":"normalizationMultiplier","type":"uint256"},{"internalType":"bool","name":"invertSecondPrice","type":"bool"}],"internalType":"struct IChainlinkV3Oracle.ChainlinkV3DeploymentConfig","name":"_config","type":"tuple"}],"name":"verifyConfig","outputs":[{"internalType":"uint256","name":"secondaryPriceDecimals","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20Metadata","name":"baseToken","type":"address"},{"internalType":"contract IERC20Metadata","name":"quoteToken","type":"address"},{"internalType":"contract AggregatorV3Interface","name":"primaryAggregator","type":"address"},{"internalType":"uint32","name":"primaryHeartbeat","type":"uint32"},{"internalType":"contract AggregatorV3Interface","name":"secondaryAggregator","type":"address"},{"internalType":"uint32","name":"secondaryHeartbeat","type":"uint32"},{"internalType":"uint256","name":"normalizationDivider","type":"uint256"},{"internalType":"uint256","name":"normalizationMultiplier","type":"uint256"},{"internalType":"bool","name":"invertSecondPrice","type":"bool"}],"internalType":"struct IChainlinkV3Oracle.ChainlinkV3DeploymentConfig","name":"_config","type":"tuple"}],"name":"verifyHeartbeat","outputs":[],"stateMutability":"pure","type":"function"}]
Contract Creation Code
60a0604052348015600e575f5ffd5b506040516019906089565b604051809103905ff0801580156031573d5f5f3e3d5ffd5b506001600160a01b03811660795760405162461bcd60e51b815260206004820152600b60248201526a5a65726f4164647265737360a81b604482015260640160405180910390fd5b6001600160a01b03166080526096565b610a0780610f3583390190565b608051610e816100b45f395f818160fc01526105070152610e815ff3fe608060405234801561000f575f5ffd5b506004361061007a575f3560e01c8063a8f39f6611610058578063a8f39f66146100f7578063b0b1f5851461011e578063f896503a14610133578063fe58f4b91461015b575f5ffd5b806328e981661461007e57806354482173146100a45780637af14d6e146100e4575b5f5ffd5b61009161008c366004610788565b61016e565b6040519081526020015b60405180910390f35b6100cc6100b2366004610831565b60016020525f90815260409020546001600160a01b031681565b6040516001600160a01b03909116815260200161009b565b6100916100f2366004610788565b61037d565b6100cc7f000000000000000000000000000000000000000000000000000000000000000081565b61013161012c366004610788565b6103ac565b005b6100cc610141366004610853565b5f602081905290815260409020546001600160a01b031681565b6100cc610169366004610788565b610470565b60208101515f906001600160a01b031661019b57604051639fabe1c160e01b815260040160405180910390fd5b81516001600160a01b03166101c357604051639fabe1c160e01b815260040160405180910390fd5b815f01516001600160a01b031682602001516001600160a01b0316036101fc57604051633d4cfcc560e11b815260040160405180910390fd5b60408201516001600160a01b031661022757604051639fabe1c160e01b815260040160405180910390fd5b81608001516001600160a01b031682604001516001600160a01b03160361026157604051630e04002960e11b815260040160405180910390fd5b60808201516001600160a01b0316156102de5781608001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102b4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102d8919061086a565b60ff1690505b6ec097ce7bc90715b34b9f10000000008260c00151111561031257604051631fe0fdf560e11b815260040160405180910390fd5b6ec097ce7bc90715b34b9f10000000008260e00151111561034657604051631f604a8560e21b815260040160405180910390fd5b60c082015115801561035a575060e0820151155b156103785760405163d156ee7f60e01b815260040160405180910390fd5b919050565b5f8160405160200161038f919061088a565b604051602081830303815290604052805190602001209050919050565b601e816060015163ffffffff1610806103d157506202a300816060015163ffffffff16115b156103ef57604051638f1825c960e01b815260040160405180910390fd5b60808101516001600160a01b031661042d5760a081015163ffffffff161561042a576040516329c5e8f160e21b815260040160405180910390fd5b50565b601e8160a0015163ffffffff16108061045257506202a3008160a0015163ffffffff16115b1561042a576040516329c5e8f160e21b815260040160405180910390fd5b5f5f61047b8361037d565b5f818152602081905260409020549091506001600160a01b031680156104bc576001600160a01b039081165f90815260016020526040902054169392505050565b6104c58461016e565b506104cf846103ac565b836040516104dc90610704565b6104e6919061088a565b604051809103905ff0801580156104ff573d5f5f3e3d5ffd5b50905061052b7f0000000000000000000000000000000000000000000000000000000000000000610595565b92506105388382846105a6565b60405163189acdbd60e31b81526001600160a01b03828116600483015284169063c4d66de8906024015f604051808303815f87803b158015610578575f5ffd5b505af115801561058a573d5f5f3e3d5ffd5b505050505050919050565b5f6105a0825f610674565b92915050565b5f818152602081905260409020546001600160a01b0316156106045760405162461bcd60e51b815260206004820152601260248201527110dbdb999a59d05b1c9958591e515e1a5cdd60721b60448201526064015b60405180910390fd5b5f8181526020818152604080832080546001600160a01b038088166001600160a01b03199283168117909355918552600190935281842080549188169190931681179092555190917fb3eacd0e351fafdfefdec84e1cd19679b38dbcd63ea7c2c24da17fd2bc3b3c0e91a2505050565b5f8147101561069f5760405163cf47918160e01b8152476004820152602481018390526044016105fb565b6e5af43d82803e903d91602b57fd5bf360205282601152763d602d80600a3d3981f3363d3d373d3d3d363d730000008360881c175f526037600983f090506001600160a01b0381166105a05760405163b06ebf3d60e01b815260040160405180910390fd5b6104fd8061094f83390190565b604051610120810167ffffffffffffffff8111828210171561074157634e487b7160e01b5f52604160045260245ffd5b60405290565b6001600160a01b038116811461042a575f5ffd5b803561037881610747565b803563ffffffff81168114610378575f5ffd5b80358015158114610378575f5ffd5b5f61012082840312801561079a575f5ffd5b506107a3610711565b6107ac8361075b565b81526107ba6020840161075b565b60208201526107cb6040840161075b565b60408201526107dc60608401610766565b60608201526107ed6080840161075b565b60808201526107fe60a08401610766565b60a082015260c0838101359082015260e080840135908201526108246101008401610779565b6101008201529392505050565b5f60208284031215610841575f5ffd5b813561084c81610747565b9392505050565b5f60208284031215610863575f5ffd5b5035919050565b5f6020828403121561087a575f5ffd5b815160ff8116811461084c575f5ffd5b81516001600160a01b031681526020808301516101208301916108b7908401826001600160a01b03169052565b5060408301516108d260408401826001600160a01b03169052565b5060608301516108ea606084018263ffffffff169052565b50608083015161090560808401826001600160a01b03169052565b5060a083015161091d60a084018263ffffffff169052565b5060c083015160c083015260e083015160e083015261010083015161094761010084018215159052565b509291505056fe6101c0604052348015610010575f5ffd5b506040516104fd3803806104fd83398101604081905261002f9161011d565b80516020820151606083015160c08085015160e08087015160a09283529092526001600160a01b0394851690915291831661010090815263ffffffff918216608090815260408601518516610120528501805185166101405292850151909116610160529051909116151561018052015115156101a0526101c6565b60405161012081016001600160401b03811182821017156100da57634e487b7160e01b5f52604160045260245ffd5b60405290565b80516001600160a01b03811681146100f6575f5ffd5b919050565b805163ffffffff811681146100f6575f5ffd5b805180151581146100f6575f5ffd5b5f61012082840312801561012f575f5ffd5b506101386100ab565b610141836100e0565b815261014f602084016100e0565b6020820152610160604084016100e0565b6040820152610171606084016100fb565b6060820152610182608084016100e0565b608082015261019360a084016100fb565b60a082015260c0838101519082015260e080840151908201526101b9610100840161010e565b6101008201529392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516102cd6102305f395f61019e01525f61017501525f60b401525f606701525f604201525f61014e01525f61012601525f61010001525f60da01525f608f01526102cd5ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063c3f909d41461002d575b5f5ffd5b60408051610140810182526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301527f000000000000000000000000000000000000000000000000000000000000000060808301527f000000000000000000000000000000000000000000000000000000000000000060a08301527f0000000000000000000000000000000000000000000000000000000000000000811660c08301527f00000000000000000000000000000000000000000000000000000000000000001660e08201527f000000000000000000000000000000000000000000000000000000000000000015156101008201527f0000000000000000000000000000000000000000000000000000000000000000151561012082015290516101d191906101da565b60405180910390f35b81516001600160a01b031681526101408101602083015161020660208401826001600160a01b03169052565b5060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015161024960c08401826001600160a01b03169052565b5060e083015161026460e08401826001600160a01b03169052565b5061010083015161027a61010084018215159052565b5061012083015161029061012084018215159052565b509291505056fea2646970667358221220366e50c3c111297f52ef99b14992a476def50e09f375a143657a8c4f22cf051264736f6c634300081c0033a2646970667358221220fda74f3fc64a787ad08e93878f0d147c166fec473847b971e5d4379db94e895564736f6c634300081c00336080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b610931806100d65f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806313b0be3314610064578063217a4b701461008a578063324b8d6e146100aa578063c4d66de8146100bc578063c71ed1e6146100d1578063f9fa619a146100fb575b5f5ffd5b6100776100723660046106c7565b61010c565b6040519081526020015b60405180910390f35b6100926102dc565b6040516001600160a01b039091168152602001610081565b5f54610092906001600160a01b031681565b6100cf6100ca3660046106f5565b610352565b005b6100e46100df366004610724565b6104a7565b604080519215158352602083019190915201610081565b6100cf6101093660046106f5565b50565b5f8054604080516330fe427560e21b8152905183926001600160a01b03169163c3f909d4916004808301926101409291908290030181865afa158015610154573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101789190610790565b90508060c001516001600160a01b0316836001600160a01b0316146101b05760405163981a2a2b60e01b815260040160405180910390fd5b6fffffffffffffffffffffffffffffffff8411156101e157604051631df5999960e21b815260040160405180910390fd5b5f5f6101f4835f01518460400151610557565b91509150816102155760405162bfc92160e01b815260040160405180910390fd5b82610100015161025d57610233868285608001518660a001516105fb565b9350835f03610255576040516301a7e28b60e61b815260040160405180910390fd5b5050506102d6565b5f5f61027185602001518660600151610557565b915091508161029357604051637c5ab47160e01b815260040160405180910390fd5b6102ae88848388608001518960a001518a6101200151610635565b9550855f036102d0576040516301a7e28b60e61b815260040160405180910390fd5b50505050505b92915050565b5f8054604080516330fe427560e21b8152905183926001600160a01b03169163c3f909d4916004808301926101409291908290030181865afa158015610324573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103489190610790565b60e0015192915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156103975750825b90505f8267ffffffffffffffff1660011480156103b35750303b155b9050811580156103c1575080155b156103df5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561040957845460ff60401b1916600160401b1785555b5f80546001600160a01b0319166001600160a01b0388169081179091556040519081527f077847d1fadf50041a730385b3d6b2de1cdeb5e078cb933fb091003e7f10e07a9060200160405180910390a1831561049f57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b5f5f5f5f5f9054906101000a90046001600160a01b03166001600160a01b031663c3f909d46040518163ffffffff1660e01b815260040161014060405180830381865afa1580156104fa573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061051e9190610790565b90508361053c5761053781602001518260600151610557565b61054d565b61054d815f01518260400151610557565b9250925050915091565b5f5f5f5f856001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610597573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105bb9190610857565b509350509250505f85420390505f831380156105d657508082115b156105ea57600183945094505050506105f4565b5f5f945094505050505b9250929050565b5f815f0361061c578284860281610614576106146108a5565b04905061062d565b84840261062983826108b9565b9150505b949350505050565b5f825f0361067857508585028161064e5784810261065f565b84818161065d5761065d6108a5565b045b9050838181610670576106706108a5565b0490506106a9565b5085850261068683826108b9565b90508161069c5761069785826108b9565b6106a6565b6106a685826108dc565b90505b9695505050505050565b6001600160a01b0381168114610109575f5ffd5b5f5f604083850312156106d8575f5ffd5b8235915060208301356106ea816106b3565b809150509250929050565b5f60208284031215610705575f5ffd5b8135610710816106b3565b9392505050565b8015158114610109575f5ffd5b5f60208284031215610734575f5ffd5b813561071081610717565b604051610140810167ffffffffffffffff8111828210171561076f57634e487b7160e01b5f52604160045260245ffd5b60405290565b8051610780816106b3565b919050565b805161078081610717565b5f6101408284031280156107a2575f5ffd5b506107ab61073f565b6107b483610775565b81526107c260208401610775565b602082015260408381015190820152606080840151908201526080808401519082015260a080840151908201526107fb60c08401610775565b60c082015261080c60e08401610775565b60e082015261081e6101008401610785565b6101008201526108316101208401610785565b6101208201529392505050565b805169ffffffffffffffffffff81168114610780575f5ffd5b5f5f5f5f5f60a0868803121561086b575f5ffd5b6108748661083e565b602087015160408801516060890151929750909550935091506108996080870161083e565b90509295509295909350565b634e487b7160e01b5f52601260045260245ffd5b80820281158282048414176102d657634e487b7160e01b5f52601160045260245ffd5b5f826108f657634e487b7160e01b5f52601260045260245ffd5b50049056fea264697066735822122060fe300384a9aa8c09f25ed19a33e41be6c0cd325c6ebe29b205d732bb8ce35f64736f6c634300081c0033
Deployed Bytecode
0x608060405234801561000f575f5ffd5b506004361061007a575f3560e01c8063a8f39f6611610058578063a8f39f66146100f7578063b0b1f5851461011e578063f896503a14610133578063fe58f4b91461015b575f5ffd5b806328e981661461007e57806354482173146100a45780637af14d6e146100e4575b5f5ffd5b61009161008c366004610788565b61016e565b6040519081526020015b60405180910390f35b6100cc6100b2366004610831565b60016020525f90815260409020546001600160a01b031681565b6040516001600160a01b03909116815260200161009b565b6100916100f2366004610788565b61037d565b6100cc7f000000000000000000000000be86dfbfb64f933d508e32908a871cfc0cb9e2e381565b61013161012c366004610788565b6103ac565b005b6100cc610141366004610853565b5f602081905290815260409020546001600160a01b031681565b6100cc610169366004610788565b610470565b60208101515f906001600160a01b031661019b57604051639fabe1c160e01b815260040160405180910390fd5b81516001600160a01b03166101c357604051639fabe1c160e01b815260040160405180910390fd5b815f01516001600160a01b031682602001516001600160a01b0316036101fc57604051633d4cfcc560e11b815260040160405180910390fd5b60408201516001600160a01b031661022757604051639fabe1c160e01b815260040160405180910390fd5b81608001516001600160a01b031682604001516001600160a01b03160361026157604051630e04002960e11b815260040160405180910390fd5b60808201516001600160a01b0316156102de5781608001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102b4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102d8919061086a565b60ff1690505b6ec097ce7bc90715b34b9f10000000008260c00151111561031257604051631fe0fdf560e11b815260040160405180910390fd5b6ec097ce7bc90715b34b9f10000000008260e00151111561034657604051631f604a8560e21b815260040160405180910390fd5b60c082015115801561035a575060e0820151155b156103785760405163d156ee7f60e01b815260040160405180910390fd5b919050565b5f8160405160200161038f919061088a565b604051602081830303815290604052805190602001209050919050565b601e816060015163ffffffff1610806103d157506202a300816060015163ffffffff16115b156103ef57604051638f1825c960e01b815260040160405180910390fd5b60808101516001600160a01b031661042d5760a081015163ffffffff161561042a576040516329c5e8f160e21b815260040160405180910390fd5b50565b601e8160a0015163ffffffff16108061045257506202a3008160a0015163ffffffff16115b1561042a576040516329c5e8f160e21b815260040160405180910390fd5b5f5f61047b8361037d565b5f818152602081905260409020549091506001600160a01b031680156104bc576001600160a01b039081165f90815260016020526040902054169392505050565b6104c58461016e565b506104cf846103ac565b836040516104dc90610704565b6104e6919061088a565b604051809103905ff0801580156104ff573d5f5f3e3d5ffd5b50905061052b7f000000000000000000000000be86dfbfb64f933d508e32908a871cfc0cb9e2e3610595565b92506105388382846105a6565b60405163189acdbd60e31b81526001600160a01b03828116600483015284169063c4d66de8906024015f604051808303815f87803b158015610578575f5ffd5b505af115801561058a573d5f5f3e3d5ffd5b505050505050919050565b5f6105a0825f610674565b92915050565b5f818152602081905260409020546001600160a01b0316156106045760405162461bcd60e51b815260206004820152601260248201527110dbdb999a59d05b1c9958591e515e1a5cdd60721b60448201526064015b60405180910390fd5b5f8181526020818152604080832080546001600160a01b038088166001600160a01b03199283168117909355918552600190935281842080549188169190931681179092555190917fb3eacd0e351fafdfefdec84e1cd19679b38dbcd63ea7c2c24da17fd2bc3b3c0e91a2505050565b5f8147101561069f5760405163cf47918160e01b8152476004820152602481018390526044016105fb565b6e5af43d82803e903d91602b57fd5bf360205282601152763d602d80600a3d3981f3363d3d373d3d3d363d730000008360881c175f526037600983f090506001600160a01b0381166105a05760405163b06ebf3d60e01b815260040160405180910390fd5b6104fd8061094f83390190565b604051610120810167ffffffffffffffff8111828210171561074157634e487b7160e01b5f52604160045260245ffd5b60405290565b6001600160a01b038116811461042a575f5ffd5b803561037881610747565b803563ffffffff81168114610378575f5ffd5b80358015158114610378575f5ffd5b5f61012082840312801561079a575f5ffd5b506107a3610711565b6107ac8361075b565b81526107ba6020840161075b565b60208201526107cb6040840161075b565b60408201526107dc60608401610766565b60608201526107ed6080840161075b565b60808201526107fe60a08401610766565b60a082015260c0838101359082015260e080840135908201526108246101008401610779565b6101008201529392505050565b5f60208284031215610841575f5ffd5b813561084c81610747565b9392505050565b5f60208284031215610863575f5ffd5b5035919050565b5f6020828403121561087a575f5ffd5b815160ff8116811461084c575f5ffd5b81516001600160a01b031681526020808301516101208301916108b7908401826001600160a01b03169052565b5060408301516108d260408401826001600160a01b03169052565b5060608301516108ea606084018263ffffffff169052565b50608083015161090560808401826001600160a01b03169052565b5060a083015161091d60a084018263ffffffff169052565b5060c083015160c083015260e083015160e083015261010083015161094761010084018215159052565b509291505056fe6101c0604052348015610010575f5ffd5b506040516104fd3803806104fd83398101604081905261002f9161011d565b80516020820151606083015160c08085015160e08087015160a09283529092526001600160a01b0394851690915291831661010090815263ffffffff918216608090815260408601518516610120528501805185166101405292850151909116610160529051909116151561018052015115156101a0526101c6565b60405161012081016001600160401b03811182821017156100da57634e487b7160e01b5f52604160045260245ffd5b60405290565b80516001600160a01b03811681146100f6575f5ffd5b919050565b805163ffffffff811681146100f6575f5ffd5b805180151581146100f6575f5ffd5b5f61012082840312801561012f575f5ffd5b506101386100ab565b610141836100e0565b815261014f602084016100e0565b6020820152610160604084016100e0565b6040820152610171606084016100fb565b6060820152610182608084016100e0565b608082015261019360a084016100fb565b60a082015260c0838101519082015260e080840151908201526101b9610100840161010e565b6101008201529392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516102cd6102305f395f61019e01525f61017501525f60b401525f606701525f604201525f61014e01525f61012601525f61010001525f60da01525f608f01526102cd5ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063c3f909d41461002d575b5f5ffd5b60408051610140810182526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301527f000000000000000000000000000000000000000000000000000000000000000060808301527f000000000000000000000000000000000000000000000000000000000000000060a08301527f0000000000000000000000000000000000000000000000000000000000000000811660c08301527f00000000000000000000000000000000000000000000000000000000000000001660e08201527f000000000000000000000000000000000000000000000000000000000000000015156101008201527f0000000000000000000000000000000000000000000000000000000000000000151561012082015290516101d191906101da565b60405180910390f35b81516001600160a01b031681526101408101602083015161020660208401826001600160a01b03169052565b5060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015161024960c08401826001600160a01b03169052565b5060e083015161026460e08401826001600160a01b03169052565b5061010083015161027a61010084018215159052565b5061012083015161029061012084018215159052565b509291505056fea2646970667358221220366e50c3c111297f52ef99b14992a476def50e09f375a143657a8c4f22cf051264736f6c634300081c0033a2646970667358221220fda74f3fc64a787ad08e93878f0d147c166fec473847b971e5d4379db94e895564736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ 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.