Source Code
Overview
S Balance
S Value
$0.00Latest 11 from a total of 11 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Create | 33391364 | 229 days ago | IN | 0 S | 0.0180138 | ||||
| Create | 33391105 | 229 days ago | IN | 0 S | 0.01801465 | ||||
| Create | 31287276 | 238 days ago | IN | 0 S | 0.01815665 | ||||
| Create | 30387578 | 242 days ago | IN | 0 S | 0.00155025 | ||||
| Create | 30385544 | 242 days ago | IN | 0 S | 0.0180138 | ||||
| Create | 30163749 | 243 days ago | IN | 0 S | 0.00166395 | ||||
| Create | 30163212 | 243 days ago | IN | 0 S | 0.01815505 | ||||
| Create | 30159339 | 243 days ago | IN | 0 S | 0.0180134 | ||||
| Create | 30159123 | 243 days ago | IN | 0 S | 0.0186204 | ||||
| Create | 28016319 | 252 days ago | IN | 0 S | 0.0180134 | ||||
| Create | 27973903 | 252 days ago | IN | 0 S | 0.0189004 |
Latest 25 internal transactions (View All)
Advanced mode:
Cross-Chain Transactions
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 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 {Create2Factory} from "common/utils/Create2Factory.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 Create2Factory, OracleFactory {
constructor() OracleFactory(address(new ChainlinkV3Oracle())) {
// noting to configure
}
function create(
IChainlinkV3Oracle.ChainlinkV3DeploymentConfig memory _config,
bytes32 _externalSalt
) 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.cloneDeterministic(ORACLE_IMPLEMENTATION, _salt(_externalSalt)));
_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.8.28;
import {Nonces} from "openzeppelin5/utils/Nonces.sol";
contract Create2Factory is Nonces {
function _salt() internal returns (bytes32 salt) {
salt = keccak256(abi.encodePacked(
msg.sender,
_useNonce(msg.sender)
));
}
function _salt(bytes32 _externalSalt) internal returns (bytes32 salt) {
salt = keccak256(abi.encodePacked(
msg.sender,
_useNonce(msg.sender),
_externalSalt
));
}
}// 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)
{
// `_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
unchecked { assetPrice = _baseAmount * _assetPrice; }
if (_normalizationMultiplier == 0) {
assetPrice = _invertSecondPrice ? assetPrice / _secondPrice : assetPrice * _secondPrice;
// div is safe
unchecked { assetPrice = assetPrice / _normalizationDivider; }
return 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) (utils/Nonces.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides tracking nonces for addresses. Nonces will only increment.
*/
abstract contract Nonces {
/**
* @dev The nonce used for an `account` is not the expected current nonce.
*/
error InvalidAccountNonce(address account, uint256 currentNonce);
mapping(address account => uint256) private _nonces;
/**
* @dev Returns the next unused nonce for an address.
*/
function nonces(address owner) public view virtual returns (uint256) {
return _nonces[owner];
}
/**
* @dev Consumes a nonce.
*
* Returns the current value and increments nonce.
*/
function _useNonce(address owner) internal virtual returns (uint256) {
// For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be
// decremented or reset. This guarantees that the nonce never overflows.
unchecked {
// It is important to do x++ and not ++x here.
return _nonces[owner]++;
}
}
/**
* @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.
*/
function _useCheckedNonce(address owner, uint256 nonce) internal virtual {
uint256 current = _useNonce(owner);
if (nonce != current) {
revert InvalidAccountNonce(owner, current);
}
}
}// 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/",
"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/",
"a16z-erc4626-tests/=gitmodules/a16z-erc4626-tests/",
"ERC4626/=gitmodules/crytic/properties/lib/ERC4626/contracts/",
"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/",
"openzeppelin-contracts-5/=gitmodules/openzeppelin-contracts-5/",
"openzeppelin-contracts-upgradeable-5/=gitmodules/openzeppelin-contracts-upgradeable-5/",
"openzeppelin-contracts-upgradeable/=gitmodules/pyth-sdk-solidity/lazer/contracts/evm/lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=gitmodules/openzeppelin-contracts-upgradeable-5/lib/openzeppelin-contracts/",
"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
Contract ABI
API[{"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":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"currentNonce","type":"uint256"}],"name":"InvalidAccountNonce","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"},{"internalType":"bytes32","name":"_externalSalt","type":"bytes32"}],"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":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","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":"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
60a0604052348015600e575f5ffd5b506040516019906089565b604051809103905ff0801580156031573d5f5f3e3d5ffd5b506001600160a01b03811660795760405162461bcd60e51b815260206004820152600b60248201526a5a65726f4164647265737360a81b604482015260640160405180910390fd5b6001600160a01b03166080526096565b610a028061100483390190565b608051610f4f6100b55f395f818161014201526104490152610f4f5ff3fe608060405234801561000f575f5ffd5b5060043610610085575f3560e01c80637ecebe00116100585780637ecebe0014610115578063a8f39f661461013d578063b0b1f58514610164578063f896503a14610179575f5ffd5b806328e981661461008957806354482173146100af5780635b996c42146100ef5780637af14d6e14610102575b5f5ffd5b61009c6100973660046108c0565b6101a1565b6040519081526020015b60405180910390f35b6100d76100bd3660046108db565b60026020525f90815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016100a6565b6100d76100fd3660046108f6565b6103b0565b61009c6101103660046108c0565b6104e2565b61009c6101233660046108db565b6001600160a01b03165f9081526020819052604090205490565b6100d77f000000000000000000000000000000000000000000000000000000000000000081565b6101776101723660046108c0565b610511565b005b6100d7610187366004610921565b60016020525f90815260409020546001600160a01b031681565b60208101515f906001600160a01b03166101ce57604051639fabe1c160e01b815260040160405180910390fd5b81516001600160a01b03166101f657604051639fabe1c160e01b815260040160405180910390fd5b815f01516001600160a01b031682602001516001600160a01b03160361022f57604051633d4cfcc560e11b815260040160405180910390fd5b60408201516001600160a01b031661025a57604051639fabe1c160e01b815260040160405180910390fd5b81608001516001600160a01b031682604001516001600160a01b03160361029457604051630e04002960e11b815260040160405180910390fd5b60808201516001600160a01b0316156103115781608001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102e7573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061030b9190610938565b60ff1690505b6ec097ce7bc90715b34b9f10000000008260c00151111561034557604051631fe0fdf560e11b815260040160405180910390fd5b6ec097ce7bc90715b34b9f10000000008260e00151111561037957604051631f604a8560e21b815260040160405180910390fd5b60c082015115801561038d575060e0820151155b156103ab5760405163d156ee7f60e01b815260040160405180910390fd5b919050565b5f5f6103bb846104e2565b5f818152600160205260409020549091506001600160a01b031680156103fe576001600160a01b039081165f908152600260205260409020541691506104dc9050565b610407856101a1565b5061041185610511565b8460405161041e90610794565b6104289190610958565b604051809103905ff080158015610441573d5f5f3e3d5ffd5b5090506104767f0000000000000000000000000000000000000000000000000000000000000000610471866105d5565b610620565b9250610483838284610633565b60405163189acdbd60e31b81526001600160a01b03828116600483015284169063c4d66de8906024015f604051808303815f87803b1580156104c3575f5ffd5b505af11580156104d5573d5f5f3e3d5ffd5b5050505050505b92915050565b5f816040516020016104f49190610958565b604051602081830303815290604052805190602001209050919050565b601e816060015163ffffffff16108061053657506202a300816060015163ffffffff16115b1561055457604051638f1825c960e01b815260040160405180910390fd5b60808101516001600160a01b03166105925760a081015163ffffffff161561058f576040516329c5e8f160e21b815260040160405180910390fd5b50565b601e8160a0015163ffffffff1610806105b757506202a3008160a0015163ffffffff16115b1561058f576040516329c5e8f160e21b815260040160405180910390fd5b335f81815260208190526040812080546001810190915590919060405160609290921b6bffffffffffffffffffffffff191660208301526034820152605481018390526074016104f4565b5f61062c83835f610703565b9392505050565b5f818152600160205260409020546001600160a01b0316156106915760405162461bcd60e51b815260206004820152601260248201527110dbdb999a59d05b1c9958591e515e1a5cdd60721b60448201526064015b60405180910390fd5b5f81815260016020908152604080832080546001600160a01b038088166001600160a01b03199283168117909355918552600290935281842080549188169190931681179092555190917fb3eacd0e351fafdfefdec84e1cd19679b38dbcd63ea7c2c24da17fd2bc3b3c0e91a2505050565b5f8147101561072e5760405163cf47918160e01b815247600482015260248101839052604401610688565b6e5af43d82803e903d91602b57fd5bf360205283601152763d602d80600a3d3981f3363d3d373d3d3d363d730000008460881c175f52826037600984f590506001600160a01b03811661062c5760405163b06ebf3d60e01b815260040160405180910390fd5b6104fd80610a1d83390190565b604051610120810167ffffffffffffffff811182821017156107d157634e487b7160e01b5f52604160045260245ffd5b60405290565b6001600160a01b038116811461058f575f5ffd5b80356103ab816107d7565b803563ffffffff811681146103ab575f5ffd5b803580151581146103ab575f5ffd5b5f6101208284031215610829575f5ffd5b6108316107a1565b905061083c826107eb565b815261084a602083016107eb565b602082015261085b604083016107eb565b604082015261086c606083016107f6565b606082015261087d608083016107eb565b608082015261088e60a083016107f6565b60a082015260c0828101359082015260e080830135908201526108b46101008301610809565b61010082015292915050565b5f61012082840312156108d1575f5ffd5b61062c8383610818565b5f602082840312156108eb575f5ffd5b813561062c816107d7565b5f5f6101408385031215610908575f5ffd5b6109128484610818565b94610120939093013593505050565b5f60208284031215610931575f5ffd5b5035919050565b5f60208284031215610948575f5ffd5b815160ff8116811461062c575f5ffd5b81516001600160a01b03168152602080830151610120830191610985908401826001600160a01b03169052565b5060408301516109a060408401826001600160a01b03169052565b5060608301516109b8606084018263ffffffff169052565b5060808301516109d360808401826001600160a01b03169052565b5060a08301516109eb60a084018263ffffffff169052565b5060c083015160c083015260e083015160e0830152610100830151610a1561010084018215159052565b509291505056fe6101c0604052348015610010575f5ffd5b506040516104fd3803806104fd83398101604081905261002f9161011d565b80516020820151606083015160c08085015160e08087015160a09283529092526001600160a01b0394851690915291831661010090815263ffffffff918216608090815260408601518516610120528501805185166101405292850151909116610160529051909116151561018052015115156101a0526101c6565b60405161012081016001600160401b03811182821017156100da57634e487b7160e01b5f52604160045260245ffd5b60405290565b80516001600160a01b03811681146100f6575f5ffd5b919050565b805163ffffffff811681146100f6575f5ffd5b805180151581146100f6575f5ffd5b5f61012082840312801561012f575f5ffd5b506101386100ab565b610141836100e0565b815261014f602084016100e0565b6020820152610160604084016100e0565b6040820152610171606084016100fb565b6060820152610182608084016100e0565b608082015261019360a084016100fb565b60a082015260c0838101519082015260e080840151908201526101b9610100840161010e565b6101008201529392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516102cd6102305f395f61019e01525f61017501525f60b401525f606701525f604201525f61014e01525f61012601525f61010001525f60da01525f608f01526102cd5ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063c3f909d41461002d575b5f5ffd5b60408051610140810182526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301527f000000000000000000000000000000000000000000000000000000000000000060808301527f000000000000000000000000000000000000000000000000000000000000000060a08301527f0000000000000000000000000000000000000000000000000000000000000000811660c08301527f00000000000000000000000000000000000000000000000000000000000000001660e08201527f000000000000000000000000000000000000000000000000000000000000000015156101008201527f0000000000000000000000000000000000000000000000000000000000000000151561012082015290516101d191906101da565b60405180910390f35b81516001600160a01b031681526101408101602083015161020660208401826001600160a01b03169052565b5060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015161024960c08401826001600160a01b03169052565b5060e083015161026460e08401826001600160a01b03169052565b5061010083015161027a61010084018215159052565b5061012083015161029061012084018215159052565b509291505056fea26469706673582212200289149b210661c8c07a18382fbeb47928cf7cd513cb99e6502941de666b06fe64736f6c634300081c0033a264697066735822122051114d87c97a41b8cbdb08ca071177666cf50fa180a3286f0c980cd9e0a5364964736f6c634300081c00336080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b61092c806100d65f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806313b0be3314610064578063217a4b701461008a578063324b8d6e146100aa578063c4d66de8146100bc578063c71ed1e6146100d1578063f9fa619a146100fb575b5f5ffd5b6100776100723660046106c2565b61010c565b6040519081526020015b60405180910390f35b6100926102dc565b6040516001600160a01b039091168152602001610081565b5f54610092906001600160a01b031681565b6100cf6100ca3660046106f0565b610352565b005b6100e46100df36600461071f565b6104a7565b604080519215158352602083019190915201610081565b6100cf6101093660046106f0565b50565b5f8054604080516330fe427560e21b8152905183926001600160a01b03169163c3f909d4916004808301926101409291908290030181865afa158015610154573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610178919061078b565b90508060c001516001600160a01b0316836001600160a01b0316146101b05760405163981a2a2b60e01b815260040160405180910390fd5b6fffffffffffffffffffffffffffffffff8411156101e157604051631df5999960e21b815260040160405180910390fd5b5f5f6101f4835f01518460400151610557565b91509150816102155760405162bfc92160e01b815260040160405180910390fd5b82610100015161025d57610233868285608001518660a001516105fb565b9350835f03610255576040516301a7e28b60e61b815260040160405180910390fd5b5050506102d6565b5f5f61027185602001518660600151610557565b915091508161029357604051637c5ab47160e01b815260040160405180910390fd5b6102ae88848388608001518960a001518a6101200151610635565b9550855f036102d0576040516301a7e28b60e61b815260040160405180910390fd5b50505050505b92915050565b5f8054604080516330fe427560e21b8152905183926001600160a01b03169163c3f909d4916004808301926101409291908290030181865afa158015610324573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610348919061078b565b60e0015192915050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156103975750825b90505f8267ffffffffffffffff1660011480156103b35750303b155b9050811580156103c1575080155b156103df5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561040957845460ff60401b1916600160401b1785555b5f80546001600160a01b0319166001600160a01b0388169081179091556040519081527f077847d1fadf50041a730385b3d6b2de1cdeb5e078cb933fb091003e7f10e07a9060200160405180910390a1831561049f57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b5f5f5f5f5f9054906101000a90046001600160a01b03166001600160a01b031663c3f909d46040518163ffffffff1660e01b815260040161014060405180830381865afa1580156104fa573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061051e919061078b565b90508361053c5761053781602001518260600151610557565b61054d565b61054d815f01518260400151610557565b9250925050915091565b5f5f5f5f856001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610597573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105bb9190610852565b509350509250505f85420390505f831380156105d657508082115b156105ea57600183945094505050506105f4565b5f5f945094505050505b9250929050565b5f815f0361061c578284860281610614576106146108a0565b04905061062d565b84840261062983826108b4565b9150505b949350505050565b8585025f83900361067757816106545761064f85826108b4565b61065e565b61065e85826108d7565b905083818161066f5761066f6108a0565b0490506106a4565b61068183826108b4565b9050816106975761069285826108b4565b6106a1565b6106a185826108d7565b90505b9695505050505050565b6001600160a01b0381168114610109575f5ffd5b5f5f604083850312156106d3575f5ffd5b8235915060208301356106e5816106ae565b809150509250929050565b5f60208284031215610700575f5ffd5b813561070b816106ae565b9392505050565b8015158114610109575f5ffd5b5f6020828403121561072f575f5ffd5b813561070b81610712565b604051610140810167ffffffffffffffff8111828210171561076a57634e487b7160e01b5f52604160045260245ffd5b60405290565b805161077b816106ae565b919050565b805161077b81610712565b5f61014082840312801561079d575f5ffd5b506107a661073a565b6107af83610770565b81526107bd60208401610770565b602082015260408381015190820152606080840151908201526080808401519082015260a080840151908201526107f660c08401610770565b60c082015261080760e08401610770565b60e08201526108196101008401610780565b61010082015261082c6101208401610780565b6101208201529392505050565b805169ffffffffffffffffffff8116811461077b575f5ffd5b5f5f5f5f5f60a08688031215610866575f5ffd5b61086f86610839565b6020870151604088015160608901519297509095509350915061089460808701610839565b90509295509295909350565b634e487b7160e01b5f52601260045260245ffd5b80820281158282048414176102d657634e487b7160e01b5f52601160045260245ffd5b5f826108f157634e487b7160e01b5f52601260045260245ffd5b50049056fea264697066735822122036b952208cd464b392bb9cc8137a6cac78b097ba0df1e1c8d4cf7fa50a593f8c64736f6c634300081c0033
Deployed Bytecode
0x608060405234801561000f575f5ffd5b5060043610610085575f3560e01c80637ecebe00116100585780637ecebe0014610115578063a8f39f661461013d578063b0b1f58514610164578063f896503a14610179575f5ffd5b806328e981661461008957806354482173146100af5780635b996c42146100ef5780637af14d6e14610102575b5f5ffd5b61009c6100973660046108c0565b6101a1565b6040519081526020015b60405180910390f35b6100d76100bd3660046108db565b60026020525f90815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016100a6565b6100d76100fd3660046108f6565b6103b0565b61009c6101103660046108c0565b6104e2565b61009c6101233660046108db565b6001600160a01b03165f9081526020819052604090205490565b6100d77f000000000000000000000000a3f6403375e5b6a4d8a18e302c3a156a9554fa0c81565b6101776101723660046108c0565b610511565b005b6100d7610187366004610921565b60016020525f90815260409020546001600160a01b031681565b60208101515f906001600160a01b03166101ce57604051639fabe1c160e01b815260040160405180910390fd5b81516001600160a01b03166101f657604051639fabe1c160e01b815260040160405180910390fd5b815f01516001600160a01b031682602001516001600160a01b03160361022f57604051633d4cfcc560e11b815260040160405180910390fd5b60408201516001600160a01b031661025a57604051639fabe1c160e01b815260040160405180910390fd5b81608001516001600160a01b031682604001516001600160a01b03160361029457604051630e04002960e11b815260040160405180910390fd5b60808201516001600160a01b0316156103115781608001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102e7573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061030b9190610938565b60ff1690505b6ec097ce7bc90715b34b9f10000000008260c00151111561034557604051631fe0fdf560e11b815260040160405180910390fd5b6ec097ce7bc90715b34b9f10000000008260e00151111561037957604051631f604a8560e21b815260040160405180910390fd5b60c082015115801561038d575060e0820151155b156103ab5760405163d156ee7f60e01b815260040160405180910390fd5b919050565b5f5f6103bb846104e2565b5f818152600160205260409020549091506001600160a01b031680156103fe576001600160a01b039081165f908152600260205260409020541691506104dc9050565b610407856101a1565b5061041185610511565b8460405161041e90610794565b6104289190610958565b604051809103905ff080158015610441573d5f5f3e3d5ffd5b5090506104767f000000000000000000000000a3f6403375e5b6a4d8a18e302c3a156a9554fa0c610471866105d5565b610620565b9250610483838284610633565b60405163189acdbd60e31b81526001600160a01b03828116600483015284169063c4d66de8906024015f604051808303815f87803b1580156104c3575f5ffd5b505af11580156104d5573d5f5f3e3d5ffd5b5050505050505b92915050565b5f816040516020016104f49190610958565b604051602081830303815290604052805190602001209050919050565b601e816060015163ffffffff16108061053657506202a300816060015163ffffffff16115b1561055457604051638f1825c960e01b815260040160405180910390fd5b60808101516001600160a01b03166105925760a081015163ffffffff161561058f576040516329c5e8f160e21b815260040160405180910390fd5b50565b601e8160a0015163ffffffff1610806105b757506202a3008160a0015163ffffffff16115b1561058f576040516329c5e8f160e21b815260040160405180910390fd5b335f81815260208190526040812080546001810190915590919060405160609290921b6bffffffffffffffffffffffff191660208301526034820152605481018390526074016104f4565b5f61062c83835f610703565b9392505050565b5f818152600160205260409020546001600160a01b0316156106915760405162461bcd60e51b815260206004820152601260248201527110dbdb999a59d05b1c9958591e515e1a5cdd60721b60448201526064015b60405180910390fd5b5f81815260016020908152604080832080546001600160a01b038088166001600160a01b03199283168117909355918552600290935281842080549188169190931681179092555190917fb3eacd0e351fafdfefdec84e1cd19679b38dbcd63ea7c2c24da17fd2bc3b3c0e91a2505050565b5f8147101561072e5760405163cf47918160e01b815247600482015260248101839052604401610688565b6e5af43d82803e903d91602b57fd5bf360205283601152763d602d80600a3d3981f3363d3d373d3d3d363d730000008460881c175f52826037600984f590506001600160a01b03811661062c5760405163b06ebf3d60e01b815260040160405180910390fd5b6104fd80610a1d83390190565b604051610120810167ffffffffffffffff811182821017156107d157634e487b7160e01b5f52604160045260245ffd5b60405290565b6001600160a01b038116811461058f575f5ffd5b80356103ab816107d7565b803563ffffffff811681146103ab575f5ffd5b803580151581146103ab575f5ffd5b5f6101208284031215610829575f5ffd5b6108316107a1565b905061083c826107eb565b815261084a602083016107eb565b602082015261085b604083016107eb565b604082015261086c606083016107f6565b606082015261087d608083016107eb565b608082015261088e60a083016107f6565b60a082015260c0828101359082015260e080830135908201526108b46101008301610809565b61010082015292915050565b5f61012082840312156108d1575f5ffd5b61062c8383610818565b5f602082840312156108eb575f5ffd5b813561062c816107d7565b5f5f6101408385031215610908575f5ffd5b6109128484610818565b94610120939093013593505050565b5f60208284031215610931575f5ffd5b5035919050565b5f60208284031215610948575f5ffd5b815160ff8116811461062c575f5ffd5b81516001600160a01b03168152602080830151610120830191610985908401826001600160a01b03169052565b5060408301516109a060408401826001600160a01b03169052565b5060608301516109b8606084018263ffffffff169052565b5060808301516109d360808401826001600160a01b03169052565b5060a08301516109eb60a084018263ffffffff169052565b5060c083015160c083015260e083015160e0830152610100830151610a1561010084018215159052565b509291505056fe6101c0604052348015610010575f5ffd5b506040516104fd3803806104fd83398101604081905261002f9161011d565b80516020820151606083015160c08085015160e08087015160a09283529092526001600160a01b0394851690915291831661010090815263ffffffff918216608090815260408601518516610120528501805185166101405292850151909116610160529051909116151561018052015115156101a0526101c6565b60405161012081016001600160401b03811182821017156100da57634e487b7160e01b5f52604160045260245ffd5b60405290565b80516001600160a01b03811681146100f6575f5ffd5b919050565b805163ffffffff811681146100f6575f5ffd5b805180151581146100f6575f5ffd5b5f61012082840312801561012f575f5ffd5b506101386100ab565b610141836100e0565b815261014f602084016100e0565b6020820152610160604084016100e0565b6040820152610171606084016100fb565b6060820152610182608084016100e0565b608082015261019360a084016100fb565b60a082015260c0838101519082015260e080840151908201526101b9610100840161010e565b6101008201529392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516102cd6102305f395f61019e01525f61017501525f60b401525f606701525f604201525f61014e01525f61012601525f61010001525f60da01525f608f01526102cd5ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c8063c3f909d41461002d575b5f5ffd5b60408051610140810182526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301527f000000000000000000000000000000000000000000000000000000000000000060808301527f000000000000000000000000000000000000000000000000000000000000000060a08301527f0000000000000000000000000000000000000000000000000000000000000000811660c08301527f00000000000000000000000000000000000000000000000000000000000000001660e08201527f000000000000000000000000000000000000000000000000000000000000000015156101008201527f0000000000000000000000000000000000000000000000000000000000000000151561012082015290516101d191906101da565b60405180910390f35b81516001600160a01b031681526101408101602083015161020660208401826001600160a01b03169052565b5060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015161024960c08401826001600160a01b03169052565b5060e083015161026460e08401826001600160a01b03169052565b5061010083015161027a61010084018215159052565b5061012083015161029061012084018215159052565b509291505056fea26469706673582212200289149b210661c8c07a18382fbeb47928cf7cd513cb99e6502941de666b06fe64736f6c634300081c0033a264697066735822122051114d87c97a41b8cbdb08ca071177666cf50fa180a3286f0c980cd9e0a5364964736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.