Overview
ETH Balance
0 ETH
Eth Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x61054060 | 20024484 | 657 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
WeETHUniV3CheckCLRSOracle
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 10000000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { FluidOracle } from "../fluidOracle.sol";
import { UniV3CheckCLRSOracle } from "./uniV3CheckCLRSOracle.sol";
import { WeETHOracleImpl } from "../implementations/weETHOracleImpl.sol";
import { IWeETH } from "../interfaces/external/IWeETH.sol";
import { OracleUtils } from "../libraries/oracleUtils.sol";
/// @title weETHOracle combined with a uniV3CheckCLRSOracle.
/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:
/// 1. weETH Oracle price for weETH -> eETH = ETH (pegged)
/// 2. result from 1. combined with a uniV3CheckCLRSOracle to get someToken (e.g. ETH) -> someToken2.
/// e.g. when going from weETH to USDC:
/// 1. weETH -> eETH = ETH via weETH Oracle
/// 2. ETH -> USDC via UniV3 ETH <> USDC pool checked against ETH -> USDC Chainlink feed.
contract WeETHUniV3CheckCLRSOracle is FluidOracle, WeETHOracleImpl, UniV3CheckCLRSOracle {
/// @notice constructs a WeETHUniV3CheckCLRSOracle with all inherited contracts
/// @param weETH_ address of the weETH contract
/// @param uniV3CheckCLRSParams_ UniV3CheckCLRSOracle constructor params
constructor(
IWeETH weETH_,
UniV3CheckCLRSConstructorParams memory uniV3CheckCLRSParams_
) WeETHOracleImpl(weETH_) UniV3CheckCLRSOracle(uniV3CheckCLRSParams_) {}
/// @inheritdoc FluidOracle
function getExchangeRateOperate()
public
view
override(FluidOracle, UniV3CheckCLRSOracle)
returns (uint256 exchangeRate_)
{
// get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns
// a price if some rate is valid, with multiple fallbacks. Can not return 0.
// (super.getExchangeRate() returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)
// Combine this rate with the weETH -> eETH = ETH rate.
exchangeRate_ =
(super.getExchangeRateOperate() * _getWeETHExchangeRate()) /
(10 ** OracleUtils.RATE_OUTPUT_DECIMALS);
}
/// @inheritdoc FluidOracle
function getExchangeRateLiquidate()
public
view
override(FluidOracle, UniV3CheckCLRSOracle)
returns (uint256 exchangeRate_)
{
// get rate from UniV3Check Oracle (likely uniV3 / Chainlink checked against for delta). This always returns
// a price if some rate is valid, with multiple fallbacks. Can not return 0.
// (super.getExchangeRate() returns UniV3CheckCLRSOracle rate, no other inherited contract has this.)
// Combine this rate with the weETH -> eETH = ETH rate.
exchangeRate_ =
(super.getExchangeRateLiquidate() * _getWeETHExchangeRate()) /
(10 ** OracleUtils.RATE_OUTPUT_DECIMALS);
}
/// @inheritdoc FluidOracle
function getExchangeRate() public view override(FluidOracle, UniV3CheckCLRSOracle) returns (uint256 exchangeRate_) {
return getExchangeRateOperate();
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
contract Error {
error FluidOracleError(uint256 errorId_);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
library ErrorTypes {
/***********************************|
| UniV3CheckCLRSOracle |
|__________________________________*/
/// @notice thrown when the delta between main price source and check rate source is exceeding the allowed delta
uint256 internal constant UniV3CheckCLRSOracle__InvalidPrice = 60001;
/// @notice thrown when an invalid parameter is passed to a method
uint256 internal constant UniV3CheckCLRSOracle__InvalidParams = 60002;
/// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config
uint256 internal constant UniV3CheckCLRSOracle__ExchangeRateZero = 60003;
/***********************************|
| sUSDe Oracle |
|__________________________________*/
/// @notice thrown when an invalid parameter is passed to a method
uint256 internal constant SUSDeOracle__InvalidParams = 60102;
/***********************************|
| Chainlink Oracle |
|__________________________________*/
/// @notice thrown when an invalid parameter is passed to a method
uint256 internal constant ChainlinkOracle__InvalidParams = 61001;
/***********************************|
| UniswapV3 Oracle |
|__________________________________*/
/// @notice thrown when an invalid parameter is passed to a method
uint256 internal constant UniV3Oracle__InvalidParams = 62001;
/// @notice thrown when constructor is called with invalid ordered seconds agos values
uint256 internal constant UniV3Oracle__InvalidSecondsAgos = 62002;
/// @notice thrown when constructor is called with invalid delta values > 100%
uint256 internal constant UniV3Oracle__InvalidDeltas = 62003;
/***********************************|
| WstETh Oracle |
|__________________________________*/
/// @notice thrown when an invalid parameter is passed to a method
uint256 internal constant WstETHOracle__InvalidParams = 63001;
/***********************************|
| Redstone Oracle |
|__________________________________*/
/// @notice thrown when an invalid parameter is passed to a method
uint256 internal constant RedstoneOracle__InvalidParams = 64001;
/***********************************|
| Fallback Oracle |
|__________________________________*/
/// @notice thrown when an invalid parameter is passed to a method
uint256 internal constant FallbackOracle__InvalidParams = 65001;
/***********************************|
| FallbackCLRSOracle |
|__________________________________*/
/// @notice thrown when the exchange rate is zero, even for the fallback oracle source (if enabled)
uint256 internal constant FallbackCLRSOracle__ExchangeRateZero = 66001;
/***********************************|
| WstETHCLRSOracle |
|__________________________________*/
/// @notice thrown when the exchange rate is zero, even for the fallback oracle source (if enabled)
uint256 internal constant WstETHCLRSOracle__ExchangeRateZero = 67001;
/***********************************|
| CLFallbackUniV3Oracle |
|__________________________________*/
/// @notice thrown when the exchange rate is zero, even for the uniV3 rate
uint256 internal constant CLFallbackUniV3Oracle__ExchangeRateZero = 68001;
/***********************************|
| WstETHCLRS2UniV3CheckCLRSOracle |
|__________________________________*/
/// @notice thrown when the exchange rate is zero, even for the uniV3 rate
uint256 internal constant WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero = 69001;
/***********************************|
| WeETh Oracle |
|__________________________________*/
/// @notice thrown when an invalid parameter is passed to a method
uint256 internal constant WeETHOracle__InvalidParams = 70001;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IFluidOracle } from "./interfaces/iFluidOracle.sol";
/// @title FluidOracle
/// @notice Base contract that any Fluid Oracle must implement
abstract contract FluidOracle is IFluidOracle {
/// @inheritdoc IFluidOracle
function getExchangeRate() external view virtual returns (uint256 exchangeRate_);
/// @inheritdoc IFluidOracle
function getExchangeRateOperate() external view virtual returns (uint256 exchangeRate_);
/// @inheritdoc IFluidOracle
function getExchangeRateLiquidate() external view virtual returns (uint256 exchangeRate_);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { ErrorTypes } from "../errorTypes.sol";
import { IChainlinkAggregatorV3 } from "../interfaces/external/IChainlinkAggregatorV3.sol";
import { Error as OracleError } from "../error.sol";
import { OracleUtils } from "../libraries/oracleUtils.sol";
import { ChainlinkStructs } from "./structs.sol";
/// @title Chainlink Oracle implementation
/// @notice This contract is used to get the exchange rate via up to 3 hops at Chainlink price feeds.
/// The rate is multiplied with the previous rate at each hop.
/// E.g. to go from wBTC to USDC (assuming rates for example):
/// 1. wBTC -> BTC https://data.chain.link/ethereum/mainnet/crypto-other/wbtc-btc, rate: 0.92.
/// 2. BTC -> USD https://data.chain.link/ethereum/mainnet/crypto-usd/btc-usd rate: 30,000.
/// 3. USD -> USDC https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd rate: 0.98. Must invert feed: 1.02
/// finale rate would be: 0.92 * 30,000 * 1.02 = 28,152
abstract contract ChainlinkOracleImpl is OracleError, ChainlinkStructs {
/// @notice Chainlink price feed 1 to check for the exchange rate
IChainlinkAggregatorV3 internal immutable _CHAINLINK_FEED1;
/// @notice Chainlink price feed 2 to check for the exchange rate
IChainlinkAggregatorV3 internal immutable _CHAINLINK_FEED2;
/// @notice Chainlink price feed 3 to check for the exchange rate
IChainlinkAggregatorV3 internal immutable _CHAINLINK_FEED3;
/// @notice Flag to invert the price or not for feed 1 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)
bool internal immutable _CHAINLINK_INVERT_RATE1;
/// @notice Flag to invert the price or not for feed 2 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)
bool internal immutable _CHAINLINK_INVERT_RATE2;
/// @notice Flag to invert the price or not for feed 3 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)
bool internal immutable _CHAINLINK_INVERT_RATE3;
/// @notice constant value for price scaling to reduce gas usage for feed 1
uint256 internal immutable _CHAINLINK_PRICE_SCALER_MULTIPLIER1;
/// @notice constant value for inverting price to reduce gas usage for feed 1
uint256 internal immutable _CHAINLINK_INVERT_PRICE_DIVIDEND1;
/// @notice constant value for price scaling to reduce gas usage for feed 2
uint256 internal immutable _CHAINLINK_PRICE_SCALER_MULTIPLIER2;
/// @notice constant value for inverting price to reduce gas usage for feed 2
uint256 internal immutable _CHAINLINK_INVERT_PRICE_DIVIDEND2;
/// @notice constant value for price scaling to reduce gas usage for feed 3
uint256 internal immutable _CHAINLINK_PRICE_SCALER_MULTIPLIER3;
/// @notice constant value for inverting price to reduce gas usage for feed 3
uint256 internal immutable _CHAINLINK_INVERT_PRICE_DIVIDEND3;
/// @notice constructor sets the Chainlink price feed and invertRate flag for each hop.
/// E.g. `invertRate_` should be true if for the USDC/ETH pool it's expected that the oracle returns USDC per 1 ETH
constructor(ChainlinkConstructorParams memory params_) {
if (
(params_.hops < 1 || params_.hops > 3) || // hops must be 1, 2 or 3
(address(params_.feed1.feed) == address(0) || params_.feed1.token0Decimals == 0) || // first feed must always be defined
(params_.hops > 1 && (address(params_.feed2.feed) == address(0) || params_.feed2.token0Decimals == 0)) || // if hops > 1, feed 2 must be defined
(params_.hops > 2 && (address(params_.feed3.feed) == address(0) || params_.feed3.token0Decimals == 0)) // if hops > 2, feed 3 must be defined
) {
revert FluidOracleError(ErrorTypes.ChainlinkOracle__InvalidParams);
}
_CHAINLINK_FEED1 = params_.feed1.feed;
_CHAINLINK_FEED2 = params_.feed2.feed;
_CHAINLINK_FEED3 = params_.feed3.feed;
_CHAINLINK_INVERT_RATE1 = params_.feed1.invertRate;
_CHAINLINK_INVERT_RATE2 = params_.feed2.invertRate;
_CHAINLINK_INVERT_RATE3 = params_.feed3.invertRate;
// Actual desired output rate example USDC/ETH (6 decimals / 18 decimals).
// Note ETH has 12 decimals more than USDC.
// 0.000515525322211842331991619857165357691 // 39 decimals. ETH for 1 USDC
// 1954.190000000000433 // 15 decimals. USDC for 1 ETH
// to get to PRICE_SCLAER_MULTIPLIER and INVERT_PRICE_DIVIDEND:
// fetched Chainlink price is in token1Decimals per 1 token0Decimals.
// E.g. for an USDC/ETH price feed it's in ETH 18 decimals.
// for an BTC/USD price feed it's in USD 8 decimals.
// So to scale to 1e27 we need to multiply by 1e27 - token0Decimals.
// E.g. for USDC/ETH it would be: fetchedPrice * 1e21
//
// or for inverted (x token0 per 1 token1), formula would be:
// = 1e27 * 10**token0Decimals / fetchedPrice
// E.g. for USDC/ETH it would be: 1e33 / fetchedPrice
// no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case
_CHAINLINK_PRICE_SCALER_MULTIPLIER1 = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed1.token0Decimals);
_CHAINLINK_INVERT_PRICE_DIVIDEND1 = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed1.token0Decimals);
_CHAINLINK_PRICE_SCALER_MULTIPLIER2 = params_.hops > 1
? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed2.token0Decimals)
: 1;
_CHAINLINK_INVERT_PRICE_DIVIDEND2 = params_.hops > 1
? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed2.token0Decimals)
: 1;
_CHAINLINK_PRICE_SCALER_MULTIPLIER3 = params_.hops > 2
? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed3.token0Decimals)
: 1;
_CHAINLINK_INVERT_PRICE_DIVIDEND3 = params_.hops > 2
? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed3.token0Decimals)
: 1;
}
/// @dev Get the exchange rate from Chainlike oracle price feed(s)
/// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`
function _getChainlinkExchangeRate() internal view returns (uint256 rate_) {
rate_ = _readFeedRate(
_CHAINLINK_FEED1,
_CHAINLINK_INVERT_RATE1,
_CHAINLINK_PRICE_SCALER_MULTIPLIER1,
_CHAINLINK_INVERT_PRICE_DIVIDEND1
);
if (rate_ == 0 || address(_CHAINLINK_FEED2) == address(0)) {
// rate 0 or only 1 hop -> return rate of price feed 1
return rate_;
}
rate_ =
(rate_ *
_readFeedRate(
_CHAINLINK_FEED2,
_CHAINLINK_INVERT_RATE2,
_CHAINLINK_PRICE_SCALER_MULTIPLIER2,
_CHAINLINK_INVERT_PRICE_DIVIDEND2
)) /
(10 ** OracleUtils.RATE_OUTPUT_DECIMALS);
if (rate_ == 0 || address(_CHAINLINK_FEED3) == address(0)) {
// rate 0 or 2 hops -> return rate of feed 1 combined with feed 2
return rate_;
}
// 3 hops -> return rate of feed 1 combined with feed 2 & feed 3
rate_ =
(rate_ *
_readFeedRate(
_CHAINLINK_FEED3,
_CHAINLINK_INVERT_RATE3,
_CHAINLINK_PRICE_SCALER_MULTIPLIER3,
_CHAINLINK_INVERT_PRICE_DIVIDEND3
)) /
(10 ** OracleUtils.RATE_OUTPUT_DECIMALS);
}
/// @dev reads the exchange `rate_` from a Chainlink price `feed_` taking into account scaling and `invertRate_`
function _readFeedRate(
IChainlinkAggregatorV3 feed_,
bool invertRate_,
uint256 priceMultiplier_,
uint256 invertDividend_
) private view returns (uint256 rate_) {
try feed_.latestRoundData() returns (uint80, int256 exchangeRate_, uint256, uint256, uint80) {
// Return the price in `OracleUtils.RATE_OUTPUT_DECIMALS`
if (invertRate_) {
return invertDividend_ / uint256(exchangeRate_);
} else {
return uint256(exchangeRate_) * priceMultiplier_;
}
} catch {
return 0;
}
}
/// @notice returns all Chainlink oracle related data as utility for easy off-chain use / block explorer in a single view method
function chainlinkOracleData()
public
view
returns (
uint256 chainlinkExchangeRate_,
IChainlinkAggregatorV3 chainlinkFeed1_,
bool chainlinkInvertRate1_,
uint256 chainlinkExchangeRate1_,
IChainlinkAggregatorV3 chainlinkFeed2_,
bool chainlinkInvertRate2_,
uint256 chainlinkExchangeRate2_,
IChainlinkAggregatorV3 chainlinkFeed3_,
bool chainlinkInvertRate3_,
uint256 chainlinkExchangeRate3_
)
{
return (
_getChainlinkExchangeRate(),
_CHAINLINK_FEED1,
_CHAINLINK_INVERT_RATE1,
_readFeedRate(
_CHAINLINK_FEED1,
_CHAINLINK_INVERT_RATE1,
_CHAINLINK_PRICE_SCALER_MULTIPLIER1,
_CHAINLINK_INVERT_PRICE_DIVIDEND1
),
_CHAINLINK_FEED2,
_CHAINLINK_INVERT_RATE2,
address(_CHAINLINK_FEED2) == address(0)
? 0
: _readFeedRate(
_CHAINLINK_FEED2,
_CHAINLINK_INVERT_RATE2,
_CHAINLINK_PRICE_SCALER_MULTIPLIER2,
_CHAINLINK_INVERT_PRICE_DIVIDEND2
),
_CHAINLINK_FEED3,
_CHAINLINK_INVERT_RATE3,
address(_CHAINLINK_FEED3) == address(0)
? 0
: _readFeedRate(
_CHAINLINK_FEED3,
_CHAINLINK_INVERT_RATE3,
_CHAINLINK_PRICE_SCALER_MULTIPLIER3,
_CHAINLINK_INVERT_PRICE_DIVIDEND3
)
);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { ErrorTypes } from "../errorTypes.sol";
import { IRedstoneOracle } from "../interfaces/external/IRedstoneOracle.sol";
import { Error as OracleError } from "../error.sol";
import { ChainlinkOracleImpl } from "./chainlinkOracleImpl.sol";
import { RedstoneOracleImpl } from "./redstoneOracleImpl.sol";
/// @title Fallback Oracle implementation
/// @notice This contract is used to get the exchange rate from a main oracle feed and a fallback oracle feed.
//
// @dev inheriting contracts should implement a view method to expose `_FALLBACK_ORACLE_MAIN_SOURCE`
abstract contract FallbackOracleImpl is OracleError, RedstoneOracleImpl, ChainlinkOracleImpl {
/// @dev which oracle to use as main source:
/// - 1 = Chainlink ONLY (no fallback)
/// - 2 = Chainlink with Redstone Fallback
/// - 3 = Redstone with Chainlink Fallback
uint8 internal immutable _FALLBACK_ORACLE_MAIN_SOURCE;
/// @notice sets the main source, Chainlink Oracle and Redstone Oracle data.
/// @param mainSource_ which oracle to use as main source:
/// - 1 = Chainlink ONLY (no fallback)
/// - 2 = Chainlink with Redstone Fallback
/// - 3 = Redstone with Chainlink Fallback
/// @param chainlinkParams_ chainlink Oracle constructor params struct.
/// @param redstoneOracle_ Redstone Oracle data. (address can be set to zero address if using Chainlink only)
constructor(
uint8 mainSource_,
ChainlinkConstructorParams memory chainlinkParams_,
RedstoneOracleData memory redstoneOracle_
)
ChainlinkOracleImpl(chainlinkParams_)
RedstoneOracleImpl(
address(redstoneOracle_.oracle) == address(0)
? RedstoneOracleData(IRedstoneOracle(_REDSTONE_ORACLE_NOT_SET_ADDRESS), false, 1)
: redstoneOracle_
)
{
if (mainSource_ < 1 || mainSource_ > 3) {
revert FluidOracleError(ErrorTypes.FallbackOracle__InvalidParams);
}
_FALLBACK_ORACLE_MAIN_SOURCE = mainSource_;
}
/// @dev returns the exchange rate for the main oracle source, or the fallback source (if configured) if the main exchange rate
/// fails to be fetched. If returned rate is 0, fetching rate failed or something went wrong.
/// @return exchangeRate_ exchange rate
/// @return fallback_ whether fallback was necessary or not
function _getRateWithFallback() internal view returns (uint256 exchangeRate_, bool fallback_) {
if (_FALLBACK_ORACLE_MAIN_SOURCE == 1) {
// 1 = Chainlink ONLY (no fallback)
exchangeRate_ = _getChainlinkExchangeRate();
} else if (_FALLBACK_ORACLE_MAIN_SOURCE == 2) {
// 2 = Chainlink with Redstone Fallback
exchangeRate_ = _getChainlinkExchangeRate();
if (exchangeRate_ == 0) {
fallback_ = true;
exchangeRate_ = _getRedstoneExchangeRate();
}
} else {
// 3 = Redstone with Chainlink Fallback
exchangeRate_ = _getRedstoneExchangeRate();
if (exchangeRate_ == 0) {
fallback_ = true;
exchangeRate_ = _getChainlinkExchangeRate();
}
}
}
/// @dev returns the exchange rate for Chainlink, or Redstone if configured & Chainlink fails.
function _getChainlinkOrRedstoneAsFallback() internal view returns (uint256 exchangeRate_) {
exchangeRate_ = _getChainlinkExchangeRate();
if (exchangeRate_ == 0 && _FALLBACK_ORACLE_MAIN_SOURCE != 1) {
// Chainlink failed but Redstone is configured too -> try Redstone
exchangeRate_ = _getRedstoneExchangeRate();
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { ErrorTypes } from "../errorTypes.sol";
import { IRedstoneOracle } from "../interfaces/external/IRedstoneOracle.sol";
import { Error as OracleError } from "../error.sol";
import { OracleUtils } from "../libraries/oracleUtils.sol";
import { RedstoneStructs } from "./structs.sol";
/// @title Redstone Oracle implementation
/// @notice This contract is used to get the exchange rate from a Redstone Oracle
abstract contract RedstoneOracleImpl is OracleError, RedstoneStructs {
/// @notice Redstone price oracle to check for the exchange rate
IRedstoneOracle internal immutable _REDSTONE_ORACLE;
/// @notice Flag to invert the price or not (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)
bool internal immutable _REDSTONE_INVERT_RATE;
/// @notice constant value for price scaling to reduce gas usage
uint256 internal immutable _REDSTONE_PRICE_SCALER_MULTIPLIER;
/// @notice constant value for inverting price to reduce gas usage
uint256 internal immutable _REDSTONE_INVERT_PRICE_DIVIDEND;
address internal immutable _REDSTONE_ORACLE_NOT_SET_ADDRESS = 0x000000000000000000000000000000000000dEaD;
/// @notice constructor sets the Redstone oracle data
constructor(RedstoneOracleData memory oracleData_) {
if (address(oracleData_.oracle) == address(0) || oracleData_.token0Decimals == 0) {
revert FluidOracleError(ErrorTypes.RedstoneOracle__InvalidParams);
}
_REDSTONE_ORACLE = oracleData_.oracle;
_REDSTONE_INVERT_RATE = oracleData_.invertRate;
// for explanation on how to get to scaler multiplier and dividend see `chainlinkOracleImpl.sol`.
// no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case
_REDSTONE_PRICE_SCALER_MULTIPLIER = address(oracleData_.oracle) == _REDSTONE_ORACLE_NOT_SET_ADDRESS
? 1
: 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - oracleData_.token0Decimals);
_REDSTONE_INVERT_PRICE_DIVIDEND = address(oracleData_.oracle) == _REDSTONE_ORACLE_NOT_SET_ADDRESS
? 1
: 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + oracleData_.token0Decimals);
}
/// @dev Get the exchange rate from Redstone oracle
/// @param rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`
function _getRedstoneExchangeRate() internal view returns (uint256 rate_) {
try _REDSTONE_ORACLE.getExchangeRate() returns (uint256 exchangeRate_) {
if (_REDSTONE_INVERT_RATE) {
// invert the price
return _REDSTONE_INVERT_PRICE_DIVIDEND / exchangeRate_;
} else {
return exchangeRate_ * _REDSTONE_PRICE_SCALER_MULTIPLIER;
}
} catch {
return 0;
}
}
/// @notice returns all Redstone oracle related data as utility for easy off-chain use / block explorer in a single view method
function redstoneOracleData()
public
view
returns (uint256 redstoneExchangeRate_, IRedstoneOracle redstoneOracle_, bool redstoneInvertRate_)
{
return (
address(_REDSTONE_ORACLE) == _REDSTONE_ORACLE_NOT_SET_ADDRESS ? 0 : _getRedstoneExchangeRate(),
_REDSTONE_ORACLE,
_REDSTONE_INVERT_RATE
);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IChainlinkAggregatorV3 } from "../interfaces/external/IChainlinkAggregatorV3.sol";
import { IRedstoneOracle } from "../interfaces/external/IRedstoneOracle.sol";
abstract contract ChainlinkStructs {
struct ChainlinkFeedData {
/// @param feed address of Chainlink feed.
IChainlinkAggregatorV3 feed;
/// @param invertRate true if rate read from price feed must be inverted.
bool invertRate;
/// @param token0Decimals decimals of asset 0. E.g. for a USDC/ETH feed, USDC is token0 and has 6 decimals.
/// (token1Decimals are available directly via Chainlink `FEED.decimals()`)
uint256 token0Decimals;
}
struct ChainlinkConstructorParams {
/// @param param hops count of hops, used for sanity checks. Must be 1, 2 or 3.
uint8 hops;
/// @param feed1 Chainlink feed 1 data. Required.
ChainlinkFeedData feed1;
/// @param feed2 Chainlink feed 2 data. Required if hops > 1.
ChainlinkFeedData feed2;
/// @param feed3 Chainlink feed 3 data. Required if hops > 2.
ChainlinkFeedData feed3;
}
}
abstract contract RedstoneStructs {
struct RedstoneOracleData {
/// @param oracle address of Redstone oracle.
IRedstoneOracle oracle;
/// @param invertRate true if rate read from price feed must be inverted.
bool invertRate;
/// @param token0Decimals decimals of asset 0. E.g. for a USDC/ETH feed, USDC is token0 and has 6 decimals.
/// (token1Decimals are available directly via Redstone `Oracle.decimals()`)
uint256 token0Decimals;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { ErrorTypes } from "../errorTypes.sol";
import { FullMath } from "../libraries/FullMath.sol";
import { TickMath } from "../libraries/TickMath.sol";
import { OracleUtils } from "../libraries/oracleUtils.sol";
import { IUniswapV3Pool } from "../interfaces/external/IUniswapV3Pool.sol";
import { Error as OracleError } from "../error.sol";
/// @title Uniswap V3 Oracle implementation
/// @notice This contract is used to get the exchange rate from from a Uniswap V3 Pool,
/// including logic to check against TWAP max deltas.
/// @dev Uses 5 secondsAgos[] values and 3 TWAP maxDeltas:
/// e.g. [240, 60, 15, 1, 0] -> [price240to60, price60to15, price 15to1, currentPrice]
/// delta checks: price240to60 vs currentPrice, price60to15 vs currentPrice and 15to1 vs currentPrice.
abstract contract UniV3OracleImpl is OracleError {
/// @dev Uniswap V3 Pool to check for the exchange rate
IUniswapV3Pool internal immutable _POOL;
/// @dev Flag to invert the price or not (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)
bool internal immutable _UNIV3_INVERT_RATE;
/// @dev Uniswap oracle delta for TWAP1 in 1e2 percent. If uniswap price TWAP1 is out of this delta,
/// current price fetching reverts. E.g. for delta of TWAP 240 -> 60 vs current price
uint256 internal immutable _UNI_TWAP1_MAX_DELTA_PERCENT;
/// @dev Uniswap oracle delta for TWAP2 in 1e2 percent. If uniswap price TWAP2 is out of this delta,
/// current price fetching reverts. E.g. for delta of TWAP 60 -> 15 vs current price
uint256 internal immutable _UNI_TWAP2_MAX_DELTA_PERCENT;
/// @dev Uniswap oracle delta for TWAP3 in 1e2 percent. If uniswap price TWAP3 is out of this delta,
/// current price fetching reverts. E.g. for delta of TWAP 15 -> 1 vs current price
uint256 internal immutable _UNI_TWAP3_MAX_DELTA_PERCENT;
/// @dev Uniswap oracle seconds ago for twap, 1. value, e.g. 240
uint256 internal immutable _UNI_SECONDS_AGO_1;
/// @dev Uniswap oracle seconds ago for twap, 2. value, e.g. 60
uint256 internal immutable _UNI_SECONDS_AGO_2;
/// @dev Uniswap oracle seconds ago for twap, 3. value, e.g. 15
uint256 internal immutable _UNI_SECONDS_AGO_3;
/// @dev Uniswap oracle seconds ago for twap, 4. value, e.g. 1
uint256 internal immutable _UNI_SECONDS_AGO_4;
/// @dev Uniswap oracle seconds ago for twap, 5. value, e.g. 0
uint256 internal immutable _UNI_SECONDS_AGO_5;
/// @dev Uniswap TWAP1 interval duration.
int256 internal immutable _UNI_TWAP1_INTERVAL;
/// @dev Uniswap TWAP2 interval duration.
int256 internal immutable _UNI_TWAP2_INTERVAL;
/// @dev Uniswap TWAP3 interval duration.
int256 internal immutable _UNI_TWAP3_INTERVAL;
/// @dev Uniswap TWAP4 interval duration.
int256 internal immutable _UNI_TWAP4_INTERVAL;
/// @dev stored array lengths to optimize gas
uint256 internal constant _SECONDS_AGOS_LENGTH = 5;
uint256 internal constant _TWAP_DELTAS_LENGTH = 3;
/// @dev constant value for price scaling to reduce gas usage
uint256 internal immutable _UNIV3_PRICE_SCALER_MULTIPLIER;
/// @dev constant value for inverting price to reduce gas usage
uint256 internal immutable _UNIV3_INVERT_PRICE_DIVIDEND;
struct UniV3ConstructorParams {
/// @param pool Uniswap V3 Pool to check for the exchange rate
IUniswapV3Pool pool;
/// @param invertRate Flag to invert the Uniswap price or not
bool invertRate;
/// @param tWAPMaxDeltaPercents Uniswap oracle delta for TWAP1-2-3 in 1e2 percent
uint256[_TWAP_DELTAS_LENGTH] tWAPMaxDeltaPercents;
/// @param secondsAgos Uniswap oracle seconds ago for the 3 TWAP values, from oldest to newest, e.g. [240, 60, 15, 1, 0]
uint32[_SECONDS_AGOS_LENGTH] secondsAgos;
}
/// @notice constructor sets the Uniswap V3 `pool_` to check for the exchange rate and the `invertRate_` flag.
/// E.g. `invertRate_` should be true if for the WETH/USDC pool it's expected that the oracle returns USDC per 1 WETH
constructor(UniV3ConstructorParams memory params_) {
if (address(params_.pool) == address(0)) {
revert FluidOracleError(ErrorTypes.UniV3Oracle__InvalidParams);
}
// sanity check that seconds agos values are ordered ascending, e.g. [240, 60, 15, 1, 0]
if (
params_.secondsAgos[0] <= params_.secondsAgos[1] ||
params_.secondsAgos[1] <= params_.secondsAgos[2] ||
params_.secondsAgos[2] <= params_.secondsAgos[3] ||
params_.secondsAgos[3] <= params_.secondsAgos[4]
) {
revert FluidOracleError(ErrorTypes.UniV3Oracle__InvalidSecondsAgos);
}
// sanity check that deltas are less than 100% and decreasing (as timespan is closer to current price):
// 1. delta must < 100%
// all following deltas must be <= than the previous one
if (
params_.tWAPMaxDeltaPercents[0] >= OracleUtils.HUNDRED_PERCENT_DELTA_SCALER ||
params_.tWAPMaxDeltaPercents[1] > params_.tWAPMaxDeltaPercents[0] ||
params_.tWAPMaxDeltaPercents[2] > params_.tWAPMaxDeltaPercents[1]
) {
revert FluidOracleError(ErrorTypes.UniV3Oracle__InvalidDeltas);
}
_UNI_SECONDS_AGO_1 = uint256(params_.secondsAgos[0]);
_UNI_SECONDS_AGO_2 = uint256(params_.secondsAgos[1]);
_UNI_SECONDS_AGO_3 = uint256(params_.secondsAgos[2]);
_UNI_SECONDS_AGO_4 = uint256(params_.secondsAgos[3]);
_UNI_SECONDS_AGO_5 = uint256(params_.secondsAgos[4]);
_UNI_TWAP1_INTERVAL = int256(uint256(params_.secondsAgos[0] - params_.secondsAgos[1]));
_UNI_TWAP2_INTERVAL = int256(uint256(params_.secondsAgos[1] - params_.secondsAgos[2]));
_UNI_TWAP3_INTERVAL = int256(uint256(params_.secondsAgos[2] - params_.secondsAgos[3]));
_UNI_TWAP4_INTERVAL = int256(uint256(params_.secondsAgos[3] - params_.secondsAgos[4]));
_UNI_TWAP1_MAX_DELTA_PERCENT = params_.tWAPMaxDeltaPercents[0]; // e.g. for TWAP 240 -> 60 vs current price
_UNI_TWAP2_MAX_DELTA_PERCENT = params_.tWAPMaxDeltaPercents[1]; // e.g. for TWAP 60 -> 15 vs current price
_UNI_TWAP3_MAX_DELTA_PERCENT = params_.tWAPMaxDeltaPercents[2]; // e.g. for TWAP 15 -> 1 vs current price
_POOL = params_.pool;
_UNIV3_INVERT_RATE = params_.invertRate;
// uniswapV3 returned price is already scaled to token decimals.
_UNIV3_PRICE_SCALER_MULTIPLIER = 10 ** OracleUtils.RATE_OUTPUT_DECIMALS;
// uniV3 invert price dividend happens on the already scaled by 1e27 result for price in token1 per 1 token0
_UNIV3_INVERT_PRICE_DIVIDEND = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS * 2);
}
/// @dev Get the last exchange rate from the pool's last observed value without any checks
/// @return exchangeRateUnsafe_ The exchange rate between the underlying asset and the peg asset in `OracleUtils.RATE_OUTPUT_DECIMALS`
function _getUniV3ExchangeRateUnsafe() internal view returns (uint256 exchangeRateUnsafe_) {
(uint160 sqrtPriceX96_, , , , , , ) = _POOL.slot0();
exchangeRateUnsafe_ = _UNIV3_INVERT_RATE
? _invertUniV3Price(_getPriceFromSqrtPriceX96(sqrtPriceX96_))
: _getPriceFromSqrtPriceX96(sqrtPriceX96_);
}
/// @dev Get the last exchange rate from the pool's last observed value, checked against TWAP deviations.
/// @return exchangeRate_ The exchange rate between the underlying asset and the peg asset in `OracleUtils.RATE_OUTPUT_DECIMALS`
/// If 0 then the fetching the price failed or a delta was invalid.
function _getUniV3ExchangeRate() internal view returns (uint256 exchangeRate_) {
// build calldata bytes in a gas-optimized way without having to build an array / using abi.encode.
// gas efficient work around for Solidity not supporting immutable non-value types.
bytes memory data_ = abi.encodePacked(
hex"883bdbfd", // pack function selector
hex"0000000000000000000000000000000000000000000000000000000000000020", // pack start offset of dynamic array
_SECONDS_AGOS_LENGTH, // pack length of dynamic array
// pack seconds agos values:
_UNI_SECONDS_AGO_1,
_UNI_SECONDS_AGO_2,
_UNI_SECONDS_AGO_3,
_UNI_SECONDS_AGO_4,
_UNI_SECONDS_AGO_5
);
// get the tickCumulatives from Pool.observe()
(bool success_, bytes memory result_) = address(_POOL).staticcall(data_);
if (!success_) {
return 0;
}
int56[] memory tickCumulatives_ = abi.decode(result_, (int56[]));
unchecked {
int24 exchangeRateTick_;
{
int56 tickCumulativesDelta_ = (tickCumulatives_[_TWAP_DELTAS_LENGTH + 1] -
tickCumulatives_[_TWAP_DELTAS_LENGTH]);
// _UNI_TWAP4_INTERVAL can not be 0 because of constructor sanity checks
exchangeRateTick_ = int24(tickCumulativesDelta_ / _UNI_TWAP4_INTERVAL);
// Always round to negative infinity, see UniV3 OracleLibrary
// https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/libraries/OracleLibrary.sol#L36
if (tickCumulativesDelta_ < 0 && (tickCumulativesDelta_ % _UNI_TWAP4_INTERVAL != 0)) {
exchangeRateTick_--;
}
}
// Check the latest Uniswap price is within the acceptable delta from each TWAP range
// TWAP 1 check
if (
_isInvalidTWAPDelta(
int256(exchangeRateTick_),
tickCumulatives_[1] - tickCumulatives_[0],
_UNI_TWAP1_INTERVAL,
int256(_UNI_TWAP1_MAX_DELTA_PERCENT)
)
) {
return 0;
}
// TWAP 2 check
if (
_isInvalidTWAPDelta(
int256(exchangeRateTick_),
tickCumulatives_[2] - tickCumulatives_[1],
_UNI_TWAP2_INTERVAL,
int256(_UNI_TWAP2_MAX_DELTA_PERCENT)
)
) {
return 0;
}
// TWAP 3 check
if (
_isInvalidTWAPDelta(
int256(exchangeRateTick_),
tickCumulatives_[3] - tickCumulatives_[2],
_UNI_TWAP3_INTERVAL,
int256(_UNI_TWAP3_MAX_DELTA_PERCENT)
)
) {
return 0;
}
// get the current uniswap price, which is the last tick cumulatives interval, usually [..., 1, 0]
exchangeRate_ = _getPriceFromSqrtPriceX96(TickMath.getSqrtRatioAtTick(exchangeRateTick_));
if (_UNIV3_INVERT_RATE) {
exchangeRate_ = _invertUniV3Price(exchangeRate_);
}
}
}
/// @dev verifies that `exchangeRate_` is within `maxDelta_` for derived price from `tickCumulativesDelta_` and `interval_`.
/// returns true if delta is invalid
function _isInvalidTWAPDelta(
int256 exchangeRateTick_,
int256 tickCumulativesDelta_,
int256 interval_, // can not be 0 because of constructor sanity checks
int256 maxDelta_
) internal pure returns (bool) {
unchecked {
int256 arithmeticMeanTick_ = int256(tickCumulativesDelta_ / interval_);
// Always round to negative infinity, see UniV3 OracleLibrary
// https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/libraries/OracleLibrary.sol#L36
if (tickCumulativesDelta_ < 0 && (tickCumulativesDelta_ % interval_ != 0)) {
arithmeticMeanTick_--;
}
// Check that the uniswapPrice is within DELTA of the Uniswap TWAP (via tick)
// each univ3 tick is 0.01% increase or decrease in price. `maxDelta_` has near to same precision.
// Note: near to the same because each Uniswap tick is 0.01% away so price of ticks are if current one is 100 then next will be:
// 100 + 100 * 0.01% = 100.01
// 100.01 + 100.01 * 0.01% = 100.020001
if (
exchangeRateTick_ > (arithmeticMeanTick_ + maxDelta_) ||
exchangeRateTick_ < (arithmeticMeanTick_ - maxDelta_)
) {
// Uniswap last price is NOT within the delta
return true;
}
}
return false;
}
/// @notice returns all UniV3 oracle related data as utility for easy off-chain use / block explorer in a single view method
function uniV3OracleData()
public
view
returns (
IUniswapV3Pool uniV3Pool_,
bool uniV3InvertRate_,
uint32[] memory uniV3secondsAgos_,
uint256[] memory uniV3TwapDeltas_,
uint256 uniV3exchangeRateUnsafe_,
uint256 uniV3exchangeRate_
)
{
// Get the latest TWAP prices from the Uniswap Oracle for second intervals
uniV3secondsAgos_ = new uint32[](_SECONDS_AGOS_LENGTH);
uniV3secondsAgos_[0] = uint32(_UNI_SECONDS_AGO_1);
uniV3secondsAgos_[1] = uint32(_UNI_SECONDS_AGO_2);
uniV3secondsAgos_[2] = uint32(_UNI_SECONDS_AGO_3);
uniV3secondsAgos_[3] = uint32(_UNI_SECONDS_AGO_4);
uniV3secondsAgos_[4] = uint32(_UNI_SECONDS_AGO_5);
// Check the latest Uniswap price is within the acceptable delta from each TWAP range
uniV3TwapDeltas_ = new uint256[](_TWAP_DELTAS_LENGTH);
uniV3TwapDeltas_[0] = _UNI_TWAP1_MAX_DELTA_PERCENT;
uniV3TwapDeltas_[1] = _UNI_TWAP2_MAX_DELTA_PERCENT;
uniV3TwapDeltas_[2] = _UNI_TWAP3_MAX_DELTA_PERCENT;
return (
_POOL,
_UNIV3_INVERT_RATE,
uniV3secondsAgos_,
uniV3TwapDeltas_,
_getUniV3ExchangeRateUnsafe(),
_getUniV3ExchangeRate()
);
}
/// @dev Get the price from the sqrt price in `OracleUtils.RATE_OUTPUT_DECIMALS`
/// (see https://blog.uniswap.org/uniswap-v3-math-primer)
/// @param sqrtPriceX96_ The sqrt price to convert
function _getPriceFromSqrtPriceX96(uint160 sqrtPriceX96_) private view returns (uint256 priceX96_) {
return
FullMath.mulDiv(
uint256(sqrtPriceX96_) * uint256(sqrtPriceX96_),
_UNIV3_PRICE_SCALER_MULTIPLIER,
1 << 192 // 2^96 * 2
);
}
/// @dev Invert the price
/// @param price_ The price to invert
/// @return invertedPrice_ The inverted price in `OracleUtils.RATE_OUTPUT_DECIMALS`
function _invertUniV3Price(uint256 price_) private view returns (uint256 invertedPrice_) {
return _UNIV3_INVERT_PRICE_DIVIDEND / price_;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IWeETH } from "../interfaces/external/IWeETH.sol";
import { ErrorTypes } from "../errorTypes.sol";
import { Error as OracleError } from "../error.sol";
import { OracleUtils } from "../libraries/oracleUtils.sol";
/// @title weETH Oracle Implementation
/// @notice This contract is used to get the exchange rate between weETH and eETH
abstract contract WeETHOracleImpl is OracleError {
/// @notice constant value for price scaling to reduce gas usage
uint256 internal immutable _WEETH_PRICE_SCALER_MULTIPLIER;
/// @notice WEETH contract, e.g. on mainnet 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee
IWeETH internal immutable _WEETH;
/// @notice constructor sets the weETH (Etherfi's wrapped eETH) `weETH_` token address.
constructor(IWeETH weETH_) {
if (address(weETH_) == address(0)) {
revert FluidOracleError(ErrorTypes.WeETHOracle__InvalidParams);
}
_WEETH = weETH_;
_WEETH_PRICE_SCALER_MULTIPLIER = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - 18); // e.g. 1e9
}
/// @notice Get the exchange rate from weETH contract
/// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`
function _getWeETHExchangeRate() internal view returns (uint256 rate_) {
return _WEETH.getEETHByWeETH(1e18) * _WEETH_PRICE_SCALER_MULTIPLIER;
}
/// @notice returns all weETH oracle related data as utility for easy off-chain use / block explorer in a single view method
function weETHOracleData() public view returns (uint256 weETHExchangeRate_, IWeETH weETH_) {
return (_getWeETHExchangeRate(), _WEETH);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
/// from https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol
/// Copyright (c) 2018 SmartContract ChainLink, Ltd.
interface IChainlinkAggregatorV3 {
/// @notice represents the number of decimals the aggregator responses represent.
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
)
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
interface IRedstoneOracle {
/// @notice Get the `exchangeRate_` between the underlying asset and the peg asset
// @dev custom Redstone adapter for Instadapp implementation
function getExchangeRate() external view returns (uint256 exchangeRate_);
/**
* @notice Returns the number of decimals for the price feed
* @dev By default, RedStone uses 8 decimals for data feeds
* @return decimals The number of decimals in the price feed values
*/
// see https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol#L51C12-L51C20
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.21;
/// from https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces.
/// Copyright (c) 2022 Uniswap Labs
/// @title Pool state that never changes
/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
interface IUniswapV3PoolImmutables {
/// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
/// @return The contract address
function factory() external view returns (address);
/// @notice The first of the two tokens of the pool, sorted by address
/// @return The token contract address
function token0() external view returns (address);
/// @notice The second of the two tokens of the pool, sorted by address
/// @return The token contract address
function token1() external view returns (address);
/// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
/// @return The fee
function fee() external view returns (uint24);
/// @notice The pool tick spacing
/// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
/// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
/// This value is an int24 to avoid casting even though it is always positive.
/// @return The tick spacing
function tickSpacing() external view returns (int24);
/// @notice The maximum amount of position liquidity that can use any tick in the range
/// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
/// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
/// @return The max amount of liquidity per tick
function maxLiquidityPerTick() external view returns (uint128);
}
/// @title Pool state that is not stored
/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the
/// blockchain. The functions here may have variable gas costs.
interface IUniswapV3PoolDerivedState {
/// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
/// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
/// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
/// you must call it with secondsAgos = [3600, 0].
/// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
/// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
/// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
/// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
/// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
/// timestamp
function observe(
uint32[] calldata secondsAgos
) external view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
/// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
/// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
/// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
/// snapshot is taken and the second snapshot is taken.
/// @param tickLower The lower tick of the range
/// @param tickUpper The upper tick of the range
/// @return tickCumulativeInside The snapshot of the tick accumulator for the range
/// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
/// @return secondsInside The snapshot of seconds per liquidity for the range
function snapshotCumulativesInside(
int24 tickLower,
int24 tickUpper
) external view returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside);
}
/// @title Pool state that can change
/// @notice These methods compose the pool's state, and can change with any frequency including multiple times
/// per transaction
interface IUniswapV3PoolState {
/// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
/// when accessed externally.
/// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
/// tick The current tick of the pool, i.e. according to the last tick transition that was run.
/// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
/// boundary.
/// observationIndex The index of the last oracle observation that was written,
/// observationCardinality The current maximum number of observations stored in the pool,
/// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
/// feeProtocol The protocol fee for both tokens of the pool.
/// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
/// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
/// unlocked Whether the pool is currently locked to reentrancy
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);
/// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
/// @dev This value can overflow the uint256
function feeGrowthGlobal0X128() external view returns (uint256);
/// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
/// @dev This value can overflow the uint256
function feeGrowthGlobal1X128() external view returns (uint256);
/// @notice The amounts of token0 and token1 that are owed to the protocol
/// @dev Protocol fees will never exceed uint128 max in either token
function protocolFees() external view returns (uint128 token0, uint128 token1);
/// @notice The currently in range liquidity available to the pool
/// @dev This value has no relationship to the total liquidity across all ticks
function liquidity() external view returns (uint128);
/// @notice Look up information about a specific tick in the pool
/// @param tick The tick to look up
/// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
/// tick upper,
/// liquidityNet how much liquidity changes when the pool price crosses the tick,
/// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
/// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
/// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
/// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
/// secondsOutside the seconds spent on the other side of the tick from the current tick,
/// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
/// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
/// In addition, these values are only relative and must be used only in comparison to previous snapshots for
/// a specific position.
function ticks(
int24 tick
)
external
view
returns (
uint128 liquidityGross,
int128 liquidityNet,
uint256 feeGrowthOutside0X128,
uint256 feeGrowthOutside1X128,
int56 tickCumulativeOutside,
uint160 secondsPerLiquidityOutsideX128,
uint32 secondsOutside,
bool initialized
);
/// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
function tickBitmap(int16 wordPosition) external view returns (uint256);
/// @notice Returns the information about a position by the position's key
/// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
/// @return _liquidity The amount of liquidity in the position,
/// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
/// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
/// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
/// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
function positions(
bytes32 key
)
external
view
returns (
uint128 _liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
/// @notice Returns data about a specific observation index
/// @param index The element of the observations array to fetch
/// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
/// ago, rather than at a specific index in the array.
/// @return blockTimestamp The timestamp of the observation,
/// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
/// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
/// Returns initialized whether the observation has been initialized and the values are safe to use
function observations(
uint256 index
)
external
view
returns (
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulativeX128,
bool initialized
);
}
/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is IUniswapV3PoolImmutables, IUniswapV3PoolState, IUniswapV3PoolDerivedState {
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
interface IWeETH {
/**
* @notice Get amount of eETH for {_weETHAmount} weETH
* @return Amount of eETH for {_weETHAmount} weETH
*/
function getEETHByWeETH(uint256 _weETHAmount) external view returns (uint256);
/**
* @notice Get amount of weETH for {_eETHAmount} eETH
* @return Amount of weETH for {_eETHAmount} eETH
*/
function getWeETHByeETH(uint256 _eETHAmount) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
interface IFluidOracle {
/// @dev Deprecated. Use `getExchangeRateOperate()` and `getExchangeRateLiquidate()` instead. Only implemented for
/// backwards compatibility.
function getExchangeRate() external view returns (uint256 exchangeRate_);
/// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27 for operates
function getExchangeRateOperate() external view returns (uint256 exchangeRate_);
/// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27 for liquidations
function getExchangeRateLiquidate() external view returns (uint256 exchangeRate_);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
/// @dev Modified from the original UniswapV3 library to support v0.8
/// From: uint256 twos = -denominator & denominator;
/// To: uint256 twos = (type(uint256).max - denominator + 1) & denominator;
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
/// @dev This line was modified for v0.8.x
// uint256 twos = -denominator & denominator;
uint256 twos = (type(uint256).max - denominator + 1) & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
/// @title Oracle utils library
/// @notice implements common utility methods for Fluid Oracles
library OracleUtils {
/// @dev The scaler for max delta point math (100%)
uint256 internal constant HUNDRED_PERCENT_DELTA_SCALER = 10_000;
/// @dev output precision of rates
uint256 internal constant RATE_OUTPUT_DECIMALS = 27;
/// @dev checks if `mainSourceRate_` is within a `maxDeltaPercent_` of `checkSourceRate_`. Returns true if so.
function isRateOutsideDelta(
uint256 mainSourceRate_,
uint256 checkSourceRate_,
uint256 maxDeltaPercent_
) internal pure returns (bool) {
uint256 offset_ = (checkSourceRate_ * maxDeltaPercent_) / HUNDRED_PERCENT_DELTA_SCALER;
return (mainSourceRate_ > (checkSourceRate_ + offset_) || mainSourceRate_ < (checkSourceRate_ - offset_));
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.21;
/// @dev Modified from the original UniswapV3 library to support v0.8
/// From: require(absTick <= uint256(MAX_TICK), 'T');
/// To: require(absTick <= uint256(int(MAX_TICK)), 'T');
/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
/// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
int24 internal constant MIN_TICK = -887272;
/// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
int24 internal constant MAX_TICK = -MIN_TICK;
/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_RATIO = 4295128739;
/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
/// @notice Calculates sqrt(1.0001^tick) * 2^96
/// @dev Throws if |tick| > max tick
/// @param tick The input tick for the above formula
/// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
/// at the given tick
function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
/// @dev This line was modified for v0.8.x
// require(absTick <= uint256(MAX_TICK), 'T');
require(absTick <= uint256(int(MAX_TICK)), "T");
uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
if (tick > 0) ratio = type(uint256).max / ratio;
// this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
// we then downcast because we know the result always fits within 160 bits due to our tick input constraint
// we round up in the division so getTickAtSqrtRatio of the output price is always consistent
sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
}
/// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
/// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
/// ever return.
/// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
/// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
// second inequality must be < because the price can never reach the price at the max tick
require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, "R");
uint256 ratio = uint256(sqrtPriceX96) << 32;
uint256 r = ratio;
uint256 msb = 0;
assembly {
let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(5, gt(r, 0xFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(4, gt(r, 0xFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(3, gt(r, 0xFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(2, gt(r, 0xF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(1, gt(r, 0x3))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := gt(r, 0x1)
msb := or(msb, f)
}
if (msb >= 128) r = ratio >> (msb - 127);
else r = ratio << (127 - msb);
int256 log_2 = (int256(msb) - 128) << 64;
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(63, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(62, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(61, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(60, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(59, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(58, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(57, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(56, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(55, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(54, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(53, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(52, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(51, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(50, f))
}
int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number
int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);
tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { FluidOracle } from "../fluidOracle.sol";
import { FallbackOracleImpl } from "../implementations/fallbackOracleImpl.sol";
import { UniV3OracleImpl } from "../implementations/uniV3OracleImpl.sol";
import { ErrorTypes } from "../errorTypes.sol";
import { OracleUtils } from "../libraries/oracleUtils.sol";
/// @title UniswapV3 checked against Chainlink / Redstone Oracle. Either one reported as exchange rate.
/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:
/// the price from a UniV3 pool (compared against 3 TWAPs) and (optionally) comparing it against a Chainlink
/// or Redstone price (one of Chainlink or Redstone being the main source and the other one the fallback source).
/// Alternatively it can also use Chainlink / Redstone as main price and use UniV3 as check price.
/// @dev The process for getting the aggregate oracle price is:
/// 1. Fetch the UniV3 TWAPS, the latest interval is used as the current price
/// 2. Verify this price is within an acceptable DELTA from the Uniswap TWAPS e.g.:
/// a. 240 to 60s
/// b. 60 to 15s
/// c. 15 to 1s (last block)
/// d. 1 to 0s (current)
/// 3. (unless UniV3 only mode): Verify this price is within an acceptable DELTA from the Chainlink / Redstone Oracle
/// 4. If it passes all checks, return the price. Otherwise use fallbacks, usually to Chainlink. In extreme edge-cases revert.
/// @dev For UniV3 with check mode, if fetching the check price fails, the UniV3 rate is used directly.
contract UniV3CheckCLRSOracle is FluidOracle, UniV3OracleImpl, FallbackOracleImpl {
/// @dev Rate check oracle delta percent in 1e2 percent. If current uniswap price is out of this delta,
/// current price fetching reverts.
uint256 internal immutable _RATE_CHECK_MAX_DELTA_PERCENT;
/// @dev which oracle to use as final rate source:
/// - 1 = UniV3 ONLY (no check),
/// - 2 = UniV3 with Chainlink / Redstone check
/// - 3 = Chainlink / Redstone with UniV3 used as check.
uint8 internal immutable _RATE_SOURCE;
struct UniV3CheckCLRSConstructorParams {
/// @param uniV3Params UniV3Oracle constructor params struct.
UniV3ConstructorParams uniV3Params;
/// @param chainlinkParams ChainlinkOracle constructor params struct for UniV3CheckCLRSOracle.
ChainlinkConstructorParams chainlinkParams;
/// @param redstoneOracle Redstone Oracle data for UniV3CheckCLRSOracle. (address can be set to zero address if using Chainlink only)
RedstoneOracleData redstoneOracle;
/// @param rateSource which oracle to use as final rate source for UniV3CheckCLRSOracle:
/// - 1 = UniV3 ONLY (no check),
/// - 2 = UniV3 with Chainlink / Redstone check
/// - 3 = Chainlink / Redstone with UniV3 used as check.
uint8 rateSource;
/// @param fallbackMainSource which oracle to use as CL/RS main source for UniV3CheckCLRSOracle: see FallbackOracleImpl constructor `mainSource_`
uint8 fallbackMainSource;
/// @param rateCheckMaxDeltaPercent Rate check oracle delta in 1e2 percent for UniV3CheckCLRSOracle
uint256 rateCheckMaxDeltaPercent;
}
constructor(
UniV3CheckCLRSConstructorParams memory params_
)
UniV3OracleImpl(params_.uniV3Params)
FallbackOracleImpl(params_.fallbackMainSource, params_.chainlinkParams, params_.redstoneOracle)
{
if (
params_.rateSource < 1 ||
params_.rateSource > 3 ||
params_.rateCheckMaxDeltaPercent > OracleUtils.HUNDRED_PERCENT_DELTA_SCALER ||
// Chainlink only Oracle with UniV3 check. Delta would be ignored so revert this type of Oracle setup.
(params_.fallbackMainSource == 1 && params_.rateSource == 3)
) {
revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidParams);
}
_RATE_CHECK_MAX_DELTA_PERCENT = params_.rateCheckMaxDeltaPercent;
_RATE_SOURCE = params_.rateSource;
}
/// @inheritdoc FluidOracle
function getExchangeRateOperate() public view virtual override returns (uint256 exchangeRate_) {
return _getExchangeRate();
}
/// @inheritdoc FluidOracle
function getExchangeRateLiquidate() public view virtual override returns (uint256 exchangeRate_) {
return _getExchangeRate();
}
/// @inheritdoc FluidOracle
function getExchangeRate() public view virtual override returns (uint256 exchangeRate_) {
return _getExchangeRate();
}
/// @notice returns all oracle related data as utility for easy off-chain / block explorer use in a single view method
function uniV3CheckOracleData()
public
view
returns (uint256 rateCheckMaxDelta_, uint256 rateSource_, uint256 fallbackMainSource_)
{
return (_RATE_CHECK_MAX_DELTA_PERCENT, _RATE_SOURCE, _FALLBACK_ORACLE_MAIN_SOURCE);
}
function _getExchangeRate() internal view returns (uint256 exchangeRate_) {
if (_RATE_SOURCE == 1) {
// uniswap is the only main source without check:
// 1. get uniV3 rate.
// 2. If that fails (outside delta range) -> revert (no other Oracle configured).
exchangeRate_ = _getUniV3ExchangeRate();
if (exchangeRate_ == 0) {
// fetching UniV3 failed or invalid delta -> revert
revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);
}
return exchangeRate_;
}
uint256 checkRate_;
bool fallback_;
if (_RATE_SOURCE == 2) {
// uniswap is main source, with Chainlink / Redstone as check
// 1. get uniV3 rate
// case uniV3 rate fails (outside delta range):
// 2. get Chainlink rate. -> if successful, use Chainlink as result
// 3. if Chainlink fails too, get Redstone -> if successful, use Redstone as result
// 4. if Redstone fails too, revert
// case if uniV3 rate is ok
// 2. get Chainlink or Redstone rate for check (one is configured as main check source, other one is fallback source)
// -> if both fail to fetch, use uniV3 rate directly.
// 3. check the delta for uniV3 rate against the check soure rate. -> if ok, return uniV3 rate
// 4. if delta check fails, check delta against the fallback check source. -> if ok, return uniV3 rate
// 5. if delta check fails for both sources, return Chainlink price
exchangeRate_ = _getUniV3ExchangeRate();
if (exchangeRate_ == 0) {
// uniV3 failed or invalid delta -> use (Chainlink with Redstone as fallback)
exchangeRate_ = _getChainlinkOrRedstoneAsFallback();
if (exchangeRate_ == 0) {
// Chainlink / Redstone failed too -> revert
revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);
}
return exchangeRate_;
}
(checkRate_, fallback_) = _getRateWithFallback();
if (checkRate_ == 0) {
// check price source failed to fetch -> directly use uniV3 TWAP checked price
// Note uniV3 price fetching was successful, would have been caught otherwise above.
return exchangeRate_;
}
} else {
// Chainlink / Redstone is main source, with uniV3 as check.
// 1. get Chainlink / Redstone rate (one is configured as main source, other one is fallback source)
// case when both Chainlink & Redstone fail:
// 2. get uniV3 rate. if successful, use uniV3 rate. otherwise, revert (all oracles failed).
// case when Chainlink / Redstone fetch is successful:
// 2. get uniV3 rate for check.
// 3. if uniV3 rate fails to fetch (outside delta), use Chainlink / Redstone directly (skip check).
// 4. if uniV3 rate is ok, check the delta for Chainlink / Redstone rate against uniV3 rate.
// -> if ok, return Chainlink / Redstone (main) rate
// 5. if delta check fails, check delta against the fallback main source.
// -> if ok, return fallback main rate
// 6. if delta check fails for both sources, return Chainlink price.
(exchangeRate_, fallback_) = _getRateWithFallback();
checkRate_ = _getUniV3ExchangeRate();
if (exchangeRate_ == 0) {
if (checkRate_ == 0) {
// all oracles failed, revert
revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__ExchangeRateZero);
}
// Both Chainlink & Redstone failed -> directly use uniV3 TWAP checked price
// Note uniV3 price fetching was successful, would have been caught otherwise above.
return checkRate_;
}
if (checkRate_ == 0) {
// uniV3 failed -> skip check against Uniswap price.
return exchangeRate_;
}
}
if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {
if (fallback_) {
// fallback already used, no other rate available to check.
// if price is chainlink price -> return it.
if (_FALLBACK_ORACLE_MAIN_SOURCE == 3) {
// redstone with Chainlink as fallback
return _RATE_SOURCE == 2 ? checkRate_ : exchangeRate_; // if rate source is 2, Chainlink rate is in checkRate_
}
// if price is redstone price -> revert
revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidPrice);
}
if (_FALLBACK_ORACLE_MAIN_SOURCE == 1) {
// 1 = only chainlink and UniV3 is configured and delta check failed. no fallback available.
if (_RATE_SOURCE == 2) {
// case where uniV3 is main source with only Chainlink as check rate Oracle configured.
// delta check failed -> return Chainlink price (instead of uniV3 price).
return checkRate_;
}
// here: if (_FALLBACK_ORACLE_MAIN_SOURCE == 1 && _RATE_SOURCE == 3)
// rate source is 3: Chainlink as main, uniV3 as delta. delta check failed.
// this Oracle type would basically be a more expensive Chainlink-only Oracle because the delta check against UniV3 is ignored.
// this setup is reverted in constructor, but in any case returning Chainlink price here even though this code should never be reached.
return exchangeRate_; // exchangeRate_ here is chainlink price
}
// fallback not done yet -> check against fallback price.
// So if originally Chainlink was fetched and delta failed, check against Redstone.
// if originally Redstone was fetched and delta failed, check against Chainlink.
if (_FALLBACK_ORACLE_MAIN_SOURCE == 2) {
// 2 = Chainlink with Redstone Fallback. delta check against Chainlink failed. try against Redstone.
uint256 redstoneRate_ = _getRedstoneExchangeRate();
uint256 chainlinkRate_;
if (_RATE_SOURCE == 2) {
// uniV3 main source. -> update checkRate_ with Redstone price
chainlinkRate_ = checkRate_;
checkRate_ = redstoneRate_;
} else {
// uniV3 is check source. -> update exchangeRate_ with Redstone price
chainlinkRate_ = exchangeRate_;
exchangeRate_ = redstoneRate_;
}
if (redstoneRate_ == 0) {
// fetching Redstone failed. So delta UniV3 <> Chainlink failed, fetching Redstone as backup failed.
// -> return chainlink price (for both cases when Chainlink is main and when UniV3 is the main source).
return chainlinkRate_;
}
if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {
// delta check against Redstone failed too. return Chainlink price
return chainlinkRate_;
}
// delta check against Redstone passed. if uniV3 main source -> return uniV3, else return Redstone.
// exchangeRate_ is already set correctly for this.
} else {
// 3 = Redstone with Chainlink Fallback. delta check against Redstone failed. try against Chainlink.
uint256 chainlinkRate_ = _getChainlinkExchangeRate();
if (chainlinkRate_ == 0) {
// fetching Chainlink failed. So delta UniV3 <> Redstone failed, fetching Chainlink as backup check failed.
// -> revert.
revert FluidOracleError(ErrorTypes.UniV3CheckCLRSOracle__InvalidPrice);
}
if (_RATE_SOURCE == 3) {
// uniV3 is check source. -> update exchangeRate_ with Chainlink price.
// Optimization: in this case we can directly return chainlink price, because if delta check between
// Chainlink (new main source) and uniV3 (check source) fails, we anyway return Chainlink price still.
return chainlinkRate_;
}
// uniV3 main source. -> update checkRate_ with Chainlink price and compare delta again
checkRate_ = chainlinkRate_;
if (OracleUtils.isRateOutsideDelta(exchangeRate_, checkRate_, _RATE_CHECK_MAX_DELTA_PERCENT)) {
// delta check against Chainlink failed too. case here can only be where uniV3 would have been
// main source and Chainlink check source. -> return Chainlink as price instead of uniV3
return checkRate_;
}
// delta check against Chainlink passed. if uniV3 main source -> return uniV3, else return Chainlink.
// exchangeRate_ is already set correctly for this.
}
}
}
}{
"optimizer": {
"enabled": true,
"runs": 10000000
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract IWeETH","name":"weETH_","type":"address"},{"components":[{"components":[{"internalType":"contract IUniswapV3Pool","name":"pool","type":"address"},{"internalType":"bool","name":"invertRate","type":"bool"},{"internalType":"uint256[3]","name":"tWAPMaxDeltaPercents","type":"uint256[3]"},{"internalType":"uint32[5]","name":"secondsAgos","type":"uint32[5]"}],"internalType":"struct UniV3OracleImpl.UniV3ConstructorParams","name":"uniV3Params","type":"tuple"},{"components":[{"internalType":"uint8","name":"hops","type":"uint8"},{"components":[{"internalType":"contract IChainlinkAggregatorV3","name":"feed","type":"address"},{"internalType":"bool","name":"invertRate","type":"bool"},{"internalType":"uint256","name":"token0Decimals","type":"uint256"}],"internalType":"struct ChainlinkStructs.ChainlinkFeedData","name":"feed1","type":"tuple"},{"components":[{"internalType":"contract IChainlinkAggregatorV3","name":"feed","type":"address"},{"internalType":"bool","name":"invertRate","type":"bool"},{"internalType":"uint256","name":"token0Decimals","type":"uint256"}],"internalType":"struct ChainlinkStructs.ChainlinkFeedData","name":"feed2","type":"tuple"},{"components":[{"internalType":"contract IChainlinkAggregatorV3","name":"feed","type":"address"},{"internalType":"bool","name":"invertRate","type":"bool"},{"internalType":"uint256","name":"token0Decimals","type":"uint256"}],"internalType":"struct ChainlinkStructs.ChainlinkFeedData","name":"feed3","type":"tuple"}],"internalType":"struct ChainlinkStructs.ChainlinkConstructorParams","name":"chainlinkParams","type":"tuple"},{"components":[{"internalType":"contract IRedstoneOracle","name":"oracle","type":"address"},{"internalType":"bool","name":"invertRate","type":"bool"},{"internalType":"uint256","name":"token0Decimals","type":"uint256"}],"internalType":"struct RedstoneStructs.RedstoneOracleData","name":"redstoneOracle","type":"tuple"},{"internalType":"uint8","name":"rateSource","type":"uint8"},{"internalType":"uint8","name":"fallbackMainSource","type":"uint8"},{"internalType":"uint256","name":"rateCheckMaxDeltaPercent","type":"uint256"}],"internalType":"struct UniV3CheckCLRSOracle.UniV3CheckCLRSConstructorParams","name":"uniV3CheckCLRSParams_","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidOracleError","type":"error"},{"inputs":[],"name":"chainlinkOracleData","outputs":[{"internalType":"uint256","name":"chainlinkExchangeRate_","type":"uint256"},{"internalType":"contract IChainlinkAggregatorV3","name":"chainlinkFeed1_","type":"address"},{"internalType":"bool","name":"chainlinkInvertRate1_","type":"bool"},{"internalType":"uint256","name":"chainlinkExchangeRate1_","type":"uint256"},{"internalType":"contract IChainlinkAggregatorV3","name":"chainlinkFeed2_","type":"address"},{"internalType":"bool","name":"chainlinkInvertRate2_","type":"bool"},{"internalType":"uint256","name":"chainlinkExchangeRate2_","type":"uint256"},{"internalType":"contract IChainlinkAggregatorV3","name":"chainlinkFeed3_","type":"address"},{"internalType":"bool","name":"chainlinkInvertRate3_","type":"bool"},{"internalType":"uint256","name":"chainlinkExchangeRate3_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExchangeRate","outputs":[{"internalType":"uint256","name":"exchangeRate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExchangeRateLiquidate","outputs":[{"internalType":"uint256","name":"exchangeRate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExchangeRateOperate","outputs":[{"internalType":"uint256","name":"exchangeRate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redstoneOracleData","outputs":[{"internalType":"uint256","name":"redstoneExchangeRate_","type":"uint256"},{"internalType":"contract IRedstoneOracle","name":"redstoneOracle_","type":"address"},{"internalType":"bool","name":"redstoneInvertRate_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniV3CheckOracleData","outputs":[{"internalType":"uint256","name":"rateCheckMaxDelta_","type":"uint256"},{"internalType":"uint256","name":"rateSource_","type":"uint256"},{"internalType":"uint256","name":"fallbackMainSource_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uniV3OracleData","outputs":[{"internalType":"contract IUniswapV3Pool","name":"uniV3Pool_","type":"address"},{"internalType":"bool","name":"uniV3InvertRate_","type":"bool"},{"internalType":"uint32[]","name":"uniV3secondsAgos_","type":"uint32[]"},{"internalType":"uint256[]","name":"uniV3TwapDeltas_","type":"uint256[]"},{"internalType":"uint256","name":"uniV3exchangeRateUnsafe_","type":"uint256"},{"internalType":"uint256","name":"uniV3exchangeRate_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weETHOracleData","outputs":[{"internalType":"uint256","name":"weETHExchangeRate_","type":"uint256"},{"internalType":"contract IWeETH","name":"weETH_","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
61054060405261dead610340523480156200001957600080fd5b5060405162003734380380620037348339810160408190526200003c91620009b8565b808060800151816020015182604001518160006001600160a01b031682600001516001600160a01b0316146200007357816200009f565b6040518060600160405280610340516001600160a01b0316815260200160001515815260200160018152505b8551886001600160a01b038116620000d45760405163c82fc46560e01b81526201117160048201526024015b60405180910390fd5b6001600160a01b03811660a052620000ef6012601b62000b1f565b620000fc90600a62000c38565b6080525080516001600160a01b03166200012e5760405163c82fc46560e01b815261f2316004820152602401620000cb565b60608101516020810151905163ffffffff91821691161115806200016e57506060810151604081015163ffffffff16906001602002015163ffffffff1611155b806200019657506060818101519081015163ffffffff16906002602002015163ffffffff1611155b80620001be57506060810151608081015163ffffffff16906003602002015163ffffffff1611155b15620001e25760405163c82fc46560e01b815261f2326004820152602401620000cb565b60408101515161271011158062000203575060408101518051602090910151115b806200021a57506040818101516020810151910151115b156200023e5760405163c82fc46560e01b815261f2336004820152602401620000cb565b606081810180515163ffffffff90811661016052815160209081015182166101805282516040015182166101a05282519093015181166101c052815160800151166101e0525190810151905162000296919062000c4d565b63ffffffff166102005260608101516040810151602090910151620002bc919062000c4d565b63ffffffff166102205260608181015190810151604090910151620002e2919062000c4d565b63ffffffff1661024052606081810151608081015191015162000306919062000c4d565b63ffffffff16610260526040818101805151610100528051602090810151610120529051909101516101405281516001600160a01b031660c052810151151560e05262000356601b600a62000c38565b6102805262000368601b600262000c74565b6200037590600a62000c38565b6102a0525080516001600160a01b031615806200039457506040810151155b15620003b85760405163c82fc46560e01b815261fa016004820152602401620000cb565b80516001600160a01b039081166102c052602082015115156102e052610340518251821691161462000409576040810151620003f690601b62000b1f565b6200040390600a62000c38565b6200040c565b60015b610300526103405181516001600160a01b039081169116146200044e5760408101516200043b90601b62000c8e565b6200044890600a62000c38565b62000451565b60015b61032052508051600160ff90911610806200047357506003816000015160ff16115b806200049c57506020810151516001600160a01b031615806200049c5750602081015160400151155b80620004d757506001816000015160ff16118015620004d757506040810151516001600160a01b03161580620004d757506040808201510151155b806200051357506002816000015160ff161180156200051357506060810151516001600160a01b03161580620005135750606081015160400151155b15620005375760405163c82fc46560e01b815261ee496004820152602401620000cb565b60208082018051516001600160a01b03908116610360526040808501805151831661038052606086018051519093166103a052835185015115156103c0525184015115156103e0529051909201511515610400525101516200059b90601b62000b1f565b620005a890600a62000c38565b61042052602081015160400151620005c290601b62000c8e565b620005cf90600a62000c38565b610440528051600160ff90911611620005ea5760016200060c565b6040808201510151620005ff90601b62000b1f565b6200060c90600a62000c38565b610460528051600160ff909116116200062757600162000649565b60408082015101516200063c90601b62000c8e565b6200064990600a62000c38565b610480528051600260ff909116116200066457600162000687565b6060810151604001516200067a90601b62000b1f565b6200068790600a62000c38565b6104a0528051600260ff90911611620006a2576001620006c5565b606081015160400151620006b890601b62000c8e565b620006c590600a62000c38565b6104c05250600160ff84161080620006e0575060038360ff16115b15620007045760405163c82fc46560e01b815261fde96004820152602401620000cb565b505060ff9081166104e05260608201516001911610806200072c57506003816060015160ff16115b806200073d57506127108160a00151115b80620007635750806080015160ff166001148015620007635750806060015160ff166003145b15620007875760405163c82fc46560e01b815261ea626004820152602401620000cb565b60a0810151610500526060015160ff16610520525062000ca49050565b6001600160a01b0381168114620007ba57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715620007f857620007f8620007bd565b60405290565b604051608081016001600160401b0381118282101715620007f857620007f8620007bd565b60405160c081016001600160401b0381118282101715620007f857620007f8620007bd565b805180151581146200085957600080fd5b919050565b600082601f8301126200087057600080fd5b60405160a081016001600160401b0381118282101715620008955762000895620007bd565b6040528060a0840185811115620008ab57600080fd5b845b81811015620008db57805163ffffffff81168114620008cc5760008081fd5b835260209283019201620008ad565b509195945050505050565b805160ff811681146200085957600080fd5b6000606082840312156200090b57600080fd5b62000915620007d3565b905081516200092481620007a4565b8152620009346020830162000848565b60208201526040820151604082015292915050565b600061014082840312156200095d57600080fd5b62000967620007fe565b90506200097482620008e6565b8152620009858360208401620008f8565b6020820152620009998360808401620008f8565b6040820152620009ad8360e08401620008f8565b606082015292915050565b600080828403610360811215620009ce57600080fd5b8351620009db81620007a4565b9250601f1901602061034080831215620009f457600080fd5b620009fe62000823565b61014084121562000a0e57600080fd5b62000a18620007fe565b93508287015162000a2981620007a4565b845262000a396040880162000848565b8385015287607f88011262000a4d57600080fd5b62000a57620007d3565b8060c089018a81111562000a6a57600080fd5b60608a015b8181101562000a88578051845292860192860162000a6f565b5081604088015262000a9b8b826200085e565b606088015250505083815262000ab688610160890162000949565b8382015262000aca886102a08901620008f8565b604082015262000ade6103008801620008e6565b606082015262000af26103208801620008e6565b608082015295015160a08601525091949293505050565b634e487b7160e01b600052601160045260246000fd5b8181038181111562000b355762000b3562000b09565b92915050565b600181815b8085111562000b7c57816000190482111562000b605762000b6062000b09565b8085161562000b6e57918102915b93841c939080029062000b40565b509250929050565b60008262000b955750600162000b35565b8162000ba45750600062000b35565b816001811462000bbd576002811462000bc85762000be8565b600191505062000b35565b60ff84111562000bdc5762000bdc62000b09565b50506001821b62000b35565b5060208310610133831016604e8410600b841016171562000c0d575081810a62000b35565b62000c19838362000b3b565b806000190482111562000c305762000c3062000b09565b029392505050565b600062000c46838362000b84565b9392505050565b63ffffffff82811682821603908082111562000c6d5762000c6d62000b09565b5092915050565b808202811582820484141762000b355762000b3562000b09565b8082018082111562000b355762000b3562000b09565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e05161030051610320516103405161036051610380516103a0516103c0516103e05161040051610420516104405161046051610480516104a0516104c0516104e051610500516105205161277462000fc06000396000818161016201528181611451015281816114cc01528181611647015281816116d801528181611742015261181401526000818161013d015281816115eb01528181611790015261184e01526000818161018a0152818161161c015281816116ad0152818161170901528181611e4f01528181611e880152611ec10152600081816104fb0152610c810152600081816104da0152610c600152600081816104090152610b8a0152600081816103e80152610b6901526000818161031f0152610aa70152600081816102fe0152610a86015260008181610458015281816104b90152610c3f015260008181610366015281816103c70152610b48015260008181610298015281816102dd0152610a650152600081816104370152818161049801528181610bce0152610c1e015260008181610345015281816103a601528181610ad70152610b27015260008181610277015281816102bc0152610a440152600061054e01526000610e4d01526000610e7c0152600081816105f50152610e23015260008181610585015281816105d20152610d74015260006118f8015260006118ac01526000818161119201526111d5015260006113aa01526000611311015260006112780152600081816107c1015261105f015260008181610773015261103901526000818161072501526110130152600081816106d70152610fed0152600081816106890152610fc70152600081816108a901526113cb0152600081816108690152611332015260008181610829015261129901526000818161090a01528181610f4a01526114150152600081816108e901528181610eb301526110a201526000818161023901526109c80152600061099101526127746000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80638e7bfbc01161005b5780638e7bfbc0146101f3578063a6ac913414610209578063e6aa216c14610223578063f3190c89146101f357600080fd5b80631b3904f21461008d5780637557ca41146100c45780637cebd7c51461013757806388c35209146101b9575b600080fd5b61009561022b565b6040805192835273ffffffffffffffffffffffffffffffffffffffff9091166020830152015b60405180910390f35b6100cc61025e565b604080519a8b5273ffffffffffffffffffffffffffffffffffffffff998a1660208c0152971515978a01979097526060890195909552928616608088015290151560a087015260c086015290921660e0840152901515610100830152610120820152610140016100bb565b604080517f0000000000000000000000000000000000000000000000000000000000000000815260ff7f0000000000000000000000000000000000000000000000000000000000000000811660208301527f000000000000000000000000000000000000000000000000000000000000000016918101919091526060016100bb565b6101c1610547565b6040805193845273ffffffffffffffffffffffffffffffffffffffff90921660208401521515908201526060016100bb565b6101fb61061a565b6040519081526020016100bb565b610211610651565b6040516100bb96959493929190612169565b6101fb61094e565b600080610236610958565b927f000000000000000000000000000000000000000000000000000000000000000092509050565b600080600080600080600080600080610275610a3d565b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006103437f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610caf565b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff8216156104325761042d7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610caf565b610435565b60005b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff8216156105245761051f7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610caf565b610527565b60005b995099509950995099509950995099509950995090919293949596979899565b60008060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16146105cc576105c7610d70565b6105cf565b60005b937f000000000000000000000000000000000000000000000000000000000000000093507f000000000000000000000000000000000000000000000000000000000000000092509050565b6000610628601b600a612349565b610630610958565b610638610ea4565b6106429190612355565b61064c919061239b565b905090565b60008060608082806005604051908082528060200260200182016040528015610684578160200160208202803683370190505b5093507f0000000000000000000000000000000000000000000000000000000000000000846000815181106106bb576106bb6123de565b602002602001019063ffffffff16908163ffffffff16815250507f000000000000000000000000000000000000000000000000000000000000000084600181518110610709576107096123de565b602002602001019063ffffffff16908163ffffffff16815250507f000000000000000000000000000000000000000000000000000000000000000084600281518110610757576107576123de565b602002602001019063ffffffff16908163ffffffff16815250507f0000000000000000000000000000000000000000000000000000000000000000846003815181106107a5576107a56123de565b602002602001019063ffffffff16908163ffffffff16815250507f0000000000000000000000000000000000000000000000000000000000000000846004815181106107f3576107f36123de565b63ffffffff92909216602092830291909101820152604080516003808252608082019092529182016060803683370190505092507f00000000000000000000000000000000000000000000000000000000000000008360008151811061085b5761085b6123de565b6020026020010181815250507f00000000000000000000000000000000000000000000000000000000000000008360018151811061089b5761089b6123de565b6020026020010181815250507f0000000000000000000000000000000000000000000000000000000000000000836002815181106108db576108db6123de565b6020026020010181815250507f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008585610933610eae565b61093b610f8c565b949b939a50919850965094509092509050565b600061064c61061a565b6040517f94626044000000000000000000000000000000000000000000000000000000008152670de0b6b3a764000060048201526000907f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690639462604490602401602060405180830381865afa158015610a0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a33919061240d565b61064c9190612355565b6000610acb7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610caf565b9050801580610b0e57507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16155b15610b165790565b610b22601b600a612349565b610bae7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610caf565b610bb89083612355565b610bc2919061239b565b9050801580610c0557507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16155b15610c0d5790565b610c19601b600a612349565b610ca57f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610caf565b6106429083612355565b60008473ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa925050508015610d36575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610d3391810190612445565b60015b610d4257506000610d68565b8815610d5e57610d52848861239b565b95505050505050610d68565b610d528885612355565b949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663e6aa216c6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610e17575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610e149181019061240d565b60015b610e215750600090565b7f000000000000000000000000000000000000000000000000000000000000000015610e7757610e71817f000000000000000000000000000000000000000000000000000000000000000061239b565b91505090565b610e717f000000000000000000000000000000000000000000000000000000000000000082612355565b90565b600061064c61144d565b6000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015610f1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4091906124b7565b50505050505090507f0000000000000000000000000000000000000000000000000000000000000000610f7b57610f7681611885565b610e71565b610e71610f8782611885565b6118f0565b6040517f883bdbfd000000000000000000000000000000000000000000000000000000006020808301919091526024820152600560448201527f000000000000000000000000000000000000000000000000000000000000000060648201527f000000000000000000000000000000000000000000000000000000000000000060848201527f000000000000000000000000000000000000000000000000000000000000000060a48201527f000000000000000000000000000000000000000000000000000000000000000060c48201527f000000000000000000000000000000000000000000000000000000000000000060e482015260009081906101040160405160208183030381529060405290506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16836040516110e5919061256a565b600060405180830381855afa9150503d8060008114611120576040519150601f19603f3d011682016040523d82523d6000602084013e611125565b606091505b509150915081611139576000935050505090565b60008180602001905181019061114f91906125ab565b905060008082600381518110611167576111676123de565b602002602001015183600360010181518110611185576111856123de565b60200260200101510390507f00000000000000000000000000000000000000000000000000000000000000008160060b816111c2576111c261236c565b05915060008160060b12801561120957507f00000000000000000000000000000000000000000000000000000000000000008160060b816112055761120561236c565b0715155b15611234577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101905b506112bd8160020b8360008151811061124f5761124f6123de565b60200260200101518460018151811061126a5761126a6123de565b60200260200101510360060b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061191c565b156112ce5760009550505050505090565b6113568160020b836001815181106112e8576112e86123de565b602002602001015184600281518110611303576113036123de565b60200260200101510360060b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061191c565b156113675760009550505050505090565b6113ef8160020b83600281518110611381576113816123de565b60200260200101518460038151811061139c5761139c6123de565b60200260200101510360060b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061191c565b156114005760009550505050505090565b61141161140c826119a3565b611885565b95507f00000000000000000000000000000000000000000000000000000000000000001561144557611442866118f0565b95505b505050505090565b60007f000000000000000000000000000000000000000000000000000000000000000060ff166001036114c757611482610f8c565b905080600003610ea1576040517fc82fc46500000000000000000000000000000000000000000000000000000000815261ea6360048201526024015b60405180910390fd5b6000807f000000000000000000000000000000000000000000000000000000000000000060ff16600203611574576114fd610f8c565b9250826000036115545761150f611e38565b92508260000361154f576040517fc82fc46500000000000000000000000000000000000000000000000000000000815261ea6360048201526024016114be565b505090565b61155c611e83565b9092509050600082900361156f57505090565b6115e4565b61157c611e83565b9093509050611589610f8c565b9150826000036115d757816000036115d1576040517fc82fc46500000000000000000000000000000000000000000000000000000000815261ea6360048201526024016114be565b50919050565b816000036115e457505090565b61160f83837f0000000000000000000000000000000000000000000000000000000000000000611f28565b1561154f5780156116ab577f000000000000000000000000000000000000000000000000000000000000000060ff16600303611675577f000000000000000000000000000000000000000000000000000000000000000060ff166002146115d157505090565b6040517fc82fc46500000000000000000000000000000000000000000000000000000000815261ea6160048201526024016114be565b7f000000000000000000000000000000000000000000000000000000000000000060ff16600103611707577f000000000000000000000000000000000000000000000000000000000000000060ff1660020361154f5750919050565b7f000000000000000000000000000000000000000000000000000000000000000060ff166002036117c857600061173c610d70565b905060007f000000000000000000000000000000000000000000000000000000000000000060ff166002036117745750918290611779565b509283905b8160000361178957949350505050565b6117b485857f0000000000000000000000000000000000000000000000000000000000000000611f28565b156117c157949350505050565b5050505090565b60006117d2610a3d565b905080600003611812576040517fc82fc46500000000000000000000000000000000000000000000000000000000815261ea6160048201526024016114be565b7f000000000000000000000000000000000000000000000000000000000000000060ff16600303611844579392505050565b80925061187284847f0000000000000000000000000000000000000000000000000000000000000000611f28565b1561187f57509092915050565b50505090565b60006118ea6118aa73ffffffffffffffffffffffffffffffffffffffff841680612355565b7f00000000000000000000000000000000000000000000000000000000000000007801000000000000000000000000000000000000000000000000611f6e565b92915050565b60006118ea827f000000000000000000000000000000000000000000000000000000000000000061239b565b60008083858161192e5761192e61236c565b05905060008512801561194f575083858161194b5761194b61236c565b0715155b15611977577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b828101861380611988575082810386125b15611997576001915050610d68565b50600095945050505050565b60008060008360020b126119ba578260020b6119c7565b8260020b6119c79061268e565b90506119f27ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff276186126c6565b60020b811115611a5e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f540000000000000000000000000000000000000000000000000000000000000060448201526064016114be565b600081600116600003611a8257700100000000000000000000000000000000611a94565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff1690506002821615611ad3576080611ace826ffff97272373d413259a46990580e213a612355565b901c90505b6004821615611afd576080611af8826ffff2e50f5f656932ef12357cf3c7fdcc612355565b901c90505b6008821615611b27576080611b22826fffe5caca7e10e4e61c3624eaa0941cd0612355565b901c90505b6010821615611b51576080611b4c826fffcb9843d60f6159c9db58835c926644612355565b901c90505b6020821615611b7b576080611b76826fff973b41fa98c081472e6896dfb254c0612355565b901c90505b6040821615611ba5576080611ba0826fff2ea16466c96a3843ec78b326b52861612355565b901c90505b6080821615611bcf576080611bca826ffe5dee046a99a2a811c461f1969c3053612355565b901c90505b610100821615611bfa576080611bf5826ffcbe86c7900a88aedcffc83b479aa3a4612355565b901c90505b610200821615611c25576080611c20826ff987a7253ac413176f2b074cf7815e54612355565b901c90505b610400821615611c50576080611c4b826ff3392b0822b70005940c7a398e4b70f3612355565b901c90505b610800821615611c7b576080611c76826fe7159475a2c29b7443b29c7fa6e889d9612355565b901c90505b611000821615611ca6576080611ca1826fd097f3bdfd2022b8845ad8f792aa5825612355565b901c90505b612000821615611cd1576080611ccc826fa9f746462d870fdf8a65dc1f90e061e5612355565b901c90505b614000821615611cfc576080611cf7826f70d869a156d2a1b890bb3df62baf32f7612355565b901c90505b618000821615611d27576080611d22826f31be135f97d08fd981231505542fcfa6612355565b901c90505b62010000821615611d53576080611d4e826f09aa508b5b7a84e1c677de54f3e99bc9612355565b901c90505b62020000821615611d7e576080611d79826e5d6af8dedb81196699c329225ee604612355565b901c90505b62040000821615611da8576080611da3826d2216e584f5fa1ea926041bedfe98612355565b901c90505b62080000821615611dd0576080611dcb826b048a170391f7dc42444e8fa2612355565b901c90505b60008460020b1315611e0957611e06817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61239b565b90505b611e1864010000000082612704565b15611e24576001611e27565b60005b610d689060ff16602083901c612718565b6000611e42610a3d565b905080158015611e7657507f000000000000000000000000000000000000000000000000000000000000000060ff16600114155b15610ea15761064c610d70565b6000807f000000000000000000000000000000000000000000000000000000000000000060ff16600103611ebf57611eb9610a3d565b91509091565b7f000000000000000000000000000000000000000000000000000000000000000060ff16600203611f0b57611ef2610a3d565b915081600003611f0757506001611eb9610d70565b9091565b611f13610d70565b915081600003611f0757506001611eb9610a3d565b600080612710611f388486612355565b611f42919061239b565b9050611f4e8185612718565b851180611f635750611f60818561272b565b85105b9150505b9392505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003611fc55760008411611fba57600080fd5b508290049050611f67565b808411611fd157600080fd5b600084868809808403938111909203919050600085612010817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61272b565b61201b906001612718565b1695869004959384900493600081900304600101905061203b8184612355565b90931792600061204c876003612355565b600218905061205b8188612355565b61206690600261272b565b6120709082612355565b905061207c8188612355565b61208790600261272b565b6120919082612355565b905061209d8188612355565b6120a890600261272b565b6120b29082612355565b90506120be8188612355565b6120c990600261272b565b6120d39082612355565b90506120df8188612355565b6120ea90600261272b565b6120f49082612355565b90506121008188612355565b61210b90600261272b565b6121159082612355565b90506121218186612355565b9998505050505050505050565b600081518084526020808501945080840160005b8381101561215e57815187529582019590820190600101612142565b509495945050505050565b600060c0820173ffffffffffffffffffffffffffffffffffffffff8916835260208815158185015260c0604085015281885180845260e086019150828a01935060005b818110156121ce57845163ffffffff16835293830193918301916001016121ac565b505084810360608601526121e2818961212e565b60808601979097525050505060a00152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600181815b8085111561228257817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115612268576122686121fa565b8085161561227557918102915b93841c939080029061222e565b509250929050565b600082612299575060016118ea565b816122a6575060006118ea565b81600181146122bc57600281146122c6576122e2565b60019150506118ea565b60ff8411156122d7576122d76121fa565b50506001821b6118ea565b5060208310610133831016604e8410600b8410161715612305575081810a6118ea565b61230f8383612229565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115612341576123416121fa565b029392505050565b6000611f67838361228a565b80820281158282048414176118ea576118ea6121fa565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826123aa576123aa61236c565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561241f57600080fd5b5051919050565b805169ffffffffffffffffffff8116811461244057600080fd5b919050565b600080600080600060a0868803121561245d57600080fd5b61246686612426565b945060208601519350604086015192506060860151915061248960808701612426565b90509295509295909350565b805161ffff8116811461244057600080fd5b8051801515811461244057600080fd5b600080600080600080600060e0888a0312156124d257600080fd5b875173ffffffffffffffffffffffffffffffffffffffff811681146124f657600080fd5b8097505060208801518060020b811461250e57600080fd5b955061251c60408901612495565b945061252a60608901612495565b935061253860808901612495565b925060a088015160ff8116811461254e57600080fd5b915061255c60c089016124a7565b905092959891949750929550565b6000825160005b8181101561258b5760208186018101518583015201612571565b506000920191825250919050565b8051600681900b811461244057600080fd5b600060208083850312156125be57600080fd5b825167ffffffffffffffff808211156125d657600080fd5b818501915085601f8301126125ea57600080fd5b8151818111156125fc576125fc6123af565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110858211171561263f5761263f6123af565b60405291825284820192508381018501918883111561265d57600080fd5b938501935b828510156126825761267385612599565b84529385019392850192612662565b98975050505050505050565b60007f800000000000000000000000000000000000000000000000000000000000000082036126bf576126bf6121fa565b5060000390565b60008160020b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000081036126fb576126fb6121fa565b60000392915050565b6000826127135761271361236c565b500690565b808201808211156118ea576118ea6121fa565b818103818111156118ea576118ea6121fa56fea26469706673582212206e4e824f75361a91b28beede42476d17fb3595647c6d9b91736f87f2a709b75464736f6c63430008150033000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee00000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000986b5e1e1755e3c2440e960477f25201b0a8bbd40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012c
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100885760003560e01c80638e7bfbc01161005b5780638e7bfbc0146101f3578063a6ac913414610209578063e6aa216c14610223578063f3190c89146101f357600080fd5b80631b3904f21461008d5780637557ca41146100c45780637cebd7c51461013757806388c35209146101b9575b600080fd5b61009561022b565b6040805192835273ffffffffffffffffffffffffffffffffffffffff9091166020830152015b60405180910390f35b6100cc61025e565b604080519a8b5273ffffffffffffffffffffffffffffffffffffffff998a1660208c0152971515978a01979097526060890195909552928616608088015290151560a087015260c086015290921660e0840152901515610100830152610120820152610140016100bb565b604080517f000000000000000000000000000000000000000000000000000000000000012c815260ff7f0000000000000000000000000000000000000000000000000000000000000002811660208301527f000000000000000000000000000000000000000000000000000000000000000116918101919091526060016100bb565b6101c1610547565b6040805193845273ffffffffffffffffffffffffffffffffffffffff90921660208401521515908201526060016100bb565b6101fb61061a565b6040519081526020016100bb565b610211610651565b6040516100bb96959493929190612169565b6101fb61094e565b600080610236610958565b927f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee92509050565b600080600080600080600080600080610275610a3d565b7f000000000000000000000000986b5e1e1755e3c2440e960477f25201b0a8bbd47f00000000000000000000000000000000000000000000000000000000000000016103437f000000000000000000000000986b5e1e1755e3c2440e960477f25201b0a8bbd47f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000003635c9adc5dea000007f000000000000000000000000000000000000314dc6448d9338c15b0a00000000610caf565b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff8216156104325761042d7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017f0000000000000000000000000000000000000000000000000000000000000001610caf565b610435565b60005b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff8216156105245761051f7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017f0000000000000000000000000000000000000000000000000000000000000001610caf565b610527565b60005b995099509950995099509950995099509950995090919293949596979899565b60008060007f000000000000000000000000000000000000000000000000000000000000dead73ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000dead73ffffffffffffffffffffffffffffffffffffffff16146105cc576105c7610d70565b6105cf565b60005b937f000000000000000000000000000000000000000000000000000000000000dead93507f000000000000000000000000000000000000000000000000000000000000000092509050565b6000610628601b600a612349565b610630610958565b610638610ea4565b6106429190612355565b61064c919061239b565b905090565b60008060608082806005604051908082528060200260200182016040528015610684578160200160208202803683370190505b5093507f00000000000000000000000000000000000000000000000000000000000000f0846000815181106106bb576106bb6123de565b602002602001019063ffffffff16908163ffffffff16815250507f000000000000000000000000000000000000000000000000000000000000003c84600181518110610709576107096123de565b602002602001019063ffffffff16908163ffffffff16815250507f000000000000000000000000000000000000000000000000000000000000000f84600281518110610757576107576123de565b602002602001019063ffffffff16908163ffffffff16815250507f0000000000000000000000000000000000000000000000000000000000000001846003815181106107a5576107a56123de565b602002602001019063ffffffff16908163ffffffff16815250507f0000000000000000000000000000000000000000000000000000000000000000846004815181106107f3576107f36123de565b63ffffffff92909216602092830291909101820152604080516003808252608082019092529182016060803683370190505092507f00000000000000000000000000000000000000000000000000000000000000c88360008151811061085b5761085b6123de565b6020026020010181815250507f00000000000000000000000000000000000000000000000000000000000000328360018151811061089b5761089b6123de565b6020026020010181815250507f0000000000000000000000000000000000000000000000000000000000000014836002815181106108db576108db6123de565b6020026020010181815250507f00000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f56407f00000000000000000000000000000000000000000000000000000000000000018585610933610eae565b61093b610f8c565b949b939a50919850965094509092509050565b600061064c61061a565b6040517f94626044000000000000000000000000000000000000000000000000000000008152670de0b6b3a764000060048201526000907f000000000000000000000000000000000000000000000000000000003b9aca009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee1690639462604490602401602060405180830381865afa158015610a0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a33919061240d565b61064c9190612355565b6000610acb7f000000000000000000000000986b5e1e1755e3c2440e960477f25201b0a8bbd47f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000003635c9adc5dea000007f000000000000000000000000000000000000314dc6448d9338c15b0a00000000610caf565b9050801580610b0e57507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16155b15610b165790565b610b22601b600a612349565b610bae7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017f0000000000000000000000000000000000000000000000000000000000000001610caf565b610bb89083612355565b610bc2919061239b565b9050801580610c0557507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16155b15610c0d5790565b610c19601b600a612349565b610ca57f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017f0000000000000000000000000000000000000000000000000000000000000001610caf565b6106429083612355565b60008473ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa925050508015610d36575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610d3391810190612445565b60015b610d4257506000610d68565b8815610d5e57610d52848861239b565b95505050505050610d68565b610d528885612355565b949350505050565b60007f000000000000000000000000000000000000000000000000000000000000dead73ffffffffffffffffffffffffffffffffffffffff1663e6aa216c6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610e17575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610e149181019061240d565b60015b610e215750600090565b7f000000000000000000000000000000000000000000000000000000000000000015610e7757610e71817f000000000000000000000000000000000000000000000000000000000000000161239b565b91505090565b610e717f000000000000000000000000000000000000000000000000000000000000000182612355565b90565b600061064c61144d565b6000807f00000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f564073ffffffffffffffffffffffffffffffffffffffff16633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015610f1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4091906124b7565b50505050505090507f0000000000000000000000000000000000000000000000000000000000000001610f7b57610f7681611885565b610e71565b610e71610f8782611885565b6118f0565b6040517f883bdbfd000000000000000000000000000000000000000000000000000000006020808301919091526024820152600560448201527f00000000000000000000000000000000000000000000000000000000000000f060648201527f000000000000000000000000000000000000000000000000000000000000003c60848201527f000000000000000000000000000000000000000000000000000000000000000f60a48201527f000000000000000000000000000000000000000000000000000000000000000160c48201527f000000000000000000000000000000000000000000000000000000000000000060e482015260009081906101040160405160208183030381529060405290506000807f00000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f564073ffffffffffffffffffffffffffffffffffffffff16836040516110e5919061256a565b600060405180830381855afa9150503d8060008114611120576040519150601f19603f3d011682016040523d82523d6000602084013e611125565b606091505b509150915081611139576000935050505090565b60008180602001905181019061114f91906125ab565b905060008082600381518110611167576111676123de565b602002602001015183600360010181518110611185576111856123de565b60200260200101510390507f00000000000000000000000000000000000000000000000000000000000000018160060b816111c2576111c261236c565b05915060008160060b12801561120957507f00000000000000000000000000000000000000000000000000000000000000018160060b816112055761120561236c565b0715155b15611234577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101905b506112bd8160020b8360008151811061124f5761124f6123de565b60200260200101518460018151811061126a5761126a6123de565b60200260200101510360060b7f00000000000000000000000000000000000000000000000000000000000000b47f00000000000000000000000000000000000000000000000000000000000000c861191c565b156112ce5760009550505050505090565b6113568160020b836001815181106112e8576112e86123de565b602002602001015184600281518110611303576113036123de565b60200260200101510360060b7f000000000000000000000000000000000000000000000000000000000000002d7f000000000000000000000000000000000000000000000000000000000000003261191c565b156113675760009550505050505090565b6113ef8160020b83600281518110611381576113816123de565b60200260200101518460038151811061139c5761139c6123de565b60200260200101510360060b7f000000000000000000000000000000000000000000000000000000000000000e7f000000000000000000000000000000000000000000000000000000000000001461191c565b156114005760009550505050505090565b61141161140c826119a3565b611885565b95507f00000000000000000000000000000000000000000000000000000000000000011561144557611442866118f0565b95505b505050505090565b60007f000000000000000000000000000000000000000000000000000000000000000260ff166001036114c757611482610f8c565b905080600003610ea1576040517fc82fc46500000000000000000000000000000000000000000000000000000000815261ea6360048201526024015b60405180910390fd5b6000807f000000000000000000000000000000000000000000000000000000000000000260ff16600203611574576114fd610f8c565b9250826000036115545761150f611e38565b92508260000361154f576040517fc82fc46500000000000000000000000000000000000000000000000000000000815261ea6360048201526024016114be565b505090565b61155c611e83565b9092509050600082900361156f57505090565b6115e4565b61157c611e83565b9093509050611589610f8c565b9150826000036115d757816000036115d1576040517fc82fc46500000000000000000000000000000000000000000000000000000000815261ea6360048201526024016114be565b50919050565b816000036115e457505090565b61160f83837f000000000000000000000000000000000000000000000000000000000000012c611f28565b1561154f5780156116ab577f000000000000000000000000000000000000000000000000000000000000000160ff16600303611675577f000000000000000000000000000000000000000000000000000000000000000260ff166002146115d157505090565b6040517fc82fc46500000000000000000000000000000000000000000000000000000000815261ea6160048201526024016114be565b7f000000000000000000000000000000000000000000000000000000000000000160ff16600103611707577f000000000000000000000000000000000000000000000000000000000000000260ff1660020361154f5750919050565b7f000000000000000000000000000000000000000000000000000000000000000160ff166002036117c857600061173c610d70565b905060007f000000000000000000000000000000000000000000000000000000000000000260ff166002036117745750918290611779565b509283905b8160000361178957949350505050565b6117b485857f000000000000000000000000000000000000000000000000000000000000012c611f28565b156117c157949350505050565b5050505090565b60006117d2610a3d565b905080600003611812576040517fc82fc46500000000000000000000000000000000000000000000000000000000815261ea6160048201526024016114be565b7f000000000000000000000000000000000000000000000000000000000000000260ff16600303611844579392505050565b80925061187284847f000000000000000000000000000000000000000000000000000000000000012c611f28565b1561187f57509092915050565b50505090565b60006118ea6118aa73ffffffffffffffffffffffffffffffffffffffff841680612355565b7f0000000000000000000000000000000000000000033b2e3c9fd0803ce80000007801000000000000000000000000000000000000000000000000611f6e565b92915050565b60006118ea827f0000000000000000000a70c3c40a64e6c51999090b65f67d924000000000000061239b565b60008083858161192e5761192e61236c565b05905060008512801561194f575083858161194b5761194b61236c565b0715155b15611977577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b828101861380611988575082810386125b15611997576001915050610d68565b50600095945050505050565b60008060008360020b126119ba578260020b6119c7565b8260020b6119c79061268e565b90506119f27ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff276186126c6565b60020b811115611a5e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f540000000000000000000000000000000000000000000000000000000000000060448201526064016114be565b600081600116600003611a8257700100000000000000000000000000000000611a94565b6ffffcb933bd6fad37aa2d162d1a5940015b70ffffffffffffffffffffffffffffffffff1690506002821615611ad3576080611ace826ffff97272373d413259a46990580e213a612355565b901c90505b6004821615611afd576080611af8826ffff2e50f5f656932ef12357cf3c7fdcc612355565b901c90505b6008821615611b27576080611b22826fffe5caca7e10e4e61c3624eaa0941cd0612355565b901c90505b6010821615611b51576080611b4c826fffcb9843d60f6159c9db58835c926644612355565b901c90505b6020821615611b7b576080611b76826fff973b41fa98c081472e6896dfb254c0612355565b901c90505b6040821615611ba5576080611ba0826fff2ea16466c96a3843ec78b326b52861612355565b901c90505b6080821615611bcf576080611bca826ffe5dee046a99a2a811c461f1969c3053612355565b901c90505b610100821615611bfa576080611bf5826ffcbe86c7900a88aedcffc83b479aa3a4612355565b901c90505b610200821615611c25576080611c20826ff987a7253ac413176f2b074cf7815e54612355565b901c90505b610400821615611c50576080611c4b826ff3392b0822b70005940c7a398e4b70f3612355565b901c90505b610800821615611c7b576080611c76826fe7159475a2c29b7443b29c7fa6e889d9612355565b901c90505b611000821615611ca6576080611ca1826fd097f3bdfd2022b8845ad8f792aa5825612355565b901c90505b612000821615611cd1576080611ccc826fa9f746462d870fdf8a65dc1f90e061e5612355565b901c90505b614000821615611cfc576080611cf7826f70d869a156d2a1b890bb3df62baf32f7612355565b901c90505b618000821615611d27576080611d22826f31be135f97d08fd981231505542fcfa6612355565b901c90505b62010000821615611d53576080611d4e826f09aa508b5b7a84e1c677de54f3e99bc9612355565b901c90505b62020000821615611d7e576080611d79826e5d6af8dedb81196699c329225ee604612355565b901c90505b62040000821615611da8576080611da3826d2216e584f5fa1ea926041bedfe98612355565b901c90505b62080000821615611dd0576080611dcb826b048a170391f7dc42444e8fa2612355565b901c90505b60008460020b1315611e0957611e06817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61239b565b90505b611e1864010000000082612704565b15611e24576001611e27565b60005b610d689060ff16602083901c612718565b6000611e42610a3d565b905080158015611e7657507f000000000000000000000000000000000000000000000000000000000000000160ff16600114155b15610ea15761064c610d70565b6000807f000000000000000000000000000000000000000000000000000000000000000160ff16600103611ebf57611eb9610a3d565b91509091565b7f000000000000000000000000000000000000000000000000000000000000000160ff16600203611f0b57611ef2610a3d565b915081600003611f0757506001611eb9610d70565b9091565b611f13610d70565b915081600003611f0757506001611eb9610a3d565b600080612710611f388486612355565b611f42919061239b565b9050611f4e8185612718565b851180611f635750611f60818561272b565b85105b9150505b9392505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003611fc55760008411611fba57600080fd5b508290049050611f67565b808411611fd157600080fd5b600084868809808403938111909203919050600085612010817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61272b565b61201b906001612718565b1695869004959384900493600081900304600101905061203b8184612355565b90931792600061204c876003612355565b600218905061205b8188612355565b61206690600261272b565b6120709082612355565b905061207c8188612355565b61208790600261272b565b6120919082612355565b905061209d8188612355565b6120a890600261272b565b6120b29082612355565b90506120be8188612355565b6120c990600261272b565b6120d39082612355565b90506120df8188612355565b6120ea90600261272b565b6120f49082612355565b90506121008188612355565b61210b90600261272b565b6121159082612355565b90506121218186612355565b9998505050505050505050565b600081518084526020808501945080840160005b8381101561215e57815187529582019590820190600101612142565b509495945050505050565b600060c0820173ffffffffffffffffffffffffffffffffffffffff8916835260208815158185015260c0604085015281885180845260e086019150828a01935060005b818110156121ce57845163ffffffff16835293830193918301916001016121ac565b505084810360608601526121e2818961212e565b60808601979097525050505060a00152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600181815b8085111561228257817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115612268576122686121fa565b8085161561227557918102915b93841c939080029061222e565b509250929050565b600082612299575060016118ea565b816122a6575060006118ea565b81600181146122bc57600281146122c6576122e2565b60019150506118ea565b60ff8411156122d7576122d76121fa565b50506001821b6118ea565b5060208310610133831016604e8410600b8410161715612305575081810a6118ea565b61230f8383612229565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115612341576123416121fa565b029392505050565b6000611f67838361228a565b80820281158282048414176118ea576118ea6121fa565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826123aa576123aa61236c565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561241f57600080fd5b5051919050565b805169ffffffffffffffffffff8116811461244057600080fd5b919050565b600080600080600060a0868803121561245d57600080fd5b61246686612426565b945060208601519350604086015192506060860151915061248960808701612426565b90509295509295909350565b805161ffff8116811461244057600080fd5b8051801515811461244057600080fd5b600080600080600080600060e0888a0312156124d257600080fd5b875173ffffffffffffffffffffffffffffffffffffffff811681146124f657600080fd5b8097505060208801518060020b811461250e57600080fd5b955061251c60408901612495565b945061252a60608901612495565b935061253860808901612495565b925060a088015160ff8116811461254e57600080fd5b915061255c60c089016124a7565b905092959891949750929550565b6000825160005b8181101561258b5760208186018101518583015201612571565b506000920191825250919050565b8051600681900b811461244057600080fd5b600060208083850312156125be57600080fd5b825167ffffffffffffffff808211156125d657600080fd5b818501915085601f8301126125ea57600080fd5b8151818111156125fc576125fc6123af565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110858211171561263f5761263f6123af565b60405291825284820192508381018501918883111561265d57600080fd5b938501935b828510156126825761267385612599565b84529385019392850192612662565b98975050505050505050565b60007f800000000000000000000000000000000000000000000000000000000000000082036126bf576126bf6121fa565b5060000390565b60008160020b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000081036126fb576126fb6121fa565b60000392915050565b6000826127135761271361236c565b500690565b808201808211156118ea576118ea6121fa565b818103818111156118ea576118ea6121fa56fea26469706673582212206e4e824f75361a91b28beede42476d17fb3595647c6d9b91736f87f2a709b75464736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee00000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000986b5e1e1755e3c2440e960477f25201b0a8bbd40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012c
-----Decoded View---------------
Arg [0] : weETH_ (address): 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee
Arg [1] : uniV3CheckCLRSParams_ (tuple):
Arg [1] : uniV3Params (tuple):
Arg [1] : pool (address): 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640
Arg [2] : invertRate (bool): True
Arg [3] : tWAPMaxDeltaPercents (uint256[3]): 200,50,20
Arg [4] : secondsAgos (uint32[5]): 240,60,15,1,0
Arg [2] : chainlinkParams (tuple):
Arg [1] : hops (uint8): 1
Arg [2] : feed1 (tuple):
Arg [1] : feed (address): 0x986b5E1e1755e3C2440e960477f25201B0a8bbD4
Arg [2] : invertRate (bool): True
Arg [3] : token0Decimals (uint256): 6
Arg [3] : feed2 (tuple):
Arg [1] : feed (address): 0x0000000000000000000000000000000000000000
Arg [2] : invertRate (bool): False
Arg [3] : token0Decimals (uint256): 0
Arg [4] : feed3 (tuple):
Arg [1] : feed (address): 0x0000000000000000000000000000000000000000
Arg [2] : invertRate (bool): False
Arg [3] : token0Decimals (uint256): 0
Arg [3] : redstoneOracle (tuple):
Arg [1] : oracle (address): 0x0000000000000000000000000000000000000000
Arg [2] : invertRate (bool): False
Arg [3] : token0Decimals (uint256): 0
Arg [4] : rateSource (uint8): 2
Arg [5] : fallbackMainSource (uint8): 1
Arg [6] : rateCheckMaxDeltaPercent (uint256): 300
-----Encoded View---------------
27 Constructor Arguments found :
Arg [0] : 000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee
Arg [1] : 00000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000c8
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000032
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000014
Arg [6] : 00000000000000000000000000000000000000000000000000000000000000f0
Arg [7] : 000000000000000000000000000000000000000000000000000000000000003c
Arg [8] : 000000000000000000000000000000000000000000000000000000000000000f
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [12] : 000000000000000000000000986b5e1e1755e3c2440e960477f25201b0a8bbd4
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [18] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [19] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [20] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [21] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [22] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [23] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [25] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [26] : 000000000000000000000000000000000000000000000000000000000000012c
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.