Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- PriceFeed
- Optimization enabled
- false
- Compiler version
- v0.8.27+commit.40a35a09
- EVM Version
- Verified at
- 2026-10-03T03:46:15.777327Z
Constructor Arguments
0000000000000000000000000000000000000000000000000000000000000012
Arg [0] (uint256) : 18
contracts/PriceFeed.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import "./Interfaces/IPriceFeed.sol";
import "./Interfaces/ITellorCaller.sol";
import "./Dependencies/AggregatorV3Interface.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";
import "./Dependencies/BaseMath.sol";
import "./Dependencies/LiquityMath.sol";
/*
* PriceFeed for mainnet deployment, to be connected to Chainlink's live collateral:USD aggregator reference
* contract, and a wrapper contract TellorCaller, which connects to TellorMaster contract.
*
* The PriceFeed uses Chainlink as primary oracle, and Tellor as fallback. It contains logic for
* switching oracles based on oracle failures, and conditions for returning to the primary
* Chainlink oracle.
*/
contract PriceFeed is Ownable, CheckContract, BaseMath, IPriceFeed {
string constant public NAME = "PriceFeed";
AggregatorV3Interface public priceAggregator; // Mainnet Chainlink aggregator
ITellorCaller public tellorCaller; // Wrapper contract that calls the Tellor system
uint256 public immutable tellorDigits;
// Use to convert a price answer to an 18-digit precision uint
uint256 constant public TARGET_DIGITS = 18;
// Maximum deviation allowed between two consecutive Chainlink oracle prices. 18-digit precision.
uint256 constant public MAX_PRICE_DEVIATION_FROM_PREVIOUS_ROUND = 5e17; // 50%
/*
* The maximum relative price difference between two oracle responses allowed in order for the PriceFeed
* to return to using the Chainlink oracle. 18-digit precision.
*/
uint256 constant public MAX_PRICE_DIFFERENCE_BETWEEN_ORACLES = 5e16; // 5%
// Maximum allowed staleness before treating an oracle price as frozen
uint256 constant public MAX_TIME_OUT = 4 hours;
// The last good price seen from an oracle by Liquity
uint256 public lastGoodPrice;
struct ChainlinkResponse {
uint80 roundId;
int256 answer;
uint256 timestamp;
bool success;
uint8 decimals;
}
struct TellorResponse {
bool ifRetrieve;
uint256 value;
uint256 timestamp;
bool success;
}
enum Status {
chainlinkWorking,
usingTellorChainlinkUntrusted,
bothOraclesUntrusted,
usingTellorChainlinkFrozen,
usingChainlinkTellorUntrusted
}
// The current status of the PricFeed, which determines the conditions for the next price fetch attempt
Status public status;
event PriceFeedStatusChanged(Status newStatus);
constructor (uint256 _tellorDigits) {
require(_tellorDigits > 0 && _tellorDigits <= TARGET_DIGITS, "PriceFeed: wrong decimals for Tellor");
tellorDigits = _tellorDigits;
}
// --- Dependency setters ---
function setAddresses(
address _priceAggregatorAddress,
address _tellorCallerAddress
)
external
onlyOwner
{
require(address(tellorCaller) == address(0), "PriceFeed: contacts already set");
checkContract(_tellorCallerAddress);
priceAggregator = AggregatorV3Interface(_priceAggregatorAddress);
tellorCaller = ITellorCaller(_tellorCallerAddress);
if (_priceAggregatorAddress != address(0)) {
ChainlinkResponse memory cl = _getCurrentChainlinkResponse();
if (!_badChainlinkResponse(cl)) {
uint256 seedPrice = _scaleChainlinkPriceByDigits(uint256(cl.answer), cl.decimals);
_storePrice(seedPrice);
status = Status.chainlinkWorking;
return;
}
}
TellorResponse memory tellor = _getCurrentTellorResponse();
require(
!_tellorIsBroken(tellor) && !_tellorIsFrozen(tellor),
"PriceFeed: no fresh initial price from either oracle"
);
_storeTellorPrice(tellor);
status = Status.usingTellorChainlinkUntrusted;
}
// --- Functions ---
/*
* fetchPrice():
* Returns the latest price obtained from the Oracle. Called by Liquity functions that require a current price.
*
* Also callable by anyone externally.
*
* Non-view function - it stores the last good price seen by Liquity.
*
* Uses a main oracle (Chainlink) and a fallback oracle (Tellor) in case Chainlink fails. If both fail,
* it uses the last good price seen by Liquity.
*
*/
function fetchPrice() external override returns (uint) {
// PulseChain deployment: if no Chainlink aggregator is configured, use Tellor-only path
TellorResponse memory tellorResponse = _getCurrentTellorResponse();
if (address(priceAggregator) == address(0)) {
require(!_tellorIsBroken(tellorResponse), "PriceFeed: Tellor unavailable");
require(!_tellorIsFrozen(tellorResponse), "PriceFeed: Tellor price is stale");
uint256 price = _storeTellorPrice(tellorResponse);
_changeStatus(Status.usingTellorChainlinkUntrusted);
return price;
}
// Get current and previous price data from Chainlink, and current price data from Tellor
ChainlinkResponse memory chainlinkResponse = _getCurrentChainlinkResponse();
ChainlinkResponse memory prevChainlinkResponse = _getPrevChainlinkResponse(chainlinkResponse.roundId, chainlinkResponse.decimals);
// --- CASE 1: System fetched last price from Chainlink ---
if (status == Status.chainlinkWorking) {
// If Chainlink is broken, try Tellor
if (_chainlinkIsBroken(chainlinkResponse, prevChainlinkResponse)) {
// If Tellor is broken then both oracles are untrusted, so return the last good price
if (_tellorIsBroken(tellorResponse)) {
_changeStatus(Status.bothOraclesUntrusted);
return lastGoodPrice;
}
/*
* If Tellor is only frozen but otherwise returning valid data, return the last good price.
* Tellor may need to be tipped to return current data.
*/
if (_tellorIsFrozen(tellorResponse)) {
_changeStatus(Status.usingTellorChainlinkUntrusted);
return lastGoodPrice;
}
// If Chainlink is broken and Tellor is working, switch to Tellor and return current Tellor price
_changeStatus(Status.usingTellorChainlinkUntrusted);
return _storeTellorPrice(tellorResponse);
}
// If Chainlink is frozen, try Tellor
if (_chainlinkIsFrozen(chainlinkResponse)) {
// If Tellor is broken too, remember Tellor broke, and return last good price
if (_tellorIsBroken(tellorResponse)) {
_changeStatus(Status.usingChainlinkTellorUntrusted);
return lastGoodPrice;
}
// If Tellor is frozen or working, remember Chainlink froze, and switch to Tellor
_changeStatus(Status.usingTellorChainlinkFrozen);
if (_tellorIsFrozen(tellorResponse)) {return lastGoodPrice;}
// If Tellor is working, use it
return _storeTellorPrice(tellorResponse);
}
// If Chainlink price has changed by > 50% between two consecutive rounds, compare it to Tellor's price
if (_chainlinkPriceChangeAboveMax(chainlinkResponse, prevChainlinkResponse)) {
// If Tellor is broken, both oracles are untrusted, and return last good price
if (_tellorIsBroken(tellorResponse)) {
_changeStatus(Status.bothOraclesUntrusted);
return lastGoodPrice;
}
// If Tellor is frozen, switch to Tellor and return last good price
if (_tellorIsFrozen(tellorResponse)) {
_changeStatus(Status.usingTellorChainlinkUntrusted);
return lastGoodPrice;
}
/*
* If Tellor is live and both oracles have a similar price, conclude that Chainlink's large price deviation between
* two consecutive rounds was likely a legitmate market price movement, and so continue using Chainlink
*/
if (_bothOraclesSimilarPrice(chainlinkResponse, tellorResponse)) {
return _storeChainlinkPrice(chainlinkResponse);
}
// If Tellor is live but the oracles differ too much in price, conclude that Chainlink's initial price deviation was
// an oracle failure. Switch to Tellor, and use Tellor price
_changeStatus(Status.usingTellorChainlinkUntrusted);
return _storeTellorPrice(tellorResponse);
}
// If Chainlink is working and Tellor is broken, remember Tellor is broken
if (_tellorIsBroken(tellorResponse)) {
_changeStatus(Status.usingChainlinkTellorUntrusted);
}
// If Chainlink is working, return Chainlink current price (no status change)
return _storeChainlinkPrice(chainlinkResponse);
}
// --- CASE 2: The system fetched last price from Tellor ---
if (status == Status.usingTellorChainlinkUntrusted) {
// If both Tellor and Chainlink are live, unbroken, and reporting similar prices, switch back to Chainlink
if (_bothOraclesLiveAndUnbrokenAndSimilarPrice(chainlinkResponse, prevChainlinkResponse, tellorResponse)) {
_changeStatus(Status.chainlinkWorking);
return _storeChainlinkPrice(chainlinkResponse);
}
if (_tellorIsBroken(tellorResponse)) {
_changeStatus(Status.bothOraclesUntrusted);
return lastGoodPrice;
}
/*
* If Tellor is only frozen but otherwise returning valid data, just return the last good price.
* Tellor may need to be tipped to return current data.
*/
if (_tellorIsFrozen(tellorResponse)) {return lastGoodPrice;}
// Otherwise, use Tellor price
return _storeTellorPrice(tellorResponse);
}
// --- CASE 3: Both oracles were untrusted at the last price fetch ---
if (status == Status.bothOraclesUntrusted) {
/*
* If both oracles are now live, unbroken and similar price, we assume that they are reporting
* accurately, and so we switch back to Chainlink.
*/
if (_bothOraclesLiveAndUnbrokenAndSimilarPrice(chainlinkResponse, prevChainlinkResponse, tellorResponse)) {
_changeStatus(Status.chainlinkWorking);
return _storeChainlinkPrice(chainlinkResponse);
}
// Otherwise, return the last good price - both oracles are still untrusted (no status change)
return lastGoodPrice;
}
// --- CASE 4: Using Tellor, and Chainlink is frozen ---
if (status == Status.usingTellorChainlinkFrozen) {
if (_chainlinkIsBroken(chainlinkResponse, prevChainlinkResponse)) {
// If both Oracles are broken, return last good price
if (_tellorIsBroken(tellorResponse)) {
_changeStatus(Status.bothOraclesUntrusted);
return lastGoodPrice;
}
// If Chainlink is broken, remember it and switch to using Tellor
_changeStatus(Status.usingTellorChainlinkUntrusted);
if (_tellorIsFrozen(tellorResponse)) {return lastGoodPrice;}
// If Tellor is working, return Tellor current price
return _storeTellorPrice(tellorResponse);
}
if (_chainlinkIsFrozen(chainlinkResponse)) {
// if Chainlink is frozen and Tellor is broken, remember Tellor broke, and return last good price
if (_tellorIsBroken(tellorResponse)) {
_changeStatus(Status.usingChainlinkTellorUntrusted);
return lastGoodPrice;
}
// If both are frozen, just use lastGoodPrice
if (_tellorIsFrozen(tellorResponse)) {return lastGoodPrice;}
// if Chainlink is frozen and Tellor is working, keep using Tellor (no status change)
return _storeTellorPrice(tellorResponse);
}
// if Chainlink is live and Tellor is broken, remember Tellor broke, and return Chainlink price
if (_tellorIsBroken(tellorResponse)) {
_changeStatus(Status.usingChainlinkTellorUntrusted);
return _storeChainlinkPrice(chainlinkResponse);
}
// If Chainlink is live and Tellor is frozen, just use last good price (no status change) since we have no basis for comparison
if (_tellorIsFrozen(tellorResponse)) {return lastGoodPrice;}
// If Chainlink is live and Tellor is working, compare prices. Switch to Chainlink
// if prices are within 5%, and return Chainlink price.
if (_bothOraclesSimilarPrice(chainlinkResponse, tellorResponse)) {
_changeStatus(Status.chainlinkWorking);
return _storeChainlinkPrice(chainlinkResponse);
}
// Otherwise if Chainlink is live but price not within 5% of Tellor, distrust Chainlink, and return Tellor price
_changeStatus(Status.usingTellorChainlinkUntrusted);
return _storeTellorPrice(tellorResponse);
}
// --- CASE 5: Using Chainlink, Tellor is untrusted ---
if (status == Status.usingChainlinkTellorUntrusted) {
// If Chainlink breaks, now both oracles are untrusted
if (_chainlinkIsBroken(chainlinkResponse, prevChainlinkResponse)) {
_changeStatus(Status.bothOraclesUntrusted);
return lastGoodPrice;
}
// If Chainlink is frozen, return last good price (no status change)
if (_chainlinkIsFrozen(chainlinkResponse)) {
return lastGoodPrice;
}
// If Chainlink and Tellor are both live, unbroken and similar price, switch back to chainlinkWorking and return Chainlink price
if (_bothOraclesLiveAndUnbrokenAndSimilarPrice(chainlinkResponse, prevChainlinkResponse, tellorResponse)) {
_changeStatus(Status.chainlinkWorking);
return _storeChainlinkPrice(chainlinkResponse);
}
// If Chainlink is live but deviated >50% from it's previous price and Tellor is still untrusted, switch
// to bothOraclesUntrusted and return last good price
if (_chainlinkPriceChangeAboveMax(chainlinkResponse, prevChainlinkResponse)) {
_changeStatus(Status.bothOraclesUntrusted);
return lastGoodPrice;
}
// Otherwise if Chainlink is live and deviated <50% from it's previous price and Tellor is still untrusted,
// return Chainlink price (no status change)
return _storeChainlinkPrice(chainlinkResponse);
}
return lastGoodPrice;
}
// --- Helper functions ---
/* Chainlink is considered broken if its current or previous round data is in any way bad. We check the previous round
* for two reasons:
*
* 1) It is necessary data for the price deviation check in case 1,
* and
* 2) Chainlink is the PriceFeed's preferred primary oracle - having two consecutive valid round responses adds
* peace of mind when using or returning to Chainlink.
*/
function _chainlinkIsBroken(ChainlinkResponse memory _currentResponse, ChainlinkResponse memory _prevResponse) internal view returns (bool) {
return _badChainlinkResponse(_currentResponse) || _badChainlinkResponse(_prevResponse);
}
function _badChainlinkResponse(ChainlinkResponse memory _response) internal view returns (bool) {
// Check for response call reverted
if (!_response.success) {return true;}
// Check for an invalid roundId that is 0
if (_response.roundId == 0) {return true;}
// Check for an invalid timeStamp that is 0, or in the future
if (_response.timestamp == 0 || _response.timestamp > block.timestamp) {return true;}
// Check for non-positive price
if (_response.answer <= 0) {return true;}
return false;
}
function _chainlinkIsFrozen(ChainlinkResponse memory _response) internal view returns (bool) {
if (!_response.success) {return false;}
if (_response.timestamp == 0) {return true;}
return block.timestamp - _response.timestamp >= MAX_TIME_OUT;
}
function _chainlinkPriceChangeAboveMax(ChainlinkResponse memory _currentResponse, ChainlinkResponse memory _prevResponse) internal pure returns (bool) {
uint256 currentScaledPrice = _scaleChainlinkPriceByDigits(uint256(_currentResponse.answer), _currentResponse.decimals);
uint256 prevScaledPrice = _scaleChainlinkPriceByDigits(uint256(_prevResponse.answer), _prevResponse.decimals);
uint256 minPrice = LiquityMath._min(currentScaledPrice, prevScaledPrice);
uint256 maxPrice = LiquityMath._max(currentScaledPrice, prevScaledPrice);
/*
* Use the larger price as the denominator:
* - If price decreased, the percentage deviation is in relation to the the previous price.
* - If price increased, the percentage deviation is in relation to the current price.
*/
uint256 percentDeviation = (maxPrice - minPrice) * DECIMAL_PRECISION / maxPrice;
// Return true if price has more than doubled, or more than halved.
return percentDeviation > MAX_PRICE_DEVIATION_FROM_PREVIOUS_ROUND;
}
function _tellorIsBroken(TellorResponse memory _response) internal view returns (bool) {
// Check for response call reverted
if (!_response.success) {return true;}
// Check for an invalid timeStamp that is 0, or in the future
if (_response.timestamp == 0 || _response.timestamp > block.timestamp) {return true;}
// Check for zero price
if (_response.value == 0) {return true;}
return false;
}
function _tellorIsFrozen(TellorResponse memory _response) internal view returns (bool) {
if (!_response.success) {return false;}
if (_response.timestamp == 0) {return true;}
return block.timestamp - _response.timestamp >= MAX_TIME_OUT;
}
function _bothOraclesLiveAndUnbrokenAndSimilarPrice
(
ChainlinkResponse memory _chainlinkResponse,
ChainlinkResponse memory _prevChainlinkResponse,
TellorResponse memory _tellorResponse
)
internal
view
returns (bool)
{
// Return false if either oracle is broken or frozen
if
(
_tellorIsBroken(_tellorResponse) ||
_tellorIsFrozen(_tellorResponse) ||
_chainlinkIsBroken(_chainlinkResponse, _prevChainlinkResponse) ||
_chainlinkIsFrozen(_chainlinkResponse)
)
{
return false;
}
return _bothOraclesSimilarPrice(_chainlinkResponse, _tellorResponse);
}
function _bothOraclesSimilarPrice( ChainlinkResponse memory _chainlinkResponse, TellorResponse memory _tellorResponse) internal view returns (bool) {
uint256 scaledChainlinkPrice = _scaleChainlinkPriceByDigits(uint256(_chainlinkResponse.answer), _chainlinkResponse.decimals);
uint256 scaledTellorPrice = _scaleTellorPriceByDigits(_tellorResponse.value);
// Get the relative price difference between the oracles. Use the lower price as the denominator, i.e. the reference for the calculation.
uint256 minPrice = LiquityMath._min(scaledTellorPrice, scaledChainlinkPrice);
uint256 maxPrice = LiquityMath._max(scaledTellorPrice, scaledChainlinkPrice);
uint256 percentPriceDifference = (maxPrice - minPrice) * DECIMAL_PRECISION / minPrice;
/*
* Return true if the relative price difference is <= 3%: if so, we assume both oracles are probably reporting
* the honest market price, as it is unlikely that both have been broken/hacked and are still in-sync.
*/
return percentPriceDifference <= MAX_PRICE_DIFFERENCE_BETWEEN_ORACLES;
}
function _scaleChainlinkPriceByDigits(uint256 _price, uint256 _answerDigits) internal pure returns (uint) {
/*
* Convert the price returned by the Chainlink oracle to an 18-digit decimal for use by Liquity.
* At date of Liquity launch, Chainlink uses an 8-digit price, but we also handle the possibility of
* future changes.
*
*/
uint256 price;
if (_answerDigits >= TARGET_DIGITS) {
// Scale the returned price value down to Liquity's target precision
price = _price / (10 ** (_answerDigits - TARGET_DIGITS));
}
else if (_answerDigits < TARGET_DIGITS) {
// Scale the returned price value up to Liquity's target precision
price = _price * (10 ** (TARGET_DIGITS - _answerDigits));
}
return price;
}
function _scaleTellorPriceByDigits(uint256 _price) internal view returns (uint) {
return _price * (10**(TARGET_DIGITS - tellorDigits));
}
function _changeStatus(Status _status) internal {
status = _status;
emit PriceFeedStatusChanged(_status);
}
function _storePrice(uint256 _currentPrice) internal {
lastGoodPrice = _currentPrice;
emit LastGoodPriceUpdated(_currentPrice);
}
function _storeTellorPrice(TellorResponse memory _tellorResponse) internal returns (uint) {
uint256 scaledTellorPrice = _scaleTellorPriceByDigits(_tellorResponse.value);
_storePrice(scaledTellorPrice);
return scaledTellorPrice;
}
function _storeChainlinkPrice(ChainlinkResponse memory _chainlinkResponse) internal returns (uint) {
uint256 scaledChainlinkPrice = _scaleChainlinkPriceByDigits(uint256(_chainlinkResponse.answer), _chainlinkResponse.decimals);
_storePrice(scaledChainlinkPrice);
return scaledChainlinkPrice;
}
// --- Oracle response wrapper functions ---
function _getCurrentTellorResponse() internal returns (TellorResponse memory tellorResponse) {
try tellorCaller.getTellorCurrentValue() returns
(
bool ifRetrieve,
uint256 value,
uint256 _timestampRetrieved
)
{
// If call to Tellor succeeds, return the response and success = true
tellorResponse.ifRetrieve = ifRetrieve;
tellorResponse.value = value;
tellorResponse.timestamp = _timestampRetrieved;
tellorResponse.success = true;
return (tellorResponse);
}catch {
// If call to Tellor reverts, return a zero response with success = false
return (tellorResponse);
}
}
function _getCurrentChainlinkResponse() internal view returns (ChainlinkResponse memory chainlinkResponse) {
if (address(priceAggregator) == address(0)) {
return chainlinkResponse;
}
// First, try to get current decimal precision:
try priceAggregator.decimals() returns (uint8 decimals) {
// If call to Chainlink succeeds, record the current decimal precision
chainlinkResponse.decimals = decimals;
} catch {
// If call to Chainlink aggregator reverts, return a zero response with success = false
return chainlinkResponse;
}
// Secondly, try to get latest price data:
try priceAggregator.latestRoundData() returns
(
uint80 roundId,
int256 answer,
uint256 /* startedAt */,
uint256 timestamp,
uint80 /* answeredInRound */
)
{
// If call to Chainlink succeeds, return the response and success = true
chainlinkResponse.roundId = roundId;
chainlinkResponse.answer = answer;
chainlinkResponse.timestamp = timestamp;
chainlinkResponse.success = true;
return chainlinkResponse;
} catch {
// If call to Chainlink aggregator reverts, return a zero response with success = false
return chainlinkResponse;
}
}
function _getPrevChainlinkResponse(uint80 _currentRoundId, uint8 _currentDecimals) internal view returns (ChainlinkResponse memory prevChainlinkResponse) {
/*
* NOTE: Chainlink only offers a current decimals() value - there is no way to obtain the decimal precision used in a
* previous round. We assume the decimals used in the previous round are the same as the current round.
*/
if (address(priceAggregator) == address(0)) {
return prevChainlinkResponse;
}
if (_currentRoundId == 0) {
return prevChainlinkResponse;
}
// Try to get the price data from the previous round:
try priceAggregator.getRoundData(_currentRoundId - 1) returns
(
uint80 roundId,
int256 answer,
uint256 /* startedAt */,
uint256 timestamp,
uint80 /* answeredInRound */
)
{
// If call to Chainlink succeeds, return the response and success = true
prevChainlinkResponse.roundId = roundId;
prevChainlinkResponse.answer = answer;
prevChainlinkResponse.timestamp = timestamp;
prevChainlinkResponse.decimals = _currentDecimals;
prevChainlinkResponse.success = true;
return prevChainlinkResponse;
} catch {
// If call to Chainlink aggregator reverts, return a zero response with success = false
return prevChainlinkResponse;
}
}
/*
* forceExitBothUntrustedStatus()
* DAO can help one oracle return back online only if previously both were untrusted.
* Function reverts if both oracles are still broken.
* In case when both oracles are online but have different prices then caller can control
* which will be checked first: ChainLink if tryTellorFirst==false, Tellor otherwise
*/
function forceExitBothUntrustedStatus(bool tryTellorFirst) external onlyOwner {
require(status == Status.bothOraclesUntrusted, "PriceFeed: at least one oracle is working");
ChainlinkResponse memory chainlinkResponse = _getCurrentChainlinkResponse();
ChainlinkResponse memory prevChainlinkResponse = _getPrevChainlinkResponse(chainlinkResponse.roundId, chainlinkResponse.decimals);
TellorResponse memory tellorResponse = _getCurrentTellorResponse();
// check Tellor first only if flag was true, exit function in case of success
if
(
tryTellorFirst &&
_changeStatusIfTellorLiveAndUnbroken(tellorResponse)
)
{
return;
}
// check ChainLink feed
if
(
!_chainlinkIsBroken(chainlinkResponse, prevChainlinkResponse) &&
!_chainlinkIsFrozen(chainlinkResponse) &&
!_chainlinkPriceChangeAboveMax(chainlinkResponse, prevChainlinkResponse)
)
{
_changeStatus(Status.usingChainlinkTellorUntrusted);
_storeChainlinkPrice(chainlinkResponse);
return;
}
// if Tellor was already checked or check is false then revert transaction
if
(
!tryTellorFirst &&
_changeStatusIfTellorLiveAndUnbroken(tellorResponse)
)
{
return;
}
// Transaction is successful only if one oracle returned back online
revert("PriceFeed: both oracles are still untrusted");
}
function _changeStatusIfTellorLiveAndUnbroken
(
TellorResponse memory _tellorResponse
)
internal
returns (bool)
{
// Return true if Tellor is back online
if
(
!_tellorIsBroken(_tellorResponse) &&
!_tellorIsFrozen(_tellorResponse)
)
{
_changeStatus(Status.usingTellorChainlinkUntrusted);
_storeTellorPrice(_tellorResponse);
return true;
}
return false;
}
}
contracts/Dependencies/AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
// Code from https://github.com/smartcontractkit/chainlink/blob/master/evm-contracts/src/v0.6/interfaces/AggregatorV3Interface.sol
pragma solidity ^0.8.27;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
// getRoundData and latestRoundData should both raise "No data present"
// if they do not have data to report, instead of returning unset values
// which could be misinterpreted as actual reported values.
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
);
}
contracts/Dependencies/BaseMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
contract BaseMath {
uint256 constant public DECIMAL_PRECISION = 1e18;
}
contracts/Dependencies/CheckContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
contract CheckContract {
/**
* Check that the account is an already deployed non-destroyed contract.
* See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L12
*/
function checkContract(address _account) internal view {
require(_account != address(0), "Account cannot be zero address");
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(_account) }
require(size > 0, "Account code size cannot be zero");
}
}
contracts/Dependencies/LiquityMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
library LiquityMath {
uint256 internal constant DECIMAL_PRECISION = 1e18;
/* Precision for Nominal ICR (independent of price). Rationale for the value:
*
* - Making it “too high” could lead to overflows.
* - Making it “too low” could lead to an ICR equal to zero, due to truncation from Solidity floor division.
*
* This value of 1e20 is chosen for safety: the NICR will only overflow for numerator > ~1e39 ETH,
* and will only truncate to 0 if the denominator is at least 1e20 times greater than the numerator.
*
*/
uint256 internal constant NICR_PRECISION = 1e20;
function _min(uint256 _a, uint256 _b) internal pure returns (uint) {
return (_a < _b) ? _a : _b;
}
function _max(uint256 _a, uint256 _b) internal pure returns (uint) {
return (_a >= _b) ? _a : _b;
}
/*
* Multiply two decimal numbers and use normal rounding rules:
* -round product up if 19'th mantissa digit >= 5
* -round product down if 19'th mantissa digit < 5
*
* Used only inside the exponentiation, _decPow().
*/
function decMul(uint256 x, uint256 y) internal pure returns (uint256 decProd) {
uint256 prod_xy = x * y;
decProd = (prod_xy + (DECIMAL_PRECISION / 2)) / DECIMAL_PRECISION;
}
/*
* _decPow: Exponentiation function for 18-digit decimal base, and integer exponent n.
*
* Uses the efficient "exponentiation by squaring" algorithm. O(log(n)) complexity.
*
* Called by one function that represent time in units of minutes:
* 1) VaultManager._calcDecayedBaseRate
*
* The exponent is capped to avoid reverting due to overflow. The cap 525600000 equals
* "minutes in 1000 years": 60 * 24 * 365 * 1000
*
* If a period of > 1000 years is ever used as an exponent in either of the above functions, the result will be
* negligibly different from just passing the cap, since:
*
* In function 1), the decayed base rate will be 0 for 1000 years or > 1000 years
* In function 2), the difference in tokens issued at 1000 years and any time > 1000 years, will be negligible
*/
function _decPow(uint256 _base, uint256 _minutes) internal pure returns (uint) {
if (_minutes > 525600000) {_minutes = 525600000;} // cap to avoid overflow
if (_minutes == 0) {return DECIMAL_PRECISION;}
uint256 y = DECIMAL_PRECISION;
uint256 x = _base;
uint256 n = _minutes;
// Exponentiation-by-squaring
while (n > 1) {
if (n % 2 == 0) {
x = decMul(x, x);
n = n / 2;
} else { // if (n % 2 != 0)
y = decMul(x, y);
x = decMul(x, x);
n = (n - 1) / 2;
}
}
return decMul(x, y);
}
function _getAbsoluteDifference(uint256 _a, uint256 _b) internal pure returns (uint) {
return (_a >= _b) ? _a - _b : _b - _a;
}
function _computeNominalCR(uint256 _coll, uint256 _debt) internal pure returns (uint) {
if (_debt > 0) {
return _coll * NICR_PRECISION / _debt;
}
// Return the maximal value for uint256 if the Vault has a debt of 0. Represents "infinite" CR.
else { // if (_debt == 0)
return type(uint256).max;
}
}
function _computeCR(uint256 _coll, uint256 _debt, uint256 _price) internal pure returns (uint) {
if (_debt > 0) {
uint256 newCollRatio = _coll * _price / _debt;
return newCollRatio;
}
// Return the maximal value for uint256 if the Vault has a debt of 0. Represents "infinite" CR.
else { // if (_debt == 0)
return type(uint256).max;
}
}
}
contracts/Dependencies/Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
/**
* Based on OpenZeppelin's Ownable contract:
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
*
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () {
_owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*
* NOTE: This function is not safe, as it doesn’t check owner is calling it.
* Make sure you check it before calling it.
*/
function _renounceOwnership() internal {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
contracts/Interfaces/IPriceFeed.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
interface IPriceFeed {
// --- Events ---
event LastGoodPriceUpdated(uint256 _lastGoodPrice);
// --- Function ---
function tellorDigits() external returns (uint256);
function fetchPrice() external returns (uint);
}
contracts/Interfaces/ITellorCaller.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
interface ITellorCaller {
function getTellorCurrentValue() external returns (bool, uint256, uint256);
}
Compiler Settings
{"viaIR":true,"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata","devdoc","userdoc","storageLayout","evm.gasEstimates"],"":["ast"]}},"optimizer":{"runs":1,"enabled":true},"metadata":{"useLiteralContent":true},"libraries":{},"evmVersion":"shanghai"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"uint256","name":"_tellorDigits","internalType":"uint256"}]},{"type":"event","name":"LastGoodPriceUpdated","inputs":[{"type":"uint256","name":"_lastGoodPrice","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"PriceFeedStatusChanged","inputs":[{"type":"uint8","name":"newStatus","internalType":"enum PriceFeed.Status","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"DECIMAL_PRECISION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_PRICE_DEVIATION_FROM_PREVIOUS_ROUND","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_PRICE_DIFFERENCE_BETWEEN_ORACLES","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_TIME_OUT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"NAME","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"TARGET_DIGITS","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"fetchPrice","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"forceExitBothUntrustedStatus","inputs":[{"type":"bool","name":"tryTellorFirst","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isOwner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastGoodPrice","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract AggregatorV3Interface"}],"name":"priceAggregator","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAddresses","inputs":[{"type":"address","name":"_priceAggregatorAddress","internalType":"address"},{"type":"address","name":"_tellorCallerAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"enum PriceFeed.Status"}],"name":"status","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ITellorCaller"}],"name":"tellorCaller","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tellorDigits","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]}]
Contract Creation Code
0x60a03461010457601f61171738819003918201601f19168301916001600160401b038311848410176101085780849260209460405283398101031261010457515f80546001600160a01b031916339081178255604051917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3811515806100f9575b156100ab57506080526040516115fa908161011d8239608051818181610446015261156d0152f35b62461bcd60e51b8152602060048201526024808201527f5072696365466565643a2077726f6e6720646563696d616c7320666f72205465604482015263363637b960e11b6064820152608490fd5b506012821115610083565b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c9081630490be83146104a75750806309ff5d331461048b5780630fdb11cf146104695780631a1fe6531461042f5780631be5c92f14610414578063200d2ed2146103d75780633078fff5146103af57806345079cb41461038e57806358a6aa881461036c5780637c61e3bf146103445780638da5cb5b1461031d5780638f32d59b146102f757806390107afe146102ae5780639150cac014610272578063a20baee614610250578063a3f4df7e146101ad5763f2fde38b146100d7575f80fd5b346101a95760203660031901126101a9576100f06104c1565b61010460018060a01b035f54163314610a97565b6001600160a01b03168015610155575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b5f80fd5b346101a9575f3660031901126101a957604080519081016001600160401b0381118282101761023c576040526009815268141c9a58d95199595960ba1b60208201526040519060208252818151918260208301525f5b838110610224575050815f6040809484010152601f80199101168101030190f35b60208282018101516040878401015285935001610203565b634e487b7160e01b5f52604160045260245ffd5b346101a9575f3660031901126101a9576020604051670de0b6b3a76400008152f35b346101a95760203660031901126101a95760043580151581036101a9576102ac906102a760018060a01b035f54163314610a97565b610d13565b005b346101a95760403660031901126101a9576102c76104c1565b6024356001600160a01b03811681036101a9576102ac916102f260018060a01b035f54163314610a97565b610aff565b346101a9575f3660031901126101a957602060018060a01b035f54163314604051908152f35b346101a9575f3660031901126101a9575f546040516001600160a01b039091168152602090f35b346101a9575f3660031901126101a9576002546040516001600160a01b039091168152602090f35b346101a9575f3660031901126101a95760206040516706f05b59d3b200008152f35b346101a9575f3660031901126101a957602060405166b1a2bc2ec500008152f35b346101a9575f3660031901126101a9576001546040516001600160a01b039091168152602090f35b346101a9575f3660031901126101a95760ff600454166040516005821015610400576020918152f35b634e487b7160e01b5f52602160045260245ffd5b346101a9575f3660031901126101a957602060405160128152f35b346101a9575f3660031901126101a95760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101a9575f3660031901126101a95760206104836104fa565b604051908152f35b346101a9575f3660031901126101a95760206040516138408152f35b346101a9575f3660031901126101a9576020906003548152f35b600435906001600160a01b03821682036101a957565b601f909101601f19168101906001600160401b0382119082101761023c57604052565b610502610ead565b6001546001600160a01b0316156109b95761051b61108d565b9061053760018060501b0383511660ff608085015116906111a3565b9060ff60045416600581101561040057156108715760ff600454166005811015610400576001146108495760ff6004541660058110156104005760021461080a5760ff600454166005811015610400576003146106825760ff600454166005811015610400576004146105ad5750505060035490565b6105b683611455565b8015610673575b61065e576105ca83610fe2565b610655576105d99082846113d6565b61062a576105e790826112a6565b6105f7576105f4906113bf565b90565b5061060a600260ff196004541617600455565b604051600281525f5160206115a55f395f51905f5290602090a160035490565b506105f49060ff19600454166004555f5160206115a55f395f51905f5260206040515f8152a16113bf565b50505060035490565b50505061060a600260ff196004541617600455565b5061067d82611455565b6105bd565b919061068d82611455565b9081156107fa575b506107c1576106a381610fe2565b610769576106b082610f81565b61073d576106bd82610fe2565b610735576106cb8282611339565b61070a57506020610701916106de610ae2565b5f5160206115a55f395f51905f5260405180848101600182520390a1015161156b565b6105f481611500565b6105f4915060ff19600454166004555f5160206115a55f395f51905f5260206040515f8152a16113bf565b505060035490565b6105f4915061074a610af1565b604051600481525f5160206115a55f395f51905f5290602090a16113bf565b5061077381610f81565b6107985761078081610fe2565b61079157602061070191015161156b565b5060035490565b506107a1610af1565b604051600481525f5160206115a55f395f51905f5290602090a160035490565b506107cb81610f81565b6105f7576107d7610ae2565b604051600181525f5160206115a55f395f51905f5290602090a161078081610fe2565b6108049150611455565b5f610695565b61081491836113d6565b61081f575060035490565b6105f49060ff19600454166004555f5160206115a55f395f51905f5260206040515f8152a16113bf565b91908261085691836113d6565b61070a575061086481610f81565b6105f75761078081610fe2565b919061087c82611455565b80156109aa575b6109775761089082610fe2565b61093a5761089e90826112a6565b6108bb576108ae6105f492610f81565b156113bf5761074a610af1565b6108c482610f81565b610926576108d182610fe2565b6108fc576108df8282611339565b6108f257506020610701916106de610ae2565b6105f491506113bf565b5050610906610ae2565b604051600181525f5160206115a55f395f51905f5290602090a160035490565b505061060a600260ff196004541617600455565b505061094581610f81565b61079857600360ff1960045416176004555f5160206115a55f395f51905f52602060405160038152a161078081610fe2565b505061098281610f81565b6105f75761098f81610fe2565b6109a1576020610701916106de610ae2565b50610906610ae2565b506109b481611455565b610883565b6109c281610f81565b610a52576109cf81610fe2565b610a0e5760206109e091015161156b565b6109e981611500565b6109f1610ae2565b604051600181525f5160206115a55f395f51905f5290602090a190565b606460405162461bcd60e51b815260206004820152602060248201527f5072696365466565643a2054656c6c6f72207072696365206973207374616c656044820152fd5b60405162461bcd60e51b815260206004820152601d60248201527f5072696365466565643a2054656c6c6f7220756e617661696c61626c650000006044820152606490fd5b15610a9e57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b600160ff196004541617600455565b600460ff1981541617600455565b60025491906001600160a01b038316610cce576001600160a01b038216918215610c89573b15610c4557600180546001600160a01b03929092166001600160a01b03199283168117909155921617600255610c03575b610b5d610ead565b610b6681610f81565b1580610bf3575b15610b9157610b826020610b8792015161156b565b611500565b610b8f610ae2565b565b60405162461bcd60e51b815260206004820152603460248201527f5072696365466565643a206e6f20667265736820696e697469616c2070726963604482015273652066726f6d20656974686572206f7261636c6560601b6064820152608490fd5b50610bfd81610fe2565b15610b6d565b610c0b61108d565b610c1481611455565b15610c1f5750610b55565b610b828160ff60806020610c3995015192015116906114ab565b60ff1960045416600455565b606460405162461bcd60e51b815260206004820152602060248201527f4163636f756e7420636f64652073697a652063616e6e6f74206265207a65726f6044820152fd5b60405162461bcd60e51b815260206004820152601e60248201527f4163636f756e742063616e6e6f74206265207a65726f206164647265737300006044820152606490fd5b60405162461bcd60e51b815260206004820152601f60248201527f5072696365466565643a20636f6e746163747320616c726561647920736574006044820152606490fd5b60ff60045416600581101561040057600203610e5657610d3161108d565b90610d4d60018060501b0383511660ff608085015116906111a3565b91610d56610ead565b928280610e47575b610e4157610d6b82611455565b8015610e32575b159081610e20575b81610e0e575b50610dfc5750159081610dec575b50610b8f5760405162461bcd60e51b815260206004820152602b60248201527f5072696365466565643a20626f7468206f7261636c657320617265207374696c60448201526a1b081d5b9d1c9d5cdd195960aa1b6064820152608490fd5b610df69150611531565b5f610d8e565b9050610e0b915061074a610af1565b50565b610e199150826112a6565b155f610d80565b9050610e2b82610fe2565b1590610d7a565b50610e3c81611455565b610d72565b50505050565b50610e5184611531565b610d5e565b60405162461bcd60e51b815260206004820152602960248201527f5072696365466565643a206174206c65617374206f6e65206f7261636c6520696044820152687320776f726b696e6760b81b6064820152608490fd5b604051608081016001600160401b0381118282101761023c576040525f815260208101905f825260408101915f835260608201905f825282935f606060018060a01b0360025416600460405180948193631328670560e31b83525af1925f845f935f96610f33575b50610f24575050505050505090565b60019697501515875252525290565b93509450506060823d606011610f79575b81610f51606093836104d7565b810103126101a95781519182151583036101a95760406020820151910151929092945f610f15565b3d9150610f44565b606081015115610fb15760408101518015908115610fb7575b50610fb1576020015115610fac575f90565b600190565b50600190565b905042105f610f9a565b91908203918211610fce57565b634e487b7160e01b5f52601160045260245ffd5b60608101511561100857604001518015610fb1576110036138409142610fc1565b101590565b505f90565b6040519060a082016001600160401b0381118382101761023c576040525f6080838281528260208201528260408201528260608201520152565b51906001600160501b03821682036101a957565b908160a09103126101a95761106f81611047565b916020820151916040810151916105f4608060608401519301611047565b61109561100d565b6001546001600160a01b0316801561119f5760405163313ce56760e01b8152602081600481855afa5f9181611161575b506110cf57505090565b60ff166080830152604051633fabe5a360e21b81529060a090829060049082905afa905f825f925f94611127575b506111085750505090565b6001600160501b03168352602083015260408201526001606082015290565b9150925061114d915060a03d60a01161115a575b61114581836104d7565b81019061105b565b509290509091925f6110fd565b503d61113b565b9091506020813d602011611197575b8161117d602093836104d7565b810103126101a9575160ff811681036101a957905f6110c5565b3d9150611170565b5090565b6111ab61100d565b9160018060a01b036001541691821561126f576001600160501b0316801561126f575f19016001600160501b038111610fce57604051639a6fc8f560e01b81526001600160501b0390911660048201529160a090839060249082905afa5f92815f915f93611247575b50611220575050505090565b60ff9360018060501b03168552602085015260408401521660808201526001606082015290565b91509350611264915060a03d60a01161115a5761114581836104d7565b50929490505f611214565b50505090565b81810292918115918404141715610fce57565b8115611292570490565b634e487b7160e01b5f52601260045260245ffd5b6112d76112c36112f29260ff6080602083015192015116906114ab565b9260ff6080602083015192015116906114ab565b808310929083156113325781935b61132a5750915b82610fc1565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610fce576706f05b59d3b200009161132691611288565b1190565b9050916112ec565b80936112e5565b61135f60206113576113789360ff60808483015192015116906114ab565b93015161156b565b8281109081156113b757809384925b6113b05750610fc1565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610fce5766b1a2bc2ec50000916113ab91611288565b111590565b9050610fc1565b83849261136e565b6107019060ff6080602083015192015116906114ab565b91906113e182610f81565b908115611444575b811561141a575b50801561140b575b611405576105f491611339565b50505f90565b5061141582610fe2565b6113f8565b905061142583611455565b908115611434575b505f6113f0565b61143e9150611455565b5f61142d565b905061144f82610fe2565b906113e9565b606081015115610fb15780516001600160501b031615610fb15760408101518015908115611493575b50610fb15760205f9101511315610fac575f90565b905042105f61147e565b604d8111610fce57600a0a90565b5f60128310806114d55750506011198201918211610fce576114cf6105f49261149d565b90611288565b9290926114e157505090565b601290810392508211610fce576114fa6105f49261149d565b90611275565b60207f4d29de21de555af78a62fc82dd4bc05e9ae5b0660a37f04729527e0f22780cd39180600355604051908152a1565b61153a81610f81565b158061155b575b61154a57505f90565b610b826020610fac926106de610ae2565b5061156581610fe2565b15611541565b7f00000000000000000000000000000000000000000000000000000000000000006012039060128211610fce576114fa6105f49261149d56fe5c57579a8214fe4f710c1c56fa829f045b9fa6d225a744225a30c32188064d4ea264697066735822122009c21c207b955c9a77ff248423cacbfdbe9ad1f0efbfc1245ef2ac1977af377664736f6c634300081b00330000000000000000000000000000000000000000000000000000000000000012
Deployed ByteCode
0x6080806040526004361015610012575f80fd5b5f3560e01c9081630490be83146104a75750806309ff5d331461048b5780630fdb11cf146104695780631a1fe6531461042f5780631be5c92f14610414578063200d2ed2146103d75780633078fff5146103af57806345079cb41461038e57806358a6aa881461036c5780637c61e3bf146103445780638da5cb5b1461031d5780638f32d59b146102f757806390107afe146102ae5780639150cac014610272578063a20baee614610250578063a3f4df7e146101ad5763f2fde38b146100d7575f80fd5b346101a95760203660031901126101a9576100f06104c1565b61010460018060a01b035f54163314610a97565b6001600160a01b03168015610155575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b5f80fd5b346101a9575f3660031901126101a957604080519081016001600160401b0381118282101761023c576040526009815268141c9a58d95199595960ba1b60208201526040519060208252818151918260208301525f5b838110610224575050815f6040809484010152601f80199101168101030190f35b60208282018101516040878401015285935001610203565b634e487b7160e01b5f52604160045260245ffd5b346101a9575f3660031901126101a9576020604051670de0b6b3a76400008152f35b346101a95760203660031901126101a95760043580151581036101a9576102ac906102a760018060a01b035f54163314610a97565b610d13565b005b346101a95760403660031901126101a9576102c76104c1565b6024356001600160a01b03811681036101a9576102ac916102f260018060a01b035f54163314610a97565b610aff565b346101a9575f3660031901126101a957602060018060a01b035f54163314604051908152f35b346101a9575f3660031901126101a9575f546040516001600160a01b039091168152602090f35b346101a9575f3660031901126101a9576002546040516001600160a01b039091168152602090f35b346101a9575f3660031901126101a95760206040516706f05b59d3b200008152f35b346101a9575f3660031901126101a957602060405166b1a2bc2ec500008152f35b346101a9575f3660031901126101a9576001546040516001600160a01b039091168152602090f35b346101a9575f3660031901126101a95760ff600454166040516005821015610400576020918152f35b634e487b7160e01b5f52602160045260245ffd5b346101a9575f3660031901126101a957602060405160128152f35b346101a9575f3660031901126101a95760206040517f00000000000000000000000000000000000000000000000000000000000000128152f35b346101a9575f3660031901126101a95760206104836104fa565b604051908152f35b346101a9575f3660031901126101a95760206040516138408152f35b346101a9575f3660031901126101a9576020906003548152f35b600435906001600160a01b03821682036101a957565b601f909101601f19168101906001600160401b0382119082101761023c57604052565b610502610ead565b6001546001600160a01b0316156109b95761051b61108d565b9061053760018060501b0383511660ff608085015116906111a3565b9060ff60045416600581101561040057156108715760ff600454166005811015610400576001146108495760ff6004541660058110156104005760021461080a5760ff600454166005811015610400576003146106825760ff600454166005811015610400576004146105ad5750505060035490565b6105b683611455565b8015610673575b61065e576105ca83610fe2565b610655576105d99082846113d6565b61062a576105e790826112a6565b6105f7576105f4906113bf565b90565b5061060a600260ff196004541617600455565b604051600281525f5160206115a55f395f51905f5290602090a160035490565b506105f49060ff19600454166004555f5160206115a55f395f51905f5260206040515f8152a16113bf565b50505060035490565b50505061060a600260ff196004541617600455565b5061067d82611455565b6105bd565b919061068d82611455565b9081156107fa575b506107c1576106a381610fe2565b610769576106b082610f81565b61073d576106bd82610fe2565b610735576106cb8282611339565b61070a57506020610701916106de610ae2565b5f5160206115a55f395f51905f5260405180848101600182520390a1015161156b565b6105f481611500565b6105f4915060ff19600454166004555f5160206115a55f395f51905f5260206040515f8152a16113bf565b505060035490565b6105f4915061074a610af1565b604051600481525f5160206115a55f395f51905f5290602090a16113bf565b5061077381610f81565b6107985761078081610fe2565b61079157602061070191015161156b565b5060035490565b506107a1610af1565b604051600481525f5160206115a55f395f51905f5290602090a160035490565b506107cb81610f81565b6105f7576107d7610ae2565b604051600181525f5160206115a55f395f51905f5290602090a161078081610fe2565b6108049150611455565b5f610695565b61081491836113d6565b61081f575060035490565b6105f49060ff19600454166004555f5160206115a55f395f51905f5260206040515f8152a16113bf565b91908261085691836113d6565b61070a575061086481610f81565b6105f75761078081610fe2565b919061087c82611455565b80156109aa575b6109775761089082610fe2565b61093a5761089e90826112a6565b6108bb576108ae6105f492610f81565b156113bf5761074a610af1565b6108c482610f81565b610926576108d182610fe2565b6108fc576108df8282611339565b6108f257506020610701916106de610ae2565b6105f491506113bf565b5050610906610ae2565b604051600181525f5160206115a55f395f51905f5290602090a160035490565b505061060a600260ff196004541617600455565b505061094581610f81565b61079857600360ff1960045416176004555f5160206115a55f395f51905f52602060405160038152a161078081610fe2565b505061098281610f81565b6105f75761098f81610fe2565b6109a1576020610701916106de610ae2565b50610906610ae2565b506109b481611455565b610883565b6109c281610f81565b610a52576109cf81610fe2565b610a0e5760206109e091015161156b565b6109e981611500565b6109f1610ae2565b604051600181525f5160206115a55f395f51905f5290602090a190565b606460405162461bcd60e51b815260206004820152602060248201527f5072696365466565643a2054656c6c6f72207072696365206973207374616c656044820152fd5b60405162461bcd60e51b815260206004820152601d60248201527f5072696365466565643a2054656c6c6f7220756e617661696c61626c650000006044820152606490fd5b15610a9e57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b600160ff196004541617600455565b600460ff1981541617600455565b60025491906001600160a01b038316610cce576001600160a01b038216918215610c89573b15610c4557600180546001600160a01b03929092166001600160a01b03199283168117909155921617600255610c03575b610b5d610ead565b610b6681610f81565b1580610bf3575b15610b9157610b826020610b8792015161156b565b611500565b610b8f610ae2565b565b60405162461bcd60e51b815260206004820152603460248201527f5072696365466565643a206e6f20667265736820696e697469616c2070726963604482015273652066726f6d20656974686572206f7261636c6560601b6064820152608490fd5b50610bfd81610fe2565b15610b6d565b610c0b61108d565b610c1481611455565b15610c1f5750610b55565b610b828160ff60806020610c3995015192015116906114ab565b60ff1960045416600455565b606460405162461bcd60e51b815260206004820152602060248201527f4163636f756e7420636f64652073697a652063616e6e6f74206265207a65726f6044820152fd5b60405162461bcd60e51b815260206004820152601e60248201527f4163636f756e742063616e6e6f74206265207a65726f206164647265737300006044820152606490fd5b60405162461bcd60e51b815260206004820152601f60248201527f5072696365466565643a20636f6e746163747320616c726561647920736574006044820152606490fd5b60ff60045416600581101561040057600203610e5657610d3161108d565b90610d4d60018060501b0383511660ff608085015116906111a3565b91610d56610ead565b928280610e47575b610e4157610d6b82611455565b8015610e32575b159081610e20575b81610e0e575b50610dfc5750159081610dec575b50610b8f5760405162461bcd60e51b815260206004820152602b60248201527f5072696365466565643a20626f7468206f7261636c657320617265207374696c60448201526a1b081d5b9d1c9d5cdd195960aa1b6064820152608490fd5b610df69150611531565b5f610d8e565b9050610e0b915061074a610af1565b50565b610e199150826112a6565b155f610d80565b9050610e2b82610fe2565b1590610d7a565b50610e3c81611455565b610d72565b50505050565b50610e5184611531565b610d5e565b60405162461bcd60e51b815260206004820152602960248201527f5072696365466565643a206174206c65617374206f6e65206f7261636c6520696044820152687320776f726b696e6760b81b6064820152608490fd5b604051608081016001600160401b0381118282101761023c576040525f815260208101905f825260408101915f835260608201905f825282935f606060018060a01b0360025416600460405180948193631328670560e31b83525af1925f845f935f96610f33575b50610f24575050505050505090565b60019697501515875252525290565b93509450506060823d606011610f79575b81610f51606093836104d7565b810103126101a95781519182151583036101a95760406020820151910151929092945f610f15565b3d9150610f44565b606081015115610fb15760408101518015908115610fb7575b50610fb1576020015115610fac575f90565b600190565b50600190565b905042105f610f9a565b91908203918211610fce57565b634e487b7160e01b5f52601160045260245ffd5b60608101511561100857604001518015610fb1576110036138409142610fc1565b101590565b505f90565b6040519060a082016001600160401b0381118382101761023c576040525f6080838281528260208201528260408201528260608201520152565b51906001600160501b03821682036101a957565b908160a09103126101a95761106f81611047565b916020820151916040810151916105f4608060608401519301611047565b61109561100d565b6001546001600160a01b0316801561119f5760405163313ce56760e01b8152602081600481855afa5f9181611161575b506110cf57505090565b60ff166080830152604051633fabe5a360e21b81529060a090829060049082905afa905f825f925f94611127575b506111085750505090565b6001600160501b03168352602083015260408201526001606082015290565b9150925061114d915060a03d60a01161115a575b61114581836104d7565b81019061105b565b509290509091925f6110fd565b503d61113b565b9091506020813d602011611197575b8161117d602093836104d7565b810103126101a9575160ff811681036101a957905f6110c5565b3d9150611170565b5090565b6111ab61100d565b9160018060a01b036001541691821561126f576001600160501b0316801561126f575f19016001600160501b038111610fce57604051639a6fc8f560e01b81526001600160501b0390911660048201529160a090839060249082905afa5f92815f915f93611247575b50611220575050505090565b60ff9360018060501b03168552602085015260408401521660808201526001606082015290565b91509350611264915060a03d60a01161115a5761114581836104d7565b50929490505f611214565b50505090565b81810292918115918404141715610fce57565b8115611292570490565b634e487b7160e01b5f52601260045260245ffd5b6112d76112c36112f29260ff6080602083015192015116906114ab565b9260ff6080602083015192015116906114ab565b808310929083156113325781935b61132a5750915b82610fc1565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610fce576706f05b59d3b200009161132691611288565b1190565b9050916112ec565b80936112e5565b61135f60206113576113789360ff60808483015192015116906114ab565b93015161156b565b8281109081156113b757809384925b6113b05750610fc1565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610fce5766b1a2bc2ec50000916113ab91611288565b111590565b9050610fc1565b83849261136e565b6107019060ff6080602083015192015116906114ab565b91906113e182610f81565b908115611444575b811561141a575b50801561140b575b611405576105f491611339565b50505f90565b5061141582610fe2565b6113f8565b905061142583611455565b908115611434575b505f6113f0565b61143e9150611455565b5f61142d565b905061144f82610fe2565b906113e9565b606081015115610fb15780516001600160501b031615610fb15760408101518015908115611493575b50610fb15760205f9101511315610fac575f90565b905042105f61147e565b604d8111610fce57600a0a90565b5f60128310806114d55750506011198201918211610fce576114cf6105f49261149d565b90611288565b9290926114e157505090565b601290810392508211610fce576114fa6105f49261149d565b90611275565b60207f4d29de21de555af78a62fc82dd4bc05e9ae5b0660a37f04729527e0f22780cd39180600355604051908152a1565b61153a81610f81565b158061155b575b61154a57505f90565b610b826020610fac926106de610ae2565b5061156581610fe2565b15611541565b7f00000000000000000000000000000000000000000000000000000000000000126012039060128211610fce576114fa6105f49261149d56fe5c57579a8214fe4f710c1c56fa829f045b9fa6d225a744225a30c32188064d4ea264697066735822122009c21c207b955c9a77ff248423cacbfdbe9ad1f0efbfc1245ef2ac1977af377664736f6c634300081b0033