Source Code
Overview
HYPE Balance
HYPE Value
$0.00Latest 12 from a total of 12 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Transmit Seconda... | 25347984 | 7 days ago | IN | 0 HYPE | 0.00009206 | ||||
| Transmit | 25347984 | 7 days ago | IN | 0 HYPE | 0.00009206 | ||||
| Transmit Seconda... | 25262004 | 8 days ago | IN | 0 HYPE | 0.00003078 | ||||
| Transmit | 25262004 | 8 days ago | IN | 0 HYPE | 0.00003078 | ||||
| Transmit | 25195749 | 9 days ago | IN | 0 HYPE | 0.00007384 | ||||
| Transmit Seconda... | 25195748 | 9 days ago | IN | 0 HYPE | 0.00007384 | ||||
| Add Access | 25175098 | 9 days ago | IN | 0 HYPE | 0.00013544 | ||||
| Add Access | 25175037 | 9 days ago | IN | 0 HYPE | 0.00005158 | ||||
| Transfer Ownersh... | 25174915 | 9 days ago | IN | 0 HYPE | 0.00024183 | ||||
| Set Config | 25174793 | 9 days ago | IN | 0 HYPE | 0.00037497 | ||||
| Set Billing | 25174732 | 9 days ago | IN | 0 HYPE | 0.00030661 | ||||
| Set Payees | 25174671 | 9 days ago | IN | 0 HYPE | 0.00101294 |
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | ||||
|---|---|---|---|---|---|---|---|
| 26039270 | 39 mins ago | 0 HYPE | |||||
| 26039270 | 39 mins ago | 0 HYPE | |||||
| 26039270 | 39 mins ago | 0 HYPE | |||||
| 26039270 | 39 mins ago | 0 HYPE | |||||
| 26039270 | 39 mins ago | 0 HYPE | |||||
| 26022182 | 5 hrs ago | 0 HYPE | |||||
| 26022182 | 5 hrs ago | 0 HYPE | |||||
| 26022182 | 5 hrs ago | 0 HYPE | |||||
| 26022182 | 5 hrs ago | 0 HYPE | |||||
| 26022182 | 5 hrs ago | 0 HYPE | |||||
| 26021754 | 5 hrs ago | 0 HYPE | |||||
| 26021571 | 5 hrs ago | 0 HYPE | |||||
| 26021571 | 5 hrs ago | 0 HYPE | |||||
| 26021571 | 5 hrs ago | 0 HYPE | |||||
| 26021571 | 5 hrs ago | 0 HYPE | |||||
| 26021571 | 5 hrs ago | 0 HYPE | |||||
| 26020778 | 5 hrs ago | 0 HYPE | |||||
| 26020656 | 5 hrs ago | 0 HYPE | |||||
| 26020656 | 5 hrs ago | 0 HYPE | |||||
| 26020656 | 5 hrs ago | 0 HYPE | |||||
| 26020656 | 5 hrs ago | 0 HYPE | |||||
| 26020656 | 5 hrs ago | 0 HYPE | |||||
| 26017646 | 6 hrs ago | 0 HYPE | |||||
| 26017646 | 6 hrs ago | 0 HYPE | |||||
| 26017646 | 6 hrs ago | 0 HYPE |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
DualAggregator
Compiler Version
v0.8.24+commit.e11b9ed9
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL 1.1
pragma solidity 0.8.24;
import {AccessControllerInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AccessControllerInterface.sol";
import {AggregatorV2V3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol";
import {AggregatorValidatorInterface} from
"@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorValidatorInterface.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";
import {SimpleReadAccessController} from "@chainlink/contracts/src/v0.8/shared/access/SimpleReadAccessController.sol";
import {CallWithExactGas} from "@chainlink/contracts/src/v0.8/shared/call/CallWithExactGas.sol";
import {OCR2Abstract} from "@chainlink/contracts/src/v0.8/shared/ocr2/OCR2Abstract.sol";
// This contract is a port of OCR2Aggregator from `libocr` it is being used
// for a new feeds based project that is ongoing, there will be some modernization
// that happens to this contract as the project progresses.
// solhint-disable max-states-count
contract DualAggregator is OCR2Abstract, AggregatorV2V3Interface, SimpleReadAccessController {
string public constant override typeAndVersion = "DualAggregator 1.0.0";
// This contract is divided into sections. Each section defines a set of
// variables, events, and functions that belong together.
// ================================================================
// │ Variables used in multiple other sections │
// ================================================================
// Transmitter information.
struct Transmitter {
bool active; // ─────────╮ True if active.
uint8 index; // │ Index in `s_transmittersList`.
// │
// │ Juels-denominated payment for transmitters, covering gas costs incurred
// │ by the transmitter plus additional rewards. The entire LINK supply (1e9
uint96 paymentJuels; // ─╯ LINK = 1e27 Juels) will always fit into a uint96.
}
// Signer information.
struct Signer {
bool active; // ─╮ True if active.
uint8 index; // ─╯ Index of oracle in `s_signersList`.
}
// Storing these fields used on the hot path in a HotVars variable reduces the
// retrieval of all of them to one SLOAD.
struct HotVars {
uint8 f; // ─────────────────────────╮ Maximum number of faulty oracles.
// │
uint40 latestEpochAndRound; // │ Epoch and round from OCR protocol,
// │ 32 most significant bits for epoch, 8 least sig bits for round.
// │
uint32 latestAggregatorRoundId; // │ Chainlink Aggregators expose a roundId to consumers. The offchain protocol
// │ does not use this id anywhere. We increment it whenever a new transmission
// │ is made to provide callers with contiguous ids for successive reports.
uint32 latestSecondaryRoundId; // │ Latest transmission round arrived from the Secondary Proxy.
uint32 maximumGasPriceGwei; // │ Highest compensated gas price, in gwei uints.
uint32 reasonableGasPriceGwei; // │ If gas price is less (in gwei units), transmitter gets half the savings.
uint32 observationPaymentGjuels; // │ Fixed LINK reward for each observer.
uint32 transmissionPaymentGjuels; // │ Fixed reward for transmitter.
bool isLatestSecondary; // ───────────╯ Whether the latest report was secondary or not
}
/// @notice mapping containing the transmitter information of each transmitter address.
mapping(address transmitterAddress => Transmitter transmitter) internal s_transmitters;
/// @notice mapping containing the signer information of each signer address.
mapping(address signerAddress => Signer signer) internal s_signers;
/// @notice s_signersList contains the signing address of each oracle.
address[] internal s_signersList;
/// @notice s_transmittersList contains the transmission address of each oracle,
/// i.e. the address the oracle actually sends transactions to the contract from.
address[] internal s_transmittersList;
/// @notice We assume that all oracles contribute observations to all rounds. This
/// variable tracks (per-oracle) from what round an oracle should be rewarded,
/// i.e. the oracle gets (latestAggregatorRoundId - rewardFromAggregatorRoundId) * reward.
uint32[MAX_NUM_ORACLES] internal s_rewardFromAggregatorRoundId;
/// @notice latest setted config.
bytes32 internal s_latestConfigDigest;
/// @notice overhead incurred by accounting logic.
uint24 internal s_accountingGas;
/// @notice most common fields used on the hot path.
HotVars internal s_hotVars;
/// @notice lowest answer the system is allowed to report in response to transmissions.
int192 internal immutable i_minAnswer;
/// @notice highest answer the system is allowed to report in response to transmissions.
int192 internal immutable i_maxAnswer;
/// @param link address of the LINK contract.
/// @param minAnswer_ lowest answer the median of a report is allowed to be.
/// @param maxAnswer_ highest answer the median of a report is allowed to be.
/// @param billingAccessController access controller for managing the billing.
/// @param requesterAccessController access controller for requesting new rounds.
/// @param decimals_ answers are stored in fixed-point format, with this many digits of precision.
/// @param description_ short human-readable description of observable this contract's answers pertain to.
/// @param secondaryProxy_ proxy address to manage the secondary reports.
/// @param cutoffTime_ timetamp to define the window in which a secondary report is valid.
/// @param maxSyncIterations_ max iterations the secondary proxy will be able to loop to sync with the primary rounds.
constructor(
LinkTokenInterface link,
int192 minAnswer_,
int192 maxAnswer_,
AccessControllerInterface billingAccessController,
AccessControllerInterface requesterAccessController,
uint8 decimals_,
string memory description_,
address secondaryProxy_,
uint32 cutoffTime_,
uint32 maxSyncIterations_
) {
i_decimals = decimals_;
i_minAnswer = minAnswer_;
i_maxAnswer = maxAnswer_;
i_secondaryProxy = secondaryProxy_;
i_maxSyncIterations = maxSyncIterations_;
s_linkToken = link;
emit LinkTokenSet(LinkTokenInterface(address(0)), link);
_setBillingAccessController(billingAccessController);
setRequesterAccessController(requesterAccessController);
setValidatorConfig(AggregatorValidatorInterface(address(0x0)), 0);
s_cutoffTime = cutoffTime_;
emit CutoffTimeSet(cutoffTime_);
s_description = description_;
}
// ================================================================
// │ OCR2Abstract Configuration │
// ================================================================
// SetConfig information
struct SetConfigArgs {
uint64 offchainConfigVersion; // ─╮ OffchainConfig version.
uint8 f; // ──────────────────────╯ Faulty Oracles amount.
bytes onchainConfig; // Onchain configuration.
bytes offchainConfig; // Offchain configuration.
address[] signers; // Signing addresses of each oracle.
address[] transmitters; // Transmitting addresses of each oracle.
}
error FMustBePositive();
error TooManyOracles();
error OracleLengthMismatch();
error FaultyOracleFTooHigh();
error InvalidOnChainConfig();
error RepeatedSignerAddress();
error RepeatedTransmitterAddress();
/// @notice incremented each time a new config is posted. This count is incorporated
/// into the config digest to prevent replay attacks.
uint32 internal s_configCount;
/// @notice makes it easier for offchain systems to extract config from logs.
uint32 internal s_latestConfigBlockNumber;
/// @notice check if `f` is a positive number.
/// @dev left as a function so this check can be disabled in derived contracts.
/// @param f amount of faulty oracles to check.
function _requirePositiveF(
uint256 f
) internal pure virtual {
if (f <= 0) {
revert FMustBePositive();
}
}
/// @inheritdoc OCR2Abstract
function setConfig(
address[] memory signers,
address[] memory transmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) external override onlyOwner {
if (signers.length > MAX_NUM_ORACLES) {
revert TooManyOracles();
}
if (signers.length != transmitters.length) {
revert OracleLengthMismatch();
}
if (3 * f >= signers.length) {
revert FaultyOracleFTooHigh();
}
_requirePositiveF(f);
if (keccak256(onchainConfig) != keccak256(abi.encodePacked(uint8(1), /*version*/ i_minAnswer, i_maxAnswer))) {
revert InvalidOnChainConfig();
}
SetConfigArgs memory args = SetConfigArgs({
signers: signers,
transmitters: transmitters,
f: f,
onchainConfig: onchainConfig,
offchainConfigVersion: offchainConfigVersion,
offchainConfig: offchainConfig
});
s_hotVars.latestEpochAndRound = 0;
_payOracles();
// Remove any old signer/transmitter addresses.
uint256 oldLength = s_signersList.length;
for (uint256 i = 0; i < oldLength; ++i) {
address signer = s_signersList[i];
address transmitter = s_transmittersList[i];
delete s_signers[signer];
delete s_transmitters[transmitter];
}
delete s_signersList;
delete s_transmittersList;
// Add new signer/transmitter addresses.
for (uint256 i = 0; i < args.signers.length; ++i) {
if (s_signers[args.signers[i]].active) {
revert RepeatedSignerAddress();
}
s_signers[args.signers[i]] = Signer({active: true, index: uint8(i)});
if (s_transmitters[args.transmitters[i]].active) {
revert RepeatedTransmitterAddress();
}
s_transmitters[args.transmitters[i]] = Transmitter({active: true, index: uint8(i), paymentJuels: 0});
}
s_signersList = args.signers;
s_transmittersList = args.transmitters;
s_hotVars.f = args.f;
uint32 previousConfigBlockNumber = s_latestConfigBlockNumber;
s_latestConfigBlockNumber = uint32(block.number);
s_configCount += 1;
s_latestConfigDigest = _configDigestFromConfigData(
block.chainid,
address(this),
s_configCount,
args.signers,
args.transmitters,
args.f,
args.onchainConfig,
args.offchainConfigVersion,
args.offchainConfig
);
emit ConfigSet(
previousConfigBlockNumber,
s_latestConfigDigest,
s_configCount,
args.signers,
args.transmitters,
args.f,
args.onchainConfig,
args.offchainConfigVersion,
args.offchainConfig
);
uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
for (uint256 i = 0; i < args.signers.length; ++i) {
s_rewardFromAggregatorRoundId[i] = latestAggregatorRoundId;
}
}
/// @inheritdoc OCR2Abstract
function latestConfigDetails()
external
view
override
returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest)
{
return (s_configCount, s_latestConfigBlockNumber, s_latestConfigDigest);
}
/// @notice get the transmitters list.
/// @dev The list will match the order used to specify the transmitter during setConfig.
/// @return s_transmittersList list of addresses permitted to transmit reports to this contract.
function getTransmitters() external view returns (address[] memory) {
return s_transmittersList;
}
/// @notice Get the mininum answer value.
/// @return minAnswer the lowest answer the system is allowed to report in a transmission.
function minAnswer() public view returns (int256) {
return i_minAnswer;
}
/// @notice Get the maximum answer value.
/// @return maxAnswer the highest answer the system is allowed to report in a transmission.
function maxAnswer() public view returns (int256) {
return i_maxAnswer;
}
// ================================================================
// │ Onchain Validation │
// ================================================================
// Configuration for validator.
struct ValidatorConfig {
AggregatorValidatorInterface validator; // ─╮ Validator contract interface.
uint32 gasLimit; // ────────────────────────╯ Gas limit defined for the validation call.
}
/// @notice indicates that the validator configuration has been set.
/// @param previousValidator previous validator contract.
/// @param previousGasLimit previous gas limit for validate calls.
/// @param currentValidator current validator contract.
/// @param currentGasLimit current gas limit for validate calls.
event ValidatorConfigSet(
AggregatorValidatorInterface indexed previousValidator,
uint32 previousGasLimit,
AggregatorValidatorInterface indexed currentValidator,
uint32 currentGasLimit
);
error InsufficientGas();
/// @notice contstant exact gas cushion defined to do a call.
uint16 private constant CALL_WITH_EXACT_GAS_CUSHION = 5_000;
/// @notice validator configuration.
ValidatorConfig private s_validatorConfig;
/// @notice get the validator configuration.
/// @return validator validator contract.
/// @return gasLimit gas limit for validate calls.
function getValidatorConfig() external view returns (AggregatorValidatorInterface validator, uint32 gasLimit) {
ValidatorConfig memory vc = s_validatorConfig;
return (vc.validator, vc.gasLimit);
}
/// @notice sets validator configuration.
/// @dev set newValidator to 0x0 to disable validate calls.
/// @param newValidator address of the new validator contract.
/// @param newGasLimit new gas limit for validate calls.
function setValidatorConfig(AggregatorValidatorInterface newValidator, uint32 newGasLimit) public onlyOwner {
ValidatorConfig memory previous = s_validatorConfig;
if (previous.validator != newValidator || previous.gasLimit != newGasLimit) {
s_validatorConfig = ValidatorConfig({validator: newValidator, gasLimit: newGasLimit});
emit ValidatorConfigSet(previous.validator, previous.gasLimit, newValidator, newGasLimit);
}
}
/// @notice validate the answer against the validator configuration.
/// @param aggregatorRoundId report round id to validate.
/// @param answer report answer to validate.
function _validateAnswer(uint32 aggregatorRoundId, int256 answer) private {
ValidatorConfig memory vc = s_validatorConfig;
if (address(vc.validator) == address(0)) {
return;
}
uint32 prevAggregatorRoundId = aggregatorRoundId - 1;
int256 prevAggregatorRoundAnswer = s_transmissions[prevAggregatorRoundId].answer;
(, bool sufficientGas) = CallWithExactGas._callWithExactGasEvenIfTargetIsNoContract(
abi.encodeCall(
AggregatorValidatorInterface.validate,
(uint256(prevAggregatorRoundId), prevAggregatorRoundAnswer, uint256(aggregatorRoundId), answer)
),
address(vc.validator),
vc.gasLimit,
CALL_WITH_EXACT_GAS_CUSHION
);
if (!sufficientGas) {
revert InsufficientGas();
}
}
// ================================================================
// │ RequestNewRound │
// ================================================================
/// @notice contract address with AccessController Interface.
AccessControllerInterface internal s_requesterAccessController;
/// @notice emitted when a new requester access controller contract is set.
/// @param old the address prior to the current setting.
/// @param current the address of the new access controller contract.
event RequesterAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);
/// @notice emitted to immediately request a new round.
/// @param requester the address of the requester.
/// @param configDigest the latest transmission's configDigest.
/// @param epoch the latest transmission's epoch.
/// @param round the latest transmission's round.
event RoundRequested(address indexed requester, bytes32 configDigest, uint32 epoch, uint8 round);
error OnlyOwnerAndRequesterCanCall();
/// @notice address of the requester access controller contract.
/// @return s_requesterAccessController requester access controller address.
function getRequesterAccessController() external view returns (AccessControllerInterface) {
return s_requesterAccessController;
}
/// @notice sets the new requester access controller.
/// @param requesterAccessController designates the address of the new requester access controller.
function setRequesterAccessController(
AccessControllerInterface requesterAccessController
) public onlyOwner {
AccessControllerInterface oldController = s_requesterAccessController;
if (requesterAccessController != oldController) {
s_requesterAccessController = AccessControllerInterface(requesterAccessController);
emit RequesterAccessControllerSet(oldController, requesterAccessController);
}
}
/// @notice immediately requests a new round.
/// @dev access control provided by requesterAccessController.
/// @return aggregatorRoundId round id of the next round. Note: The report for this round may have been
/// transmitted (but not yet mined) *before* requestNewRound() was even called. There is *no*
/// guarantee of causality between the request and the report at aggregatorRoundId.
function requestNewRound() external returns (uint80) {
if (msg.sender != owner() && !s_requesterAccessController.hasAccess(msg.sender, msg.data)) {
revert OnlyOwnerAndRequesterCanCall();
}
uint40 latestEpochAndRound = s_hotVars.latestEpochAndRound;
uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
emit RoundRequested(msg.sender, s_latestConfigDigest, uint32(latestEpochAndRound >> 8), uint8(latestEpochAndRound));
return latestAggregatorRoundId + 1;
}
// ================================================================
// │ Secondary Proxy │
// ================================================================
// Used to relieve stack pressure in transmit.
struct Report {
int192 juelsPerFeeCoin; // ───────╮ Exchange rate between feeCoin (e.g. ETH on Ethereum) and LINK, denominated in juels.
uint32 observationsTimestamp; // ─╯ Timestamp when the observations were made offchain.
bytes observers; // i-th element is the index of the ith observer.
int192[] observations; // i-th element is the ith observation.
}
// Transmission records the median answer from the transmit transaction at
// time timestamp.
struct Transmission {
int192 answer; // ───────────────╮ 192 bits ought to be enough for anyone.
uint32 observationsTimestamp; // │ When were observations made offchain.
uint32 recordedTimestamp; // ────╯ When was report received onchain.
}
/// @notice indicates that a new report arrived from the secondary feed and the round id was updated.
/// @param secondaryRoundId the new secondary round id.
event SecondaryRoundIdUpdated(uint32 indexed secondaryRoundId);
/// @notice indicates that a new report arrived from the primary feed and the report had already been stored .
/// @param primaryRoundId the new primary round id (if we're at the next block since the report it should be the same).
event PrimaryFeedUnlocked(uint32 indexed primaryRoundId);
/// @notice emitted when a new cutoff time is set.
/// @param cutoffTime the new defined cutoff time.
event CutoffTimeSet(uint32 cutoffTime);
/// @notice revert when the loop reaches the max sync iterations amount.
error MaxSyncIterationsReached();
/// @notice mapping containing the Transmission records of each round id.
mapping(uint32 aggregatorRoundId => Transmission transmission) internal s_transmissions;
/// @notice secondary proxy address, used to detect who's calling the contract methods.
address internal immutable i_secondaryProxy;
/// @notice cutoff time defines the time window in which a secondary report is valid.
uint32 internal s_cutoffTime;
/// @notice max iterations the secondary proxy will be able to loop to sync with the primary rounds.
uint32 internal immutable i_maxSyncIterations;
/// @notice sets the max time cutoff.
/// @param _cutoffTime new max cutoff timestamp.
function setCutoffTime(
uint32 _cutoffTime
) external onlyOwner {
s_cutoffTime = _cutoffTime;
emit CutoffTimeSet(_cutoffTime);
}
/// @notice check if a report has already been transmitted.
/// @param report the report to check.
/// @return exist whether the report exist or not.
/// @return roundId the round id where the report was found.
function _doesReportExist(
Report memory report
) internal view returns (bool exist, uint32 roundId) {
// Get the latest round id.
uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
for (uint32 round_ = latestAggregatorRoundId; round_ > 0; --round_) {
// In case the loop reaches the max iterations revert it, the
// function is not able to check if the report exists or not
if (latestAggregatorRoundId - round_ == i_maxSyncIterations) {
revert MaxSyncIterationsReached();
}
Transmission memory transmission = s_transmissions[round_];
if (transmission.observationsTimestamp < report.observationsTimestamp) {
return (false, 0);
}
if (
transmission.observationsTimestamp == report.observationsTimestamp
&& transmission.answer == report.observations[report.observations.length / 2]
) {
return (true, round_);
}
}
return (false, 0);
}
/// @notice sync data with the primary rounds, return the freshest valid round id.
/// @return roundId synced round id with the primary feed.
function _getSyncPrimaryRound() internal view returns (uint32 roundId) {
// Get the latest round id and the max iterations.
uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
// Decreasing loop from the latest primary round id.
for (uint32 round_ = latestAggregatorRoundId; round_ > 0; --round_) {
// In case the loop reached the maxIterations, break it.
if (latestAggregatorRoundId - round_ == i_maxSyncIterations) {
break;
}
// Check if this round does not accomplish the cutoff time condition.
if (s_transmissions[round_].recordedTimestamp + s_cutoffTime < block.timestamp) {
return round_;
}
}
// If the loop couldn't find a match, return the latest secondary round id.
return s_hotVars.latestSecondaryRoundId;
}
/// @notice aggregator round in which the latest report was conceded depending on the caller.
/// @return roundId the latest valid round id.
function _getLatestRound() internal view returns (uint32) {
// Get the latest round ids.
uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
uint32 latestSecondaryRoundId = s_hotVars.latestSecondaryRoundId;
bool isLatestSecondary = s_hotVars.isLatestSecondary;
// Check if the message sender is the secondary proxy.
if (msg.sender == i_secondaryProxy) {
// In case the latest secondary round does not accomplish the cutoff time condition,
// get the round id syncing with the primary rounds.
if (s_transmissions[latestSecondaryRoundId].recordedTimestamp + s_cutoffTime < block.timestamp) {
return _getSyncPrimaryRound();
}
// In case the latest secondary round accomplish the cutoff time condition, return it.
return latestSecondaryRoundId;
}
// In case the report was sent by the secondary proxy.
if (latestAggregatorRoundId == latestSecondaryRoundId) {
// In case the transmission was sent in this same block only by the secondary proxy, return the previous round id.
if (isLatestSecondary && s_transmissions[latestAggregatorRoundId].recordedTimestamp == block.timestamp) {
return latestAggregatorRoundId - 1;
}
}
return latestAggregatorRoundId;
}
// ================================================================
// │ Transmission │
// ================================================================
/// @notice indicates that a new report was transmitted.
/// @param aggregatorRoundId the round to which this report was assigned.
/// @param answer median of the observations attached to this report.
/// @param transmitter address from which the report was transmitted.
/// @param observationsTimestamp when were observations made offchain.
/// @param observations observations transmitted with this report.
/// @param observers i-th element is the oracle id of the oracle that made the i-th observation.
/// @param juelsPerFeeCoin exchange rate between feeCoin (e.g. ETH on Ethereum) and LINK, denominated in juels.
/// @param configDigest configDigest of transmission.
/// @param epochAndRound least-significant byte is the OCR protocol round number, the other bytes give the OCR protocol epoch number.
event NewTransmission(
uint32 indexed aggregatorRoundId,
int192 answer,
address transmitter,
uint32 observationsTimestamp,
int192[] observations,
bytes observers,
int192 juelsPerFeeCoin,
bytes32 configDigest,
uint40 epochAndRound
);
error CalldataLengthMismatch();
error StaleReport();
error UnauthorizedTransmitter();
error ConfigDigestMismatch();
error WrongNumberOfSignatures();
error SignaturesOutOfRegistration();
error SignatureError();
error DuplicateSigner();
error OnlyCallableByEOA();
error ReportLengthMismatch();
error NumObservationsOutOfBounds();
error TooFewValuesToTrustMedian();
error MedianIsOutOfMinMaxRange();
/// @notice the constant-length components of the msg.data sent to transmit.
// See the "If we wanted to call sam" example on for example reasoning
// https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html
uint256 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = 4 // Function selector.
+ 32 * 3 // 3 words containing reportContext.
+ 32 // Word containing start location of abiencoded report value.
+ 32 // Word containing start location of abiencoded rs value.
+ 32 // Word containing start location of abiencoded ss value.
+ 32 // RawVs value.
+ 32 // Word containing length of report.
+ 32 // Word containing length rs.
+ 32 // Word containing length of ss.
+ 0; // Placeholder.
/// @notice decodes a serialized report into a Report struct.
/// @param rawReport serialized report in raw format.
/// @return report the decoded report in Report struct format.
function _decodeReport(
bytes memory rawReport
) internal pure returns (Report memory) {
(uint32 observationsTimestamp, bytes32 rawObservers, int192[] memory observations, int192 juelsPerFeeCoin) =
abi.decode(rawReport, (uint32, bytes32, int192[], int192));
_requireExpectedReportLength(rawReport, observations);
uint256 numObservations = observations.length;
bytes memory observers = abi.encodePacked(rawObservers);
assembly {
// We truncate observers from length 32 to the number of observations.
mstore(observers, numObservations)
}
return Report({
observationsTimestamp: observationsTimestamp,
observers: observers,
observations: observations,
juelsPerFeeCoin: juelsPerFeeCoin
});
}
/// @notice make sure the calldata length matches the inputs. Otherwise, the
/// transmitter could append an arbitrarily long (up to gas-block limit)
/// string of 0 bytes, which we would reimburse at a rate of 16 gas/byte, but
/// which would only cost the transmitter 4 gas/byte.
/// @param reportLength the length of the serialized report.
/// @param rsLength the length of the rs signatures.
/// @param ssLength the length of the ss signatures.
function _requireExpectedMsgDataLength(uint256 reportLength, uint256 rsLength, uint256 ssLength) private pure {
// Calldata will never be big enough to make this overflow.
uint256 expected = TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT + reportLength // One byte per entry in report.
+ rsLength * 32 // 32 bytes per entry in rs.
+ ssLength * 32 // 32 bytes per entry in ss.
+ 0; // Placeholder.
if (msg.data.length != expected) {
revert CalldataLengthMismatch();
}
}
/// @inheritdoc OCR2Abstract
function transmit(
// reportContext consists of:
// reportContext[0]: ConfigDigest.
// reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round.
// reportContext[2]: ExtraHash.
bytes32[3] calldata reportContext,
bytes calldata report,
// ECDSA signatures.
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 rawVs
) external override {
// Call the internal transmit function without the isSecondary flag.
_transmit(reportContext, report, rs, ss, rawVs, false);
}
/// @notice secondary proxy transmit entrypoint, call the internal transmit function with the isSecondary flag.
/// @param reportContext serialized report context containing configDigest, epoch, round and extraHash.
/// @param report serialized report, which the signatures are signing.
/// @param rs i-th element is the R components of the i-th signature on report. Must have at most maxNumOracles entries.
/// @param ss i-th element is the S components of the i-th signature on report. Must have at most maxNumOracles entries.
/// @param rawVs i-th element is the the V component of the i-th signature.
function transmitSecondary(
// reportContext consists of:
// reportContext[0]: ConfigDigest.
// reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round.
// reportContext[2]: ExtraHash.
bytes32[3] calldata reportContext,
bytes calldata report,
// ECDSA signatures.
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 rawVs
) external {
_transmit(reportContext, report, rs, ss, rawVs, true);
}
/// @notice internal transmit function, is called to post a new report to the contract.
/// @param reportContext serialized report context containing configDigest, epoch, round and extraHash.
/// @param report serialized report, which the signatures are signing.
/// @param rs i-th element is the R components of the i-th signature on report. Must have at most maxNumOracles entries.
/// @param ss i-th element is the S components of the i-th signature on report. Must have at most maxNumOracles entries.
/// @param rawVs i-th element is the the V component of the i-th signature.
/// @param isSecondary whether the transmission was sent by the secondary proxy or not.
function _transmit(
// reportContext consists of:
// reportContext[0]: ConfigDigest.
// reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round.
// reportContext[2]: ExtraHash.
bytes32[3] calldata reportContext,
bytes calldata report,
// ECDSA signatures.
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 rawVs,
bool isSecondary
) internal {
// NOTE: If the arguments to this function are changed, _requireExpectedMsgDataLength and/or
// TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly.
uint256 initialGas = gasleft(); // This line must come first.
// Validate the report data.
_validateReport(reportContext, report.length, rs.length, ss.length);
Report memory report_ = _decodeReport(report); // Decode the report.
HotVars memory hotVars = s_hotVars; // Load hotVars into memory.
if (isSecondary) {
(bool exist, uint32 roundId) = _doesReportExist(report_);
// In case the report exists, copy the round id and pay the transmitter.
if (exist) {
// In case the round has already been processed by the secondary feed.
if (hotVars.latestSecondaryRoundId >= roundId) {
revert StaleReport();
}
s_hotVars.latestSecondaryRoundId = roundId;
emit SecondaryRoundIdUpdated(roundId);
_payTransmitter(hotVars, report_.juelsPerFeeCoin, uint32(initialGas));
return;
}
}
// Report epoch and round.
uint40 epochAndRound = uint40(uint256(reportContext[1]));
// Only skip the report transmission in case the epochAndRound is equal to the latestEpochAndRound
// and the latest sender was the secondary feed.
if (epochAndRound != hotVars.latestEpochAndRound || !hotVars.isLatestSecondary) {
// In case the epochAndRound is lower or equal than the latestEpochAndRound, it's a stale report
// because it's older or has already been transmitted.
if (epochAndRound <= hotVars.latestEpochAndRound) {
revert StaleReport();
}
// Verify signatures attached to report.
_verifySignatures(reportContext, report, rs, ss, rawVs);
_report(hotVars, reportContext[0], epochAndRound, report_, isSecondary);
} else {
// If the report is the same and the latest sender was the secondary feed,
// we're effectively unlocking the primary feed with this
emit PrimaryFeedUnlocked(s_hotVars.latestAggregatorRoundId);
}
// Store if the latest report was secondary or not.
s_hotVars.isLatestSecondary = isSecondary;
_payTransmitter(hotVars, report_.juelsPerFeeCoin, uint32(initialGas));
}
/// @notice helper function to validate the report data.
/// @param reportContext serialized report context containing configDigest, epoch, round and extraHash.
/// @param reportLength the length of the serialized report.
/// @param rsLength the length of the rs signatures.
/// @param ssLength the length of the ss signatures.
function _validateReport(
bytes32[3] calldata reportContext,
uint256 reportLength,
uint256 rsLength,
uint256 ssLength
) internal view {
if (!s_transmitters[msg.sender].active) {
revert UnauthorizedTransmitter();
}
if (s_latestConfigDigest != reportContext[0]) {
revert ConfigDigestMismatch();
}
_requireExpectedMsgDataLength(reportLength, rsLength, ssLength);
if (rsLength != s_hotVars.f + 1) {
revert WrongNumberOfSignatures();
}
if (rsLength != ssLength) {
revert SignaturesOutOfRegistration();
}
}
/// @notice helper function to verify the report signatures.
/// @param reportContext serialized report context containing configDigest, epoch, round and extraHash.
/// @param report serialized report, which the signatures are signing.
/// @param rs i-th element is the R components of the i-th signature on report. Must have at most maxNumOracles entries.
/// @param ss i-th element is the S components of the i-th signature on report. Must have at most maxNumOracles entries.
/// @param rawVs i-th element is the the V component of the i-th signature.
function _verifySignatures(
bytes32[3] calldata reportContext,
bytes calldata report,
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 rawVs
) internal view {
bytes32 h = keccak256(abi.encode(keccak256(report), reportContext));
// i-th byte counts number of sigs made by i-th signer.
uint256 signedCount = 0;
Signer memory signer;
for (uint256 i = 0; i < rs.length; ++i) {
address signerAddress = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]);
signer = s_signers[signerAddress];
if (!signer.active) {
revert SignatureError();
}
unchecked {
signedCount += 1 << (8 * signer.index);
}
}
// The first byte of the mask can be 0, because we only ever have 31 oracles.
if (signedCount & 0x0001010101010101010101010101010101010101010101010101010101010101 != signedCount) {
revert DuplicateSigner();
}
}
/// @notice details about the most recent report.
/// @return configDigest domain separation tag for the latest report.
/// @return epoch epoch in which the latest report was generated.
/// @return round OCR round in which the latest report was generated.
/// @return latestAnswer_ median value from latest report.
/// @return latestTimestamp_ when the latest report was transmitted.
function latestTransmissionDetails()
external
view
returns (bytes32 configDigest, uint32 epoch, uint8 round, int192 latestAnswer_, uint64 latestTimestamp_)
{
// solhint-disable-next-line avoid-tx-origin
if (msg.sender != tx.origin) revert OnlyCallableByEOA();
return (
s_latestConfigDigest,
uint32(s_hotVars.latestEpochAndRound >> 8),
uint8(s_hotVars.latestEpochAndRound),
s_transmissions[s_hotVars.latestAggregatorRoundId].answer,
s_transmissions[s_hotVars.latestAggregatorRoundId].recordedTimestamp
);
}
/// @inheritdoc OCR2Abstract
function latestConfigDigestAndEpoch()
external
view
virtual
override
returns (bool scanLogs, bytes32 configDigest, uint32 epoch)
{
return (false, s_latestConfigDigest, uint32(s_hotVars.latestEpochAndRound >> 8));
}
/// @notice evaluate the serialized report length and compare it with the expected length.
/// @param report serialized report, which the signatures are signing.
/// @param observations decoded observations from the report.
function _requireExpectedReportLength(bytes memory report, int192[] memory observations) private pure {
uint256 expected = 32 // ObservationsTimestamp.
+ 32 // RawObservers.
+ 32 // Observations offset.
+ 32 // JuelsPerFeeCoin.
+ 32 // Observations length.
+ 32 * observations.length // Observations payload.
+ 0;
if (report.length != expected) revert ReportLengthMismatch();
}
/// @notice report a new transmission and emit the necessary events.
/// @param hotVars most common fields used in the hot path.
/// @param configDigest digested configuration.
/// @param epochAndRound report epoch and round.
/// @param report decoded report in Report struct format.
/// @param isSecondary whether the report was sent by the secondary proxy or not.
function _report(
HotVars memory hotVars,
bytes32 configDigest,
uint40 epochAndRound,
Report memory report,
bool isSecondary
) internal {
if (report.observations.length > MAX_NUM_ORACLES) revert NumObservationsOutOfBounds();
// Offchain logic ensures that a quorum of oracles is operating on a matching set of at least
// 2f+1 observations. By assumption, up to f of those can be faulty, which includes being
// malformed. Conversely, more than f observations have to be well-formed and sent on chain.
if (report.observations.length <= hotVars.f) revert TooFewValuesToTrustMedian();
hotVars.latestEpochAndRound = epochAndRound;
// Get median, validate its range, store it in new aggregator round.
int192 median = report.observations[report.observations.length / 2];
if (i_minAnswer > median || median > i_maxAnswer) revert MedianIsOutOfMinMaxRange();
hotVars.latestAggregatorRoundId++;
s_transmissions[hotVars.latestAggregatorRoundId] = Transmission({
answer: median,
observationsTimestamp: report.observationsTimestamp,
recordedTimestamp: uint32(block.timestamp)
});
// In case the sender is the secondary proxy, update the latest secondary round id.
if (isSecondary) {
hotVars.latestSecondaryRoundId = hotVars.latestAggregatorRoundId;
emit SecondaryRoundIdUpdated(hotVars.latestSecondaryRoundId);
}
// Persist updates to hotVars.
s_hotVars = hotVars;
emit NewTransmission(
hotVars.latestAggregatorRoundId,
median,
msg.sender,
report.observationsTimestamp,
report.observations,
report.observers,
report.juelsPerFeeCoin,
configDigest,
epochAndRound
);
// Emit these for backwards compatibility with offchain consumers
// that only support legacy events.
emit NewRound(
hotVars.latestAggregatorRoundId,
address(0x0), // Use zero address since we don't have anybody "starting" the round here.
report.observationsTimestamp
);
emit AnswerUpdated(median, hotVars.latestAggregatorRoundId, block.timestamp);
_validateAnswer(hotVars.latestAggregatorRoundId, median);
}
// ================================================================
// │ v2 AggregatorInterface │
// ================================================================
/// @notice median from the most recent report.
/// @return answer the latest answer.
function latestAnswer() public view virtual override returns (int256) {
return s_transmissions[_getLatestRound()].answer;
}
/// @notice timestamp of block in which last report was transmitted.
/// @return recordedTimestamp the latest recorded timestamp.
function latestTimestamp() public view virtual override returns (uint256) {
return s_transmissions[_getLatestRound()].recordedTimestamp;
}
/// @notice Aggregator round (NOT OCR round) in which last report was transmitted.
/// @return roundId the latest round id.
function latestRound() public view virtual override returns (uint256) {
return _getLatestRound();
}
/// @notice median of report from given aggregator round (NOT OCR round).
/// @param roundId the aggregator round of the target report.
/// @return answer the answer of the round id.
function getAnswer(
uint256 roundId
) public view virtual override returns (int256) {
if (roundId > _getLatestRound()) return 0;
return s_transmissions[uint32(roundId)].answer;
}
/// @notice timestamp of block in which report from given aggregator round was transmitted.
/// @param roundId aggregator round (NOT OCR round) of target report.
/// @return recordedTimestamp the recorded timestamp of the round id.
function getTimestamp(
uint256 roundId
) public view virtual override returns (uint256) {
if (roundId > _getLatestRound()) return 0;
return s_transmissions[uint32(roundId)].recordedTimestamp;
}
// ================================================================
// │ v3 AggregatorInterface │
// ================================================================
error RoundNotFound();
/// @notice amount of decimals.
uint8 private immutable i_decimals;
/// @notice aggregator contract version.
uint256 internal constant VERSION = 6;
/// @notice human readable description.
string internal s_description;
/// @notice get the amount of decimals.
/// @return i_decimals amount of decimals.
function decimals() public view virtual override returns (uint8) {
return i_decimals;
}
/// @notice get the contract version.
/// @return VERSION the contract version.
function version() public view virtual override returns (uint256) {
return VERSION;
}
/// @notice human-readable description of observable this contract is reporting on.
/// @return s_description the contract description.
function description() public view virtual override returns (string memory) {
return s_description;
}
/// @notice details for the given aggregator round.
/// @param roundId target aggregator round, must fit in uint32.
/// @return roundId_ roundId.
/// @return answer median of report from given roundId.
/// @return startedAt timestamp of when observations were made offchain.
/// @return updatedAt timestamp of block in which report from given roundId was transmitted.
/// @return answeredInRound roundId.
function getRoundData(
uint80 roundId
)
public
view
virtual
override
returns (uint80 roundId_, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
{
if (roundId > _getLatestRound()) return (0, 0, 0, 0, 0);
Transmission memory transmission = s_transmissions[uint32(roundId)];
return (roundId, transmission.answer, transmission.observationsTimestamp, transmission.recordedTimestamp, roundId);
}
/// @notice aggregator details for the most recently transmitted report.
/// @return roundId aggregator round of latest report (NOT OCR round).
/// @return answer median of latest report.
/// @return startedAt timestamp of when observations were made offchain.
/// @return updatedAt timestamp of block containing latest report.
/// @return answeredInRound aggregator round of latest report.
function latestRoundData()
public
view
virtual
override
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
{
uint80 latestRoundId = _getLatestRound();
Transmission memory transmission = s_transmissions[uint32(latestRoundId)];
return (
latestRoundId,
transmission.answer,
transmission.observationsTimestamp,
transmission.recordedTimestamp,
latestRoundId
);
}
// ================================================================
// │ Configurable LINK Token │
// ================================================================
/// @notice emitted when the LINK token contract is set.
/// @param oldLinkToken the address of the old LINK token contract.
/// @param newLinkToken the address of the new LINK token contract.
event LinkTokenSet(LinkTokenInterface indexed oldLinkToken, LinkTokenInterface indexed newLinkToken);
error TransferRemainingFundsFailed();
/// @notice we assume that the token contract is correct. This contract is not written
/// to handle misbehaving ERC20 tokens!
LinkTokenInterface internal s_linkToken;
/// @notice sets the LINK token contract used for paying oracles.
/// @dev this function will return early (without an error) without changing any state
/// if linkToken equals getLinkToken().
/// @dev this will trigger a payout so that a malicious owner cannot take from oracles
/// what is already owed to them.
/// @dev we assume that the token contract is correct. This contract is not written
/// to handle misbehaving ERC20 tokens!
/// @param linkToken the address of the LINK token contract.
/// @param recipient remaining funds from the previous token contract are transferred
/// here.
function setLinkToken(LinkTokenInterface linkToken, address recipient) external onlyOwner {
LinkTokenInterface oldLinkToken = s_linkToken;
if (linkToken == oldLinkToken) {
// No change, nothing to be done.
return;
}
// Call balanceOf as a sanity check on whether we're talking to a token
// contract.
linkToken.balanceOf(address(this));
// We break CEI here, but that's okay because we're dealing with a correct
// token contract (by assumption).
_payOracles();
uint256 remainingBalance = oldLinkToken.balanceOf(address(this));
if (!oldLinkToken.transfer(recipient, remainingBalance)) revert TransferRemainingFundsFailed();
// solhint-disable-next-line reentrancy
s_linkToken = linkToken;
emit LinkTokenSet(oldLinkToken, linkToken);
}
/// @notice gets the LINK token contract used for paying oracles.
/// @return linkToken the address of the LINK token contract.
function getLinkToken() external view returns (LinkTokenInterface linkToken) {
return s_linkToken;
}
// ================================================================
// │ BillingAccessController Management │
// ================================================================
/// @notice emitted when a new access-control contract is set.
/// @param old the address prior to the current setting.
/// @param current the address of the new access-control contract.
event BillingAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);
/// @notice controls who can change billing parameters. A billingAdmin is not able to
/// affect any OCR protocol settings and therefore cannot tamper with the
/// liveness or integrity of a data feed. However, a billingAdmin can set
/// faulty billing parameters causing oracles to be underpaid, or causing them
/// to be paid so much that further calls to setConfig, setBilling,
/// setLinkToken will always fail due to the contract being underfunded.
AccessControllerInterface internal s_billingAccessController;
/// @notice internal function to set a new billingAccessController.
/// @param billingAccessController new billingAccessController contract address.
function _setBillingAccessController(
AccessControllerInterface billingAccessController
) internal {
AccessControllerInterface oldController = s_billingAccessController;
if (billingAccessController != oldController) {
s_billingAccessController = billingAccessController;
emit BillingAccessControllerSet(oldController, billingAccessController);
}
}
/// @notice sets billingAccessController.
/// @param _billingAccessController new billingAccessController contract address.
function setBillingAccessController(
AccessControllerInterface _billingAccessController
) external onlyOwner {
_setBillingAccessController(_billingAccessController);
}
/// @notice gets billingAccessController.
/// @return s_billingAccessController address of billingAccessController contract.
function getBillingAccessController() external view returns (AccessControllerInterface) {
return s_billingAccessController;
}
// ================================================================
// │ Billing Configuration │
// ================================================================
/// @notice emitted when billing parameters are set.
/// @param maximumGasPriceGwei highest gas price for which transmitter will be compensated.
/// @param reasonableGasPriceGwei transmitter will receive reward for gas prices under this value.
/// @param observationPaymentGjuels reward to oracle for contributing an observation to a successfully transmitted report.
/// @param transmissionPaymentGjuels reward to transmitter of a successful report.
/// @param accountingGas gas overhead incurred by accounting logic.
event BillingSet(
uint32 maximumGasPriceGwei,
uint32 reasonableGasPriceGwei,
uint32 observationPaymentGjuels,
uint32 transmissionPaymentGjuels,
uint24 accountingGas
);
error OnlyOwnerAndBillingAdminCanCall();
/// @notice sets billing parameters.
/// @dev access control provided by billingAccessController.
/// @param maximumGasPriceGwei highest gas price for which transmitter will be compensated.
/// @param reasonableGasPriceGwei transmitter will receive reward for gas prices under this value.
/// @param observationPaymentGjuels reward to oracle for contributing an observation to a successfully transmitted report.
/// @param transmissionPaymentGjuels reward to transmitter of a successful report.
/// @param accountingGas gas overhead incurred by accounting logic.
function setBilling(
uint32 maximumGasPriceGwei,
uint32 reasonableGasPriceGwei,
uint32 observationPaymentGjuels,
uint32 transmissionPaymentGjuels,
uint24 accountingGas
) external {
if (!(msg.sender == owner() || s_billingAccessController.hasAccess(msg.sender, msg.data))) {
revert OnlyOwnerAndBillingAdminCanCall();
}
_payOracles();
s_hotVars.maximumGasPriceGwei = maximumGasPriceGwei;
s_hotVars.reasonableGasPriceGwei = reasonableGasPriceGwei;
s_hotVars.observationPaymentGjuels = observationPaymentGjuels;
s_hotVars.transmissionPaymentGjuels = transmissionPaymentGjuels;
s_accountingGas = accountingGas;
emit BillingSet(
maximumGasPriceGwei, reasonableGasPriceGwei, observationPaymentGjuels, transmissionPaymentGjuels, accountingGas
);
}
/// @notice gets billing parameters.
/// @param maximumGasPriceGwei highest gas price for which transmitter will be compensated.
/// @param reasonableGasPriceGwei transmitter will receive reward for gas prices under this value.
/// @param observationPaymentGjuels reward to oracle for contributing an observation to a successfully transmitted report.
/// @param transmissionPaymentGjuels reward to transmitter of a successful report.
/// @param accountingGas gas overhead of the accounting logic.
function getBilling()
external
view
returns (
uint32 maximumGasPriceGwei,
uint32 reasonableGasPriceGwei,
uint32 observationPaymentGjuels,
uint32 transmissionPaymentGjuels,
uint24 accountingGas
)
{
return (
s_hotVars.maximumGasPriceGwei,
s_hotVars.reasonableGasPriceGwei,
s_hotVars.observationPaymentGjuels,
s_hotVars.transmissionPaymentGjuels,
s_accountingGas
);
}
// ================================================================
// │ Payments and Withdrawals │
// ================================================================
/// @notice emitted when an oracle has been paid LINK.
/// @param transmitter address from which the oracle sends reports to the transmit method.
/// @param payee address to which the payment is sent.
/// @param amount amount of LINK sent.
/// @param linkToken address of the LINK token contract.
event OraclePaid(
address indexed transmitter, address indexed payee, uint256 amount, LinkTokenInterface indexed linkToken
);
error OnlyPayeeCanWithdraw();
error InsufficientFunds();
error InsufficientBalance();
/// @notice withdraws an oracle's payment from the contract.
/// @param transmitter the transmitter address of the oracle.
/// @dev must be called by oracle's payee address.
function withdrawPayment(
address transmitter
) external {
if (msg.sender != s_payees[transmitter]) revert OnlyPayeeCanWithdraw();
_payOracle(transmitter);
}
/// @notice query an oracle's payment amount, denominated in juels.
/// @param transmitterAddress the transmitter address of the oracle.
function owedPayment(
address transmitterAddress
) public view returns (uint256) {
Transmitter memory transmitter = s_transmitters[transmitterAddress];
if (!transmitter.active) return 0;
// Safe from overflow:
// s_hotVars.latestAggregatorRoundId - s_rewardFromAggregatorRoundId[transmitter.index] <= 2**32.
// s_hotVars.observationPaymentGjuels <= 2**32.
// 1 gwei <= 2**32.
// hence juelsAmount <= 2**96.
uint256 juelsAmount = uint256(s_hotVars.latestAggregatorRoundId - s_rewardFromAggregatorRoundId[transmitter.index])
* uint256(s_hotVars.observationPaymentGjuels) * (1 gwei);
juelsAmount += transmitter.paymentJuels;
return juelsAmount;
}
/// @notice pays out transmitter's oracle balance to the corresponding payee, and zeros it out.
/// @param transmitterAddress the transmitter address of the oracle.
function _payOracle(
address transmitterAddress
) internal {
Transmitter memory transmitter = s_transmitters[transmitterAddress];
if (!transmitter.active) return;
uint256 juelsAmount = owedPayment(transmitterAddress);
if (juelsAmount > 0) {
address payee = s_payees[transmitterAddress];
// Poses no re-entrancy issues, because LINK.transfer does not yield
// control flow.
if (!s_linkToken.transfer(payee, juelsAmount)) {
revert InsufficientFunds();
}
// solhint-disable-next-line reentrancy
s_rewardFromAggregatorRoundId[transmitter.index] = s_hotVars.latestAggregatorRoundId;
// solhint-disable-next-line reentrancy
s_transmitters[transmitterAddress].paymentJuels = 0;
emit OraclePaid(transmitterAddress, payee, juelsAmount, s_linkToken);
}
}
/// @notice pays out all transmitters oracles, and zeros out their balances.
/// It's much more gas-efficient to do this as a single operation, to avoid
/// hitting storage too much.
function _payOracles() internal {
unchecked {
LinkTokenInterface linkToken = s_linkToken;
uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
uint32[MAX_NUM_ORACLES] memory rewardFromAggregatorRoundId = s_rewardFromAggregatorRoundId;
address[] memory transmitters = s_transmittersList;
for (uint256 transmitteridx = 0; transmitteridx < transmitters.length; transmitteridx++) {
uint256 reimbursementAmountJuels = s_transmitters[transmitters[transmitteridx]].paymentJuels;
s_transmitters[transmitters[transmitteridx]].paymentJuels = 0;
uint256 obsCount = latestAggregatorRoundId - rewardFromAggregatorRoundId[transmitteridx];
uint256 juelsAmount =
obsCount * uint256(s_hotVars.observationPaymentGjuels) * (1 gwei) + reimbursementAmountJuels;
if (juelsAmount > 0) {
address payee = s_payees[transmitters[transmitteridx]];
// Poses no re-entrancy issues, because LINK.transfer does not yield
// control flow.
if (!linkToken.transfer(payee, juelsAmount)) {
revert InsufficientFunds();
}
rewardFromAggregatorRoundId[transmitteridx] = latestAggregatorRoundId;
emit OraclePaid(transmitters[transmitteridx], payee, juelsAmount, linkToken);
}
}
// "Zero" the accounting storage variables.
// solhint-disable-next-line reentrancy
s_rewardFromAggregatorRoundId = rewardFromAggregatorRoundId;
}
}
/// @notice withdraw any available funds left in the contract, up to amount, after accounting for the funds due to participants in past reports.
/// @dev access control provided by billingAccessController.
/// @param recipient address to send funds to.
/// @param amount maximum amount to withdraw, denominated in LINK-wei.
function withdrawFunds(address recipient, uint256 amount) external {
if (msg.sender != owner() && !s_billingAccessController.hasAccess(msg.sender, msg.data)) {
revert OnlyOwnerAndBillingAdminCanCall();
}
uint256 linkDue = _totalLinkDue();
uint256 linkBalance = s_linkToken.balanceOf(address(this));
if (linkBalance < linkDue) {
revert InsufficientBalance();
}
if (!s_linkToken.transfer(recipient, _min(linkBalance - linkDue, amount))) {
revert InsufficientFunds();
}
}
/// @notice total LINK due to participants in past reports (denominated in Juels).
/// @return linkDue total LINK due.
function _totalLinkDue() internal view returns (uint256 linkDue) {
// Argument for overflow safety: We do all computations in
// uint256s. The inputs to linkDue are:
// - the <= 31 observation rewards each of which has less than
// 64 bits (32 bits for observationPaymentGjuels, 32 bits
// for wei/gwei conversion). Hence 69 bits are sufficient for this part.
// - the <= 31 gas reimbursements, each of which consists of at most 96
// bits. Hence 101 bits are sufficient for this part.
// So we never need more than 102 bits.
address[] memory transmitters = s_transmittersList;
uint256 n = transmitters.length;
uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
uint32[MAX_NUM_ORACLES] memory rewardFromAggregatorRoundId = s_rewardFromAggregatorRoundId;
for (uint256 i = 0; i < n; ++i) {
linkDue += latestAggregatorRoundId - rewardFromAggregatorRoundId[i];
}
// Convert observationPaymentGjuels to uint256, or this overflows!
linkDue *= uint256(s_hotVars.observationPaymentGjuels) * (1 gwei);
for (uint256 i = 0; i < n; ++i) {
linkDue += uint256(s_transmitters[transmitters[i]].paymentJuels);
}
return linkDue;
}
/// @notice allows oracles to check that sufficient LINK balance is available.
/// @return availableBalance LINK available on this contract, after accounting for outstanding obligations, can become negative.
function linkAvailableForPayment() external view returns (int256 availableBalance) {
// There are at most one billion LINK, so this cast is safe.
int256 balance = int256(s_linkToken.balanceOf(address(this)));
// According to the argument in the definition of _totalLinkDue,
// _totalLinkDue is never greater than 2**102, so this cast is safe.
int256 due = int256(_totalLinkDue());
// Safe from overflow according to above sizes.
return int256(balance) - int256(due);
}
/// @notice number of observations oracle is due to be reimbursed for.
/// @param transmitterAddress address used by oracle for signing or transmitting reports.
/// @return observations difference between the latest oracle reimbursement round id and the latest hotVars round id.
function oracleObservationCount(
address transmitterAddress
) external view returns (uint32) {
Transmitter memory transmitter = s_transmitters[transmitterAddress];
if (!transmitter.active) return 0;
return s_hotVars.latestAggregatorRoundId - s_rewardFromAggregatorRoundId[transmitter.index];
}
// ================================================================
// │ Transmitter Payment │
// ================================================================
error LeftGasCannotExceedInitialGas();
/// @notice gas price at which the transmitter should be reimbursed, in gwei/gas.
/// @param txGasPriceGwei transaction gas price in ETH-gwei units.
/// @param reasonableGasPriceGwei reasonable gas price in ETH-gwei units.
/// @param maximumGasPriceGwei maximum gas price in ETH-gwei units.
/// @return gasPrice resulting gas price to reimburse.
function _reimbursementGasPriceGwei(
uint256 txGasPriceGwei,
uint256 reasonableGasPriceGwei,
uint256 maximumGasPriceGwei
) internal pure returns (uint256) {
// This happens on the path for transmissions. We'd rather pay out
// a wrong reward than risk a liveness failure due to a revert.
unchecked {
// Reward the transmitter for choosing an efficient gas price: if they manage
// to come in lower than considered reasonable, give them half the savings.
uint256 gasPriceGwei = txGasPriceGwei;
if (txGasPriceGwei < reasonableGasPriceGwei) {
// Give transmitter half the savings for coming in under the reasonable gas price.
gasPriceGwei += (reasonableGasPriceGwei - txGasPriceGwei) / 2;
}
// Don't reimburse a gas price higher than maximumGasPriceGwei.
return _min(gasPriceGwei, maximumGasPriceGwei);
}
}
/// @notice gas reimbursement due the transmitter, in wei.
/// @param initialGas initial remaining gas.
/// @param gasPriceGwei gas price in ETH-gwei units.
/// @param callDataGas calldata gas cost.
/// @param accountingGas overhead incurred by accounting logic.
/// @param leftGas actual remaining gas.
/// @return fullGasCostWei final calculated gas cost in wei.
function _transmitterGasCostWei(
uint256 initialGas,
uint256 gasPriceGwei,
uint256 callDataGas,
uint256 accountingGas,
uint256 leftGas
) internal pure returns (uint256) {
// This happens on the path for transmissions. We'd rather pay out
// a wrong reward than risk a liveness failure due to a revert.
unchecked {
if (initialGas < leftGas) revert LeftGasCannotExceedInitialGas();
uint256 usedGas = initialGas - leftGas // Observed gas usage.
+ callDataGas + accountingGas; // Estimated gas usage.
uint256 fullGasCostWei = usedGas * gasPriceGwei * (1 gwei);
return fullGasCostWei;
}
}
/// @notice internal function to pay the transmitter on the path for transmissions. Note: We'd rather pay out
/// a wrong reward than risk a liveness failure due to a revert.
/// @param hotVars most common fields used in the hot path.
/// @param juelsPerFeeCoin exchange rate between feeCoin (e.g. ETH on Ethereum) and LINK, denominated in juels.
/// @param initialGas initial remaining gas.
function _payTransmitter(HotVars memory hotVars, int192 juelsPerFeeCoin, uint32 initialGas) internal virtual {
unchecked {
// We can't deal with negative juelsPerFeeCoin, better to just not pay.
if (juelsPerFeeCoin < 0) {
return;
}
// Reimburse transmitter of the report for gas usage.
uint256 gasPriceGwei = _reimbursementGasPriceGwei(
tx.gasprice / (1 gwei), // Convert to ETH-gwei units.
hotVars.reasonableGasPriceGwei,
hotVars.maximumGasPriceGwei
);
// The following is only an upper bound, as it ignores the cheaper cost for
// 0 bytes. Safe from overflow, because calldata just isn't that long.
uint256 callDataGasCost = 16 * msg.data.length;
uint256 gasLeft = gasleft();
uint256 gasCostEthWei =
_transmitterGasCostWei(uint256(initialGas), gasPriceGwei, callDataGasCost, s_accountingGas, gasLeft);
// Even if we assume absurdly large values, this still does not overflow, with:
// - usedGas <= 1'000'000 gas <= 2**20 gas.
// - weiPerGas <= 1'000'000 gwei <= 2**50 wei.
// - hence gasCostEthWei <= 2**70.
// - juelsPerFeeCoin <= 2**96 (more than the entire supply).
// We still fit into 166 bits.
uint256 gasCostJuels = (gasCostEthWei * uint192(juelsPerFeeCoin)) / 1e18;
uint96 oldTransmitterPaymentJuels = s_transmitters[msg.sender].paymentJuels;
uint96 newTransmitterPaymentJuels = uint96(
uint256(oldTransmitterPaymentJuels) + gasCostJuels + uint256(hotVars.transmissionPaymentGjuels) * (1 gwei)
);
// Overflow *should* never happen, but if it does, let's not persist it.
if (newTransmitterPaymentJuels < oldTransmitterPaymentJuels) {
return;
}
s_transmitters[msg.sender].paymentJuels = newTransmitterPaymentJuels;
}
}
// ================================================================
// │ Payee Management │
// ================================================================
/// @notice emitted when a transfer of an oracle's payee address has been initiated.
/// @param transmitter address from which the oracle sends reports to the transmit method.
/// @param current the payee address for the oracle, prior to this setting.
/// @param proposed the proposed new payee address for the oracle.
event PayeeshipTransferRequested(address indexed transmitter, address indexed current, address indexed proposed);
/// @notice emitted when a transfer of an oracle's payee address has been completed.
/// @param transmitter address from which the oracle sends reports to the transmit method.
/// @param previous the previous payee address for the oracle.
/// @param current the payee address for the oracle, prior to this setting.
event PayeeshipTransferred(address indexed transmitter, address indexed previous, address indexed current);
error TransmittersSizeNotEqualPayeeSize();
error PayeeAlreadySet();
error OnlyCurrentPayeeCanUpdate();
error CannotTransferPayeeToSelf();
error OnlyProposedPayeesCanAccept();
/// @notice addresses at which oracles want to receive payments, by transmitter address.
mapping(address transmitterAddress => address paymentAddress) internal s_payees;
/// @notice payee addresses which must be approved by the owner.
mapping(address transmitterAddress => address paymentAddress) internal s_proposedPayees;
/// @notice sets the payees for transmitting addresses.
/// @dev cannot be used to change payee addresses, only to initially populate them.
/// @param transmitters addresses oracles use to transmit the reports.
/// @param payees addresses of payees corresponding to list of transmitters.
function setPayees(address[] calldata transmitters, address[] calldata payees) external onlyOwner {
if (transmitters.length != payees.length) revert TransmittersSizeNotEqualPayeeSize();
for (uint256 i = 0; i < transmitters.length; ++i) {
address transmitter = transmitters[i];
address payee = payees[i];
address currentPayee = s_payees[transmitter];
bool zeroedOut = currentPayee == address(0);
if (!zeroedOut && currentPayee != payee) revert PayeeAlreadySet();
s_payees[transmitter] = payee;
if (currentPayee != payee) {
emit PayeeshipTransferred(transmitter, currentPayee, payee);
}
}
}
/// @notice first step of payeeship transfer (safe transfer pattern).
/// @dev can only be called by payee addresses.
/// @param transmitter transmitter address of oracle whose payee is changing.
/// @param proposed new payee address.
function transferPayeeship(address transmitter, address proposed) external {
if (msg.sender != s_payees[transmitter]) {
revert OnlyCurrentPayeeCanUpdate();
}
if (msg.sender == proposed) revert CannotTransferPayeeToSelf();
address previousProposed = s_proposedPayees[transmitter];
s_proposedPayees[transmitter] = proposed;
if (previousProposed != proposed) {
emit PayeeshipTransferRequested(transmitter, msg.sender, proposed);
}
}
/// @notice second step of payeeship transfer (safe transfer pattern).
/// @dev can only be called by proposed new payee address.
/// @param transmitter transmitter address of oracle whose payee is changing.
function acceptPayeeship(
address transmitter
) external {
if (msg.sender != s_proposedPayees[transmitter]) revert OnlyProposedPayeesCanAccept();
address currentPayee = s_payees[transmitter];
s_payees[transmitter] = msg.sender;
s_proposedPayees[transmitter] = address(0);
emit PayeeshipTransferred(transmitter, currentPayee, msg.sender);
}
// ================================================================
// │ Helper Functions │
// ================================================================
/// @notice helper function to compare two numbers and return the smallest number.
/// @param a first number.
/// @param b second number.
/// @return result smallest number.
function _min(uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
if (a < b) return a;
return b;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IOwnable} from "../interfaces/IOwnable.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
address private s_owner;
address private s_pendingOwner;
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
// solhint-disable-next-line gas-custom-errors
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
/// @notice Allows an owner to begin transferring ownership to a new address.
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
/// @notice Allows an ownership transfer to be completed by the recipient.
function acceptOwnership() external override {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/// @notice Get the current owner
function owner() public view override returns (address) {
return s_owner;
}
/// @notice validate, transfer ownership, and emit relevant events
function _transferOwnership(address to) private {
// solhint-disable-next-line gas-custom-errors
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/// @notice validate access
function _validateOwnership() internal view {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_owner, "Only callable by owner");
}
/// @notice Reverts if called by anyone other than the contract owner.
modifier onlyOwner() {
_validateOwnership();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {SimpleWriteAccessController} from "./SimpleWriteAccessController.sol";
/// @title SimpleReadAccessController
/// @notice Gives access to:
/// - any externally owned account (note that off-chain actors can always read
/// any contract storage regardless of on-chain access control measures, so this
/// does not weaken the access control while improving usability)
/// - accounts explicitly added to an access list
/// @dev SimpleReadAccessController is not suitable for access controlling writes
/// since it grants any externally owned account access! See
/// SimpleWriteAccessController for that.
contract SimpleReadAccessController is SimpleWriteAccessController {
/// @notice Returns the access of an address
/// @param _user The address to query
function hasAccess(address _user, bytes memory _calldata) public view virtual override returns (bool) {
// solhint-disable-next-line avoid-tx-origin
return super.hasAccess(_user, _calldata) || _user == tx.origin;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwner} from "./ConfirmedOwner.sol";
import {AccessControllerInterface} from "../interfaces/AccessControllerInterface.sol";
/// @title SimpleWriteAccessController
/// @notice Gives access to accounts explicitly added to an access list by the controller's owner.
/// @dev does not make any special permissions for externally, see SimpleReadAccessController for that.
contract SimpleWriteAccessController is AccessControllerInterface, ConfirmedOwner {
bool public checkEnabled;
mapping(address => bool) internal s_accessList;
event AddedAccess(address user);
event RemovedAccess(address user);
event CheckAccessEnabled();
event CheckAccessDisabled();
constructor() ConfirmedOwner(msg.sender) {
checkEnabled = true;
}
/// @notice Returns the access of an address
/// @param _user The address to query
function hasAccess(address _user, bytes memory) public view virtual override returns (bool) {
return s_accessList[_user] || !checkEnabled;
}
/// @notice Adds an address to the access list
/// @param _user The address to add
function addAccess(address _user) external onlyOwner {
if (!s_accessList[_user]) {
s_accessList[_user] = true;
emit AddedAccess(_user);
}
}
/// @notice Removes an address from the access list
/// @param _user The address to remove
function removeAccess(address _user) external onlyOwner {
if (s_accessList[_user]) {
s_accessList[_user] = false;
emit RemovedAccess(_user);
}
}
/// @notice makes the access check enforced
function enableAccessCheck() external onlyOwner {
if (!checkEnabled) {
checkEnabled = true;
emit CheckAccessEnabled();
}
}
/// @notice makes the access check unenforced
function disableAccessCheck() external onlyOwner {
if (checkEnabled) {
checkEnabled = false;
emit CheckAccessDisabled();
}
}
/// @dev reverts if the caller does not have access
modifier checkAccess() {
// solhint-disable-next-line gas-custom-errors
require(hasAccess(msg.sender, msg.data), "No access");
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice This library contains various callWithExactGas functions. All of them are
/// safe from gas bomb attacks.
/// @dev There is code duplication in this library. This is done to not leave the assembly
/// the blocks.
library CallWithExactGas {
error NoContract();
error NoGasForCallExactCheck();
error NotEnoughGasForCall();
bytes4 internal constant NO_CONTRACT_SIG = 0x0c3b563c;
bytes4 internal constant NO_GAS_FOR_CALL_EXACT_CHECK_SIG = 0xafa32a2c;
bytes4 internal constant NOT_ENOUGH_GAS_FOR_CALL_SIG = 0x37c3be29;
/// @notice calls target address with exactly gasAmount gas and payload as calldata.
/// Accounts for gasForCallExactCheck gas that will be used by this function. Will revert
/// if the target is not a contact. Will revert when there is not enough gas to call the
/// target with gasAmount gas.
/// @dev Ignores the return data, which makes it immune to gas bomb attacks.
/// @return success whether the call succeeded
function _callWithExactGas(
bytes memory payload,
address target,
uint256 gasLimit,
uint16 gasForCallExactCheck
) internal returns (bool success) {
assembly {
// solidity calls check that a contract actually exists at the destination, so we do the same
// Note we do this check prior to measuring gas so gasForCallExactCheck (our "cushion")
// doesn't need to account for it.
if iszero(extcodesize(target)) {
mstore(0x0, NO_CONTRACT_SIG)
revert(0x0, 0x4)
}
let g := gas()
// Compute g -= gasForCallExactCheck and check for underflow
// The gas actually passed to the callee is _min(gasAmount, 63//64*gas available).
// We want to ensure that we revert if gasAmount > 63//64*gas available
// as we do not want to provide them with less, however that check itself costs
// gas. gasForCallExactCheck ensures we have at least enough gas to be able
// to revert if gasAmount > 63//64*gas available.
if lt(g, gasForCallExactCheck) {
mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG)
revert(0x0, 0x4)
}
g := sub(g, gasForCallExactCheck)
// if g - g//64 <= gasAmount, revert. We subtract g//64 because of EIP-150
if iszero(gt(sub(g, div(g, 64)), gasLimit)) {
mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG)
revert(0x0, 0x4)
}
// call and return whether we succeeded. ignore return data
// call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
}
return success;
}
/// @notice calls target address with exactly gasAmount gas and payload as calldata.
/// Account for gasForCallExactCheck gas that will be used by this function. Will revert
/// if the target is not a contact. Will revert when there is not enough gas to call the
/// target with gasAmount gas.
/// @dev Caps the return data length, which makes it immune to gas bomb attacks.
/// @dev Return data cap logic borrowed from
/// https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol.
/// @return success whether the call succeeded
/// @return retData the return data from the call, capped at maxReturnBytes bytes
/// @return gasUsed the gas used by the external call. Does not include the overhead of this function.
function _callWithExactGasSafeReturnData(
bytes memory payload,
address target,
uint256 gasLimit,
uint16 gasForCallExactCheck,
uint16 maxReturnBytes
) internal returns (bool success, bytes memory retData, uint256 gasUsed) {
// allocate retData memory ahead of time
retData = new bytes(maxReturnBytes);
assembly {
// solidity calls check that a contract actually exists at the destination, so we do the same
// Note we do this check prior to measuring gas so gasForCallExactCheck (our "cushion")
// doesn't need to account for it.
if iszero(extcodesize(target)) {
mstore(0x0, NO_CONTRACT_SIG)
revert(0x0, 0x4)
}
let g := gas()
// Compute g -= gasForCallExactCheck and check for underflow
// The gas actually passed to the callee is _min(gasAmount, 63//64*gas available).
// We want to ensure that we revert if gasAmount > 63//64*gas available
// as we do not want to provide them with less, however that check itself costs
// gas. gasForCallExactCheck ensures we have at least enough gas to be able
// to revert if gasAmount > 63//64*gas available.
if lt(g, gasForCallExactCheck) {
mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG)
revert(0x0, 0x4)
}
g := sub(g, gasForCallExactCheck)
// if g - g//64 <= gasAmount, revert. We subtract g//64 because of EIP-150
if iszero(gt(sub(g, div(g, 64)), gasLimit)) {
mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG)
revert(0x0, 0x4)
}
// We save the gas before the call so we can calculate how much gas the call used
let gasBeforeCall := gas()
// call and return whether we succeeded. ignore return data
// call(gas,addr,value,argsOffset,argsLength,retOffset,retLength)
success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
gasUsed := sub(gasBeforeCall, gas())
// limit our copy to maxReturnBytes bytes
let toCopy := returndatasize()
if gt(toCopy, maxReturnBytes) {
toCopy := maxReturnBytes
}
// Store the length of the copied bytes
mstore(retData, toCopy)
// copy the bytes from retData[0:_toCopy]
returndatacopy(add(retData, 0x20), 0x0, toCopy)
}
return (success, retData, gasUsed);
}
/// @notice Calls target address with exactly gasAmount gas and payload as calldata
/// or reverts if at least gasLimit gas is not available.
/// @dev Does not check if target is a contract. If it is not a contract, the low-level
/// call will still be made and it will succeed.
/// @dev Ignores the return data, which makes it immune to gas bomb attacks.
/// @return success whether the call succeeded
/// @return sufficientGas Whether there was enough gas to make the call
function _callWithExactGasEvenIfTargetIsNoContract(
bytes memory payload,
address target,
uint256 gasLimit,
uint16 gasForCallExactCheck
) internal returns (bool success, bool sufficientGas) {
assembly {
let g := gas()
// Compute g -= CALL_WITH_EXACT_GAS_CUSHION and check for underflow. We
// need the cushion since the logic following the above call to gas also
// costs gas which we cannot account for exactly. So cushion is a
// conservative upper bound for the cost of this logic.
if iszero(lt(g, gasForCallExactCheck)) {
g := sub(g, gasForCallExactCheck)
// If g - g//64 <= gasAmount, we don't have enough gas. We subtract g//64 because of EIP-150.
if gt(sub(g, div(g, 64)), gasLimit) {
// Call and ignore success/return data. Note that we did not check
// whether a contract actually exists at the target address.
success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
sufficientGas := true
}
}
}
return (success, sufficientGas);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AccessControllerInterface {
function hasAccess(address user, bytes calldata data) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorInterface {
function latestAnswer() external view returns (int256);
function latestTimestamp() external view returns (uint256);
function latestRound() external view returns (uint256);
function getAnswer(uint256 roundId) external view returns (int256);
function getTimestamp(uint256 roundId) external view returns (uint256);
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {AggregatorInterface} from "./AggregatorInterface.sol";
import {AggregatorV3Interface} from "./AggregatorV3Interface.sol";
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorValidatorInterface {
function validate(
uint256 previousRoundId,
int256 previousAnswer,
uint256 currentRoundId,
int256 currentAnswer
) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ITypeAndVersion {
function typeAndVersion() external pure returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface LinkTokenInterface {
function allowance(address owner, address spender) external view returns (uint256 remaining);
function approve(address spender, uint256 value) external returns (bool success);
function balanceOf(address owner) external view returns (uint256 balance);
function decimals() external view returns (uint8 decimalPlaces);
function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
function increaseApproval(address spender, uint256 subtractedValue) external;
function name() external view returns (string memory tokenName);
function symbol() external view returns (string memory tokenSymbol);
function totalSupply() external view returns (uint256 totalTokensIssued);
function transfer(address to, uint256 value) external returns (bool success);
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);
function transferFrom(address from, address to, uint256 value) external returns (bool success);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ITypeAndVersion} from "../interfaces/ITypeAndVersion.sol";
abstract contract OCR2Abstract is ITypeAndVersion {
uint256 internal constant MAX_NUM_ORACLES = 31;
uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00
uint256 private constant PREFIX = 0x0001 << (256 - 16); // 0x000100..00
/// @notice triggers a new run of the offchain reporting protocol
/// @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis
/// @param configDigest configDigest of this configuration
/// @param configCount ordinal number of this config setting among all config settings over the life of this contract
/// @param signers ith element is address ith oracle uses to sign a report
/// @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method
/// @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
/// @param onchainConfig serialized configuration used by the contract (and possibly oracles)
/// @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter
/// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
event ConfigSet(
uint32 previousConfigBlockNumber,
bytes32 configDigest,
uint64 configCount,
address[] signers,
address[] transmitters,
uint8 f,
bytes onchainConfig,
uint64 offchainConfigVersion,
bytes offchainConfig
);
/// @notice sets offchain reporting protocol configuration incl. participating oracles
/// @param signers addresses with which oracles sign the reports
/// @param transmitters addresses oracles use to transmit the reports
/// @param f number of faulty oracles the system can tolerate
/// @param onchainConfig serialized configuration used by the contract (and possibly oracles)
/// @param offchainConfigVersion version number for offchainEncoding schema
/// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
function setConfig(
address[] memory signers,
address[] memory transmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) external virtual;
/// @notice information about current offchain reporting protocol configuration
/// @return configCount ordinal number of current config, out of all configs applied to this contract so far
/// @return blockNumber block at which this config was set
/// @return configDigest domain-separation tag for current config (see _configDigestFromConfigData)
function latestConfigDetails()
external
view
virtual
returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest);
function _configDigestFromConfigData(
uint256 chainId,
address contractAddress,
uint64 configCount,
address[] memory signers,
address[] memory transmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) internal pure returns (bytes32) {
uint256 h = uint256(
keccak256(
abi.encode(
chainId,
contractAddress,
configCount,
signers,
transmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig
)
)
);
return bytes32((PREFIX & PREFIX_MASK) | (h & ~PREFIX_MASK));
}
/// @notice optionally emitted to indicate the latest configDigest and epoch for
/// which a report was successfully transmitted. Alternatively, the contract may
/// use latestConfigDigestAndEpoch with scanLogs set to false.
event Transmitted(bytes32 configDigest, uint32 epoch);
/// @notice optionally returns the latest configDigest and epoch for which a
/// report was successfully transmitted. Alternatively, the contract may return
/// scanLogs set to true and use Transmitted events to provide this information
/// to offchain watchers.
/// @return scanLogs indicates whether to rely on the configDigest and epoch
/// returned or whether to scan logs for the Transmitted event instead.
/// @return configDigest
/// @return epoch
function latestConfigDigestAndEpoch()
external
view
virtual
returns (bool scanLogs, bytes32 configDigest, uint32 epoch);
/// @notice transmit is called to post a new report to the contract
/// @param reportContext [0]: ConfigDigest, [1]: 27 byte padding, 4-byte epoch and 1-byte round, [2]: ExtraHash
/// @param report serialized report, which the signatures are signing.
/// @param rs ith element is the R components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries
/// @param ss ith element is the S components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries
/// @param rawVs ith element is the the V component of the ith signature
function transmit(
// NOTE: If these parameters are changed, expectedMsgDataLength and/or
// TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly
bytes32[3] calldata reportContext,
bytes calldata report,
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 rawVs // signatures
) external virtual;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {AccessControllerInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AccessControllerInterface.sol";
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";
import {DualAggregator} from "src/DualAggregator.sol";
contract DualAggregatorHelper is DualAggregator {
constructor(
LinkTokenInterface link,
int192 minAnswer_,
int192 maxAnswer_,
AccessControllerInterface billingAccessController,
AccessControllerInterface requesterAccessController,
uint8 decimals_,
string memory description_,
address secondaryProxy_,
uint32 cutoffTime_,
uint32 maxSyncIterations_
)
DualAggregator(
link,
minAnswer_,
maxAnswer_,
billingAccessController,
requesterAccessController,
decimals_,
description_,
secondaryProxy_,
cutoffTime_,
maxSyncIterations_
)
{}
function getSyncPrimaryRound() public view returns (uint32 roundId) {
return _getSyncPrimaryRound();
}
function configDigestFromConfigData(
uint256 chainId,
address contractAddress,
uint64 configCount,
address[] memory signers,
address[] memory transmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) public pure returns (bytes32) {
return _configDigestFromConfigData(
chainId,
contractAddress,
configCount,
signers,
transmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig
);
}
function totalLinkDue() public view returns (uint256 linkDue) {
return _totalLinkDue();
}
function getHotVars() public view returns (HotVars memory) {
return s_hotVars;
}
}{
"remappings": [
"@chainlink/=node_modules/@chainlink/contracts/",
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/"
],
"optimizer": {
"enabled": true,
"runs": 1000000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract LinkTokenInterface","name":"link","type":"address"},{"internalType":"int192","name":"minAnswer_","type":"int192"},{"internalType":"int192","name":"maxAnswer_","type":"int192"},{"internalType":"contract AccessControllerInterface","name":"billingAccessController","type":"address"},{"internalType":"contract AccessControllerInterface","name":"requesterAccessController","type":"address"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"string","name":"description_","type":"string"},{"internalType":"address","name":"secondaryProxy_","type":"address"},{"internalType":"uint32","name":"cutoffTime_","type":"uint32"},{"internalType":"uint32","name":"maxSyncIterations_","type":"uint32"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CalldataLengthMismatch","type":"error"},{"inputs":[],"name":"CannotTransferPayeeToSelf","type":"error"},{"inputs":[],"name":"ConfigDigestMismatch","type":"error"},{"inputs":[],"name":"DuplicateSigner","type":"error"},{"inputs":[],"name":"FMustBePositive","type":"error"},{"inputs":[],"name":"FaultyOracleFTooHigh","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InsufficientGas","type":"error"},{"inputs":[],"name":"InvalidOnChainConfig","type":"error"},{"inputs":[],"name":"LeftGasCannotExceedInitialGas","type":"error"},{"inputs":[],"name":"MaxSyncIterationsReached","type":"error"},{"inputs":[],"name":"MedianIsOutOfMinMaxRange","type":"error"},{"inputs":[],"name":"NumObservationsOutOfBounds","type":"error"},{"inputs":[],"name":"OnlyCallableByEOA","type":"error"},{"inputs":[],"name":"OnlyCurrentPayeeCanUpdate","type":"error"},{"inputs":[],"name":"OnlyOwnerAndBillingAdminCanCall","type":"error"},{"inputs":[],"name":"OnlyOwnerAndRequesterCanCall","type":"error"},{"inputs":[],"name":"OnlyPayeeCanWithdraw","type":"error"},{"inputs":[],"name":"OnlyProposedPayeesCanAccept","type":"error"},{"inputs":[],"name":"OracleLengthMismatch","type":"error"},{"inputs":[],"name":"PayeeAlreadySet","type":"error"},{"inputs":[],"name":"RepeatedSignerAddress","type":"error"},{"inputs":[],"name":"RepeatedTransmitterAddress","type":"error"},{"inputs":[],"name":"ReportLengthMismatch","type":"error"},{"inputs":[],"name":"RoundNotFound","type":"error"},{"inputs":[],"name":"SignatureError","type":"error"},{"inputs":[],"name":"SignaturesOutOfRegistration","type":"error"},{"inputs":[],"name":"StaleReport","type":"error"},{"inputs":[],"name":"TooFewValuesToTrustMedian","type":"error"},{"inputs":[],"name":"TooManyOracles","type":"error"},{"inputs":[],"name":"TransferRemainingFundsFailed","type":"error"},{"inputs":[],"name":"TransmittersSizeNotEqualPayeeSize","type":"error"},{"inputs":[],"name":"UnauthorizedTransmitter","type":"error"},{"inputs":[],"name":"WrongNumberOfSignatures","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"AddedAccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"int256","name":"current","type":"int256"},{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"updatedAt","type":"uint256"}],"name":"AnswerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract AccessControllerInterface","name":"old","type":"address"},{"indexed":false,"internalType":"contract AccessControllerInterface","name":"current","type":"address"}],"name":"BillingAccessControllerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"maximumGasPriceGwei","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"reasonableGasPriceGwei","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"observationPaymentGjuels","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"transmissionPaymentGjuels","type":"uint32"},{"indexed":false,"internalType":"uint24","name":"accountingGas","type":"uint24"}],"name":"BillingSet","type":"event"},{"anonymous":false,"inputs":[],"name":"CheckAccessDisabled","type":"event"},{"anonymous":false,"inputs":[],"name":"CheckAccessEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"previousConfigBlockNumber","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"configCount","type":"uint64"},{"indexed":false,"internalType":"address[]","name":"signers","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"uint8","name":"f","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"onchainConfig","type":"bytes"},{"indexed":false,"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"ConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"cutoffTime","type":"uint32"}],"name":"CutoffTimeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract LinkTokenInterface","name":"oldLinkToken","type":"address"},{"indexed":true,"internalType":"contract LinkTokenInterface","name":"newLinkToken","type":"address"}],"name":"LinkTokenSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"address","name":"startedBy","type":"address"},{"indexed":false,"internalType":"uint256","name":"startedAt","type":"uint256"}],"name":"NewRound","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"aggregatorRoundId","type":"uint32"},{"indexed":false,"internalType":"int192","name":"answer","type":"int192"},{"indexed":false,"internalType":"address","name":"transmitter","type":"address"},{"indexed":false,"internalType":"uint32","name":"observationsTimestamp","type":"uint32"},{"indexed":false,"internalType":"int192[]","name":"observations","type":"int192[]"},{"indexed":false,"internalType":"bytes","name":"observers","type":"bytes"},{"indexed":false,"internalType":"int192","name":"juelsPerFeeCoin","type":"int192"},{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint40","name":"epochAndRound","type":"uint40"}],"name":"NewTransmission","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"payee","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"contract LinkTokenInterface","name":"linkToken","type":"address"}],"name":"OraclePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"current","type":"address"},{"indexed":true,"internalType":"address","name":"proposed","type":"address"}],"name":"PayeeshipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"previous","type":"address"},{"indexed":true,"internalType":"address","name":"current","type":"address"}],"name":"PayeeshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"primaryRoundId","type":"uint32"}],"name":"PrimaryFeedUnlocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"RemovedAccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract AccessControllerInterface","name":"old","type":"address"},{"indexed":false,"internalType":"contract AccessControllerInterface","name":"current","type":"address"}],"name":"RequesterAccessControllerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"},{"indexed":false,"internalType":"uint8","name":"round","type":"uint8"}],"name":"RoundRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"secondaryRoundId","type":"uint32"}],"name":"SecondaryRoundIdUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"}],"name":"Transmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract AggregatorValidatorInterface","name":"previousValidator","type":"address"},{"indexed":false,"internalType":"uint32","name":"previousGasLimit","type":"uint32"},{"indexed":true,"internalType":"contract AggregatorValidatorInterface","name":"currentValidator","type":"address"},{"indexed":false,"internalType":"uint32","name":"currentGasLimit","type":"uint32"}],"name":"ValidatorConfigSet","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"}],"name":"acceptPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"addAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"description","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"disableAccessCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"enableAccessCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"getAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBilling","outputs":[{"internalType":"uint32","name":"maximumGasPriceGwei","type":"uint32"},{"internalType":"uint32","name":"reasonableGasPriceGwei","type":"uint32"},{"internalType":"uint32","name":"observationPaymentGjuels","type":"uint32"},{"internalType":"uint32","name":"transmissionPaymentGjuels","type":"uint32"},{"internalType":"uint24","name":"accountingGas","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBillingAccessController","outputs":[{"internalType":"contract AccessControllerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkToken","outputs":[{"internalType":"contract LinkTokenInterface","name":"linkToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRequesterAccessController","outputs":[{"internalType":"contract AccessControllerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint80","name":"roundId","type":"uint80"}],"name":"getRoundData","outputs":[{"internalType":"uint80","name":"roundId_","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"getTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTransmitters","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getValidatorConfig","outputs":[{"internalType":"contract AggregatorValidatorInterface","name":"validator","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"hasAccess","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestConfigDetails","outputs":[{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"bytes32","name":"configDigest","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestConfigDigestAndEpoch","outputs":[{"internalType":"bool","name":"scanLogs","type":"bool"},{"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"internalType":"uint32","name":"epoch","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestRoundData","outputs":[{"internalType":"uint80","name":"roundId","type":"uint80"},{"internalType":"int256","name":"answer","type":"int256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint80","name":"answeredInRound","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestTransmissionDetails","outputs":[{"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"internalType":"uint32","name":"epoch","type":"uint32"},{"internalType":"uint8","name":"round","type":"uint8"},{"internalType":"int192","name":"latestAnswer_","type":"int192"},{"internalType":"uint64","name":"latestTimestamp_","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"linkAvailableForPayment","outputs":[{"internalType":"int256","name":"availableBalance","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"transmitterAddress","type":"address"}],"name":"oracleObservationCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"transmitterAddress","type":"address"}],"name":"owedPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"removeAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestNewRound","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"maximumGasPriceGwei","type":"uint32"},{"internalType":"uint32","name":"reasonableGasPriceGwei","type":"uint32"},{"internalType":"uint32","name":"observationPaymentGjuels","type":"uint32"},{"internalType":"uint32","name":"transmissionPaymentGjuels","type":"uint32"},{"internalType":"uint24","name":"accountingGas","type":"uint24"}],"name":"setBilling","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract AccessControllerInterface","name":"_billingAccessController","type":"address"}],"name":"setBillingAccessController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bytes","name":"onchainConfig","type":"bytes"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_cutoffTime","type":"uint32"}],"name":"setCutoffTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract LinkTokenInterface","name":"linkToken","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"setLinkToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"address[]","name":"payees","type":"address[]"}],"name":"setPayees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract AccessControllerInterface","name":"requesterAccessController","type":"address"}],"name":"setRequesterAccessController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract AggregatorValidatorInterface","name":"newValidator","type":"address"},{"internalType":"uint32","name":"newGasLimit","type":"uint32"}],"name":"setValidatorConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[3]","name":"reportContext","type":"bytes32[3]"},{"internalType":"bytes","name":"report","type":"bytes"},{"internalType":"bytes32[]","name":"rs","type":"bytes32[]"},{"internalType":"bytes32[]","name":"ss","type":"bytes32[]"},{"internalType":"bytes32","name":"rawVs","type":"bytes32"}],"name":"transmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[3]","name":"reportContext","type":"bytes32[3]"},{"internalType":"bytes","name":"report","type":"bytes"},{"internalType":"bytes32[]","name":"rs","type":"bytes32[]"},{"internalType":"bytes32[]","name":"ss","type":"bytes32[]"},{"internalType":"bytes32","name":"rawVs","type":"bytes32"}],"name":"transmitSecondary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"}],"name":"withdrawPayment","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
61012060405234801562000011575f80fd5b50604051620062fa380380620062fa8339810160408190526200003491620005c0565b33805f816200008a5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b5f80546001600160a01b0319166001600160a01b0384811691909117909155811615620000bc57620000bc81620001cd565b50506001805460ff60a01b1916600160a01b1790555060ff851661010052601789810b60805288900b60a0526001600160a01b0380841660c05263ffffffff821660e05260148054918c166001600160a01b0319909216821790556040515f907f4966a50c93f855342ccf6c5c0d358b85b91335b2acedc7da0932f691f351711a908290a36200014c8762000277565b6200015786620002f0565b620001635f806200036b565b6012805463ffffffff191663ffffffff84169081179091556040519081527fb24a681ce3399a408a89fd0c2b59dfc24bdad592b1c7ec7671cf060596c1c4d19060200160405180910390a16013620001bc85826200073b565b505050505050505050505062000807565b336001600160a01b03821603620002275760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000081565b600180546001600160a01b0319166001600160a01b038381169182179092555f8054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6015546001600160a01b039081169082168114620002ec57601580546001600160a01b0319166001600160a01b0384811691821790925560408051928416835260208301919091527f793cb73064f3c8cde7e187ae515511e6e56d1ee89bf08b82fa60fb70f8d4891291015b60405180910390a15b5050565b620002fa62000452565b6010546001600160a01b039081169082168114620002ec57601080546001600160a01b0319166001600160a01b0384811691821790925560408051928416835260208301919091527f27b89aede8b560578baaa25ee5ce3852c5eecad1e114b941bbd89e1eb4bae6349101620002e3565b6200037562000452565b60408051808201909152600f546001600160a01b03808216808452600160a01b90920463ffffffff1660208401528416141580620003c357508163ffffffff16816020015163ffffffff1614155b156200044d576040805180820182526001600160a01b0385811680835263ffffffff8681166020948501819052600f80546001600160c01b0319168417600160a01b830217905586518786015187519316835294820152909392909116917fb04e3a37abe9c0fcdfebdeae019a8e2b12ddf53f5d55ffb0caccc1bedaca1541910160405180910390a35b505050565b5f546001600160a01b03163314620004ad5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000081565b565b6001600160a01b0381168114620004c4575f80fd5b50565b8051601781900b8114620004d9575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f83011262000502575f80fd5b81516001600160401b03808211156200051f576200051f620004de565b604051601f8301601f19908116603f011681019082821181831017156200054a576200054a620004de565b816040528381526020925086602085880101111562000567575f80fd5b5f91505b838210156200058a57858201830151818301840152908201906200056b565b5f602085830101528094505050505092915050565b8051620004d981620004af565b805163ffffffff81168114620004d9575f80fd5b5f805f805f805f805f806101408b8d031215620005db575f80fd5b8a51620005e881620004af565b9950620005f860208c01620004c7565b98506200060860408c01620004c7565b975060608b01516200061a81620004af565b60808c01519097506200062d81620004af565b60a08c015190965060ff8116811462000644575f80fd5b60c08c01519095506001600160401b0381111562000660575f80fd5b6200066e8d828e01620004f2565b9450506200067f60e08c016200059f565b9250620006906101008c01620005ac565b9150620006a16101208c01620005ac565b90509295989b9194979a5092959850565b600181811c90821680620006c757607f821691505b602082108103620006e657634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200044d57805f5260205f20601f840160051c81016020851015620007135750805b601f840160051c820191505b8181101562000734575f81556001016200071f565b5050505050565b81516001600160401b03811115620007575762000757620004de565b6200076f81620007688454620006b2565b84620006ec565b602080601f831160018114620007a5575f84156200078d5750858301515b5f19600386901b1c1916600185901b178555620007ff565b5f85815260208120601f198616915b82811015620007d557888601518255948401946001909101908401620007b4565b5085821015620007f357878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b60805160a05160c05160e05161010051615a92620008685f395f61046301525f8181613d5f0152613ff301525f61319e01525f8181610504015281816120bc01526145ea01525f81816103990152818161209401526145bf0152615a925ff3fe608060405234801561000f575f80fd5b506004361061031a575f3560e01c80639bd2c0b1116101a8578063c4c92b37116100f3578063e5fe45771161009e578063eb5dcd6c11610079578063eb5dcd6c146108b8578063f2fde38b146108cb578063fbffd2c1146108de578063feaf968c146108f1575f80fd5b8063e5fe45771461083d578063e76d516814610887578063eb457163146108a5575f80fd5b8063dc7f0124116100ce578063dc7f0124146107dd578063e3d0e71214610802578063e4902f8214610815575f80fd5b8063c4c92b3714610799578063d09dc339146107b7578063daffc4b5146107bf575f80fd5b8063b17f2a6b11610153578063b633620c1161012e578063b633620c14610760578063ba0cb29e14610773578063c107532914610786575f80fd5b8063b17f2a6b14610727578063b1dc65a41461073a578063b5ab58dc1461074d575f80fd5b8063a118f24911610183578063a118f249146106d1578063afcb95d7146106e4578063b121e14714610714575f80fd5b80639bd2c0b11461064c5780639c849b30146106ab5780639e3ceeab146106be575f80fd5b80636b14daf8116102685780638205bf6a116102135780638da5cb5b116101ee5780638da5cb5b146105a157806398e5b12a146105df5780639a6fc8f514610602575f80fd5b80638205bf6a146105735780638823da6c1461057b5780638ac28d5a1461058e575f80fd5b806379ba50971161024357806379ba5097146105335780638038e4a11461053b57806381ff704814610543575f80fd5b80636b14daf8146104df57806370da2f67146105025780637284e4161461052b575f80fd5b80634fb17470116102c8578063643dc105116102a3578063643dc105146104af578063666cab8d146104c2578063668a0f02146104d7575f80fd5b80634fb174701461048d57806350d25bcd146104a057806354fd4d50146104a8575f80fd5b806322adbc78116102f857806322adbc781461039757806329937268146103c0578063313ce5671461045c575f80fd5b80630a7569831461031e5780630eafb25b14610328578063181f5a771461034e575b5f80fd5b6103266108f9565b005b61033b610336366004614e46565b610977565b6040519081526020015b60405180910390f35b61038a6040518060400160405280601481526020017f4475616c41676772656761746f7220312e302e3000000000000000000000000081525081565b6040516103459190614ec2565b7f000000000000000000000000000000000000000000000000000000000000000060170b61033b565b600d54600c546040805163ffffffff6e010000000000000000000000000000850481168252720100000000000000000000000000000000000085048116602083015276010000000000000000000000000000000000000000000085048116928201929092527a01000000000000000000000000000000000000000000000000000090930416606083015262ffffff16608082015260a001610345565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610345565b61032661049b366004614ed4565b610aa6565b61033b610d43565b600661033b565b6103266104bd366004614f1c565b610d6d565b6104ca611001565b6040516103459190614fe0565b61033b61106e565b6104f26104ed3660046150f8565b611082565b6040519015158152602001610345565b7f000000000000000000000000000000000000000000000000000000000000000060170b61033b565b61038a6110b6565b61032661113d565b61032661123e565b600e54600b546040805163ffffffff80851682526401000000009094049093166020840152820152606001610345565b61033b6112d1565b610326610589366004614e46565b61131b565b61032661059c366004614e46565b6113d3565b5f5473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610345565b6105e761143b565b60405169ffffffffffffffffffff9091168152602001610345565b610615610610366004615145565b6115b7565b6040805169ffffffffffffffffffff968716815260208101959095528401929092526060830152909116608082015260a001610345565b604080518082018252600f5473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff16602092830181905283519182529181019190915201610345565b6103266106b93660046151b6565b61167b565b6103266106cc366004614e46565b611896565b6103266106df366004614e46565b611947565b600b54600d54604080515f8152602081019390935261010090910460081c63ffffffff1690820152606001610345565b610326610722366004614e46565b6119f9565b61032661073536600461521d565b611aef565b610326610748366004615238565b611b5a565b61033b61075b366004615311565b611b75565b61033b61076e366004615311565b611bad565b610326610781366004615238565b611c05565b610326610794366004615328565b611c17565b60155473ffffffffffffffffffffffffffffffffffffffff166105ba565b61033b611ee4565b60105473ffffffffffffffffffffffffffffffffffffffff166105ba565b6001546104f29074010000000000000000000000000000000000000000900460ff1681565b610326610810366004615416565b611f96565b610828610823366004614e46565b6127bc565b60405163ffffffff9091168152602001610345565b610845612885565b6040805195865263ffffffff909416602086015260ff9092169284019290925260179190910b606083015267ffffffffffffffff16608082015260a001610345565b60145473ffffffffffffffffffffffffffffffffffffffff166105ba565b6103266108b33660046154db565b612932565b6103266108c6366004614ed4565b612a69565b6103266108d9366004614e46565b612bbe565b6103266108ec366004614e46565b612bcf565b610615612be0565b610901612c78565b60015474010000000000000000000000000000000000000000900460ff161561097557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f3be8a977a014527b50ae38adda80b56911c267328965c98ddc385d248f539638905f90a15b565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526003602090815260408083208151606081018352905460ff80821615158084526101008304909116948301949094526201000090046bffffffffffffffffffffffff1691810191909152906109e957505f92915050565b600d5460208201515f91760100000000000000000000000000000000000000000000900463ffffffff169060079060ff16601f8110610a2a57610a2a615507565b600881049190910154600d54610a60926007166004026101000a90910463ffffffff908116916601000000000000900416615561565b63ffffffff16610a70919061557e565b610a7e90633b9aca0061557e565b905081604001516bffffffffffffffffffffffff1681610a9e9190615595565b949350505050565b610aae612c78565b60145473ffffffffffffffffffffffffffffffffffffffff908116908316819003610ad857505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa158015610b40573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b6491906155a8565b50610b6d612cf8565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610bd7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfb91906155a8565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192509083169063a9059cbb906044016020604051808303815f875af1158015610c71573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c9591906155bf565b610ccb576040517f7725087a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86811691821790925560405190918416907f4966a50c93f855342ccf6c5c0d358b85b91335b2acedc7da0932f691f351711a905f90a350505b5050565b5f60115f610d4f613138565b63ffffffff16815260208101919091526040015f205460170b919050565b5f5473ffffffffffffffffffffffffffffffffffffffff16331480610e2657506015546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890610de79033905f9036906004016155de565b602060405180830381865afa158015610e02573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2691906155bf565b610e5c576040517f91ed77c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e64612cf8565b600d80547fffffffffffffffffffff0000000000000000ffffffffffffffffffffffffffff166e01000000000000000000000000000063ffffffff8881169182027fffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffff16929092177201000000000000000000000000000000000000888416908102919091177fffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffff167601000000000000000000000000000000000000000000008885169081027fffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffff16919091177a01000000000000000000000000000000000000000000000000000094881694850217909455600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000001662ffffff871690811790915560408051938452602084019290925290820193909352606081019190915260808101919091527f0bf184bf1bba9699114bdceddaf338a1b364252c5e497cc01918dde92031713f9060a00160405180910390a15050505050565b6060600680548060200260200160405190810160405280929190818152602001828054801561106457602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611039575b5050505050905090565b5f611077613138565b63ffffffff16905090565b5f61108d838361329d565b806110ad575073ffffffffffffffffffffffffffffffffffffffff831632145b90505b92915050565b6060601380546110c590615647565b80601f01602080910402602001604051908101604052809291908181526020018280546110f190615647565b80156110645780601f1061111357610100808354040283529160200191611064565b820191905f5260205f20905b81548152906001019060200180831161111f57509395945050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146111c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b5f8054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611246612c78565b60015474010000000000000000000000000000000000000000900460ff1661097557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556040517faebf329500988c6488a0074e5a0a9ff304561fc5c6fc877aeb1d59c8282c3480905f90a1565b5f60115f6112dd613138565b63ffffffff908116825260208201929092526040015f20547c0100000000000000000000000000000000000000000000000000000000900416919050565b611323612c78565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff16156113d05773ffffffffffffffffffffffffffffffffffffffff81165f8181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f3d68a6fce901d20453d1a7aa06bf3950302a735948037deb182a8db66df2a0d191015b60405180910390a15b50565b73ffffffffffffffffffffffffffffffffffffffff8181165f90815260166020526040902054163314611432576040517f2ab4a3db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d0816132f1565b5f805473ffffffffffffffffffffffffffffffffffffffff1633148015906114f957506010546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf8906114b89033905f9036906004016155de565b602060405180830381865afa1580156114d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114f791906155bf565b155b15611530576040517f4cdc445800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d54600b546040805191825263ffffffff6101008404600881901c8216602085015260ff811684840152915164ffffffffff9092169366010000000000009004169133917f41e3990591fd372502daa15842da15bc7f41c75309ab3ff4f56f1848c178825c9181900360600190a26115aa816001615692565b63ffffffff169250505090565b5f805f805f6115c4613138565b63ffffffff168669ffffffffffffffffffff1611156115f057505f935083925082915081905080611672565b5050505063ffffffff8281165f9081526011602090815260409182902082516060810184529054601781900b8083527801000000000000000000000000000000000000000000000000820486169383018490527c0100000000000000000000000000000000000000000000000000000000909104909416920182905284935090835b91939590929450565b611683612c78565b8281146116bc576040517f3d2f942900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8381101561188f575f8585838181106116d9576116d9615507565b90506020020160208101906116ee9190614e46565b90505f84848481811061170357611703615507565b90506020020160208101906117189190614e46565b73ffffffffffffffffffffffffffffffffffffffff8084165f9081526016602052604090205491925016801580158161177d57508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b156117b4576040517faeae062800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8481165f90815260166020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001685831690811790915590831614611880578273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b360405160405180910390a45b505050508060010190506116be565b5050505050565b61189e612c78565b60105473ffffffffffffffffffffffffffffffffffffffff9081169082168114610d3f57601080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560408051928416835260208301919091527f27b89aede8b560578baaa25ee5ce3852c5eecad1e114b941bbd89e1eb4bae63491015b60405180910390a15050565b61194f612c78565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff166113d05773ffffffffffffffffffffffffffffffffffffffff81165f8181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db491016113c7565b73ffffffffffffffffffffffffffffffffffffffff8181165f90815260176020526040902054163314611a58576040517f6599cbbe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8181165f8181526016602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556017909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b611af7612c78565b601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff83169081179091556040519081527fb24a681ce3399a408a89fd0c2b59dfc24bdad592b1c7ec7671cf060596c1c4d1906020016113c7565b611b6b88888888888888885f613546565b5050505050505050565b5f611b7e613138565b63ffffffff16821115611b9257505f919050565b5063ffffffff165f9081526011602052604090205460170b90565b5f611bb6613138565b63ffffffff16821115611bca57505f919050565b5063ffffffff9081165f908152601160205260409020547c010000000000000000000000000000000000000000000000000000000090041690565b611b6b88888888888888886001613546565b5f5473ffffffffffffffffffffffffffffffffffffffff163314801590611cd457506015546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890611c939033905f9036906004016155de565b602060405180830381865afa158015611cae573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cd291906155bf565b155b15611d0b576040517f91ed77c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611d146138c3565b6014546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529192505f9173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611d85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611da991906155a8565b905081811015611de5576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60145473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85611e17611e1186866156af565b87613aa6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303815f875af1158015611e84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ea891906155bf565b611ede576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6014546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f91829173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611f54573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7891906155a8565b90505f611f836138c3565b9050611f8f81836156c2565b9250505090565b611f9e612c78565b601f86511115611fda576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8451865114612015576040517f250a65b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85516120228560036156e1565b60ff161061205c576040517f20c9729a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120688460ff16613abc565b604080517f010000000000000000000000000000000000000000000000000000000000000060208201527f0000000000000000000000000000000000000000000000000000000000000000821b60218201527f000000000000000000000000000000000000000000000000000000000000000090911b60398201526051016040516020818303038152906040528051906020012083805190602001201461213b576040517fa8811dc600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c08101825267ffffffffffffffff8416815260ff86166020820152908101849052606081018290526080810187905260a08101869052600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ff1690556121a6612cf8565b6005545f5b81811015612291575f600582815481106121c7576121c7615507565b5f9182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff9092169350908490811061220057612200615507565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff948516835260048252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905594168252600390529190912080547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000169055506001016121ab565b5061229d60055f614cdc565b6122a860065f614cdc565b5f5b8260800151518110156125b55760045f846080015183815181106122d0576122d0615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f205460ff161561233a576040517f16c6131500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff1681525060045f8560800151848151811061236e5761236e615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040015f90812083518154949093015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff931515939093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093179190911790915560a0840151805160039291908490811061242357612423615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f205460ff161561248d576040517fd63d347400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180606001604052806001151581526020018260ff1681526020015f6bffffffffffffffffffffffff1681525060035f8560a0015184815181106124d5576124d5615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260409081015f20835181549385015194909201516bffffffffffffffffffffffff1662010000027fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff931515939093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009094169390931791909117929092161790556001016122aa565b50608082015180516125cf91600591602090910190614cf7565b5060a082015180516125e991600691602090910190614cf7565b506020820151600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600e80547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff811664010000000063ffffffff4381168202928317855590830481169360019390925f9261267a928692908216911617615692565b92506101000a81548163ffffffff021916908363ffffffff1602179055506126d74630600e5f9054906101000a900463ffffffff1663ffffffff1686608001518760a00151886020015189604001518a5f01518b60600151613af5565b600b819055600e54608085015160a08601516020870151604080890151895160608b015192517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e059861273d988b98919763ffffffff9091169691959094919391926156fd565b60405180910390a1600d546601000000000000900463ffffffff165f5b8460800151518110156127af5781600782601f811061277b5761277b615507565b600891828204019190066004026101000a81548163ffffffff021916908363ffffffff16021790555080600101905061275a565b5050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526003602090815260408083208151606081018352905460ff80821615158084526101008304909116948301949094526201000090046bffffffffffffffffffffffff16918101919091529061282e57505f92915050565b6007816020015160ff16601f811061284857612848615507565b600881049190910154600d5461287e926007166004026101000a90910463ffffffff908116916601000000000000900416615561565b9392505050565b5f808080803332146128c3576040517f74e2cd5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600b54600d5463ffffffff6601000000000000820481165f908152601160205260409020549296610100909204600881901c8216965064ffffffffff169450601783900b93507c010000000000000000000000000000000000000000000000000000000090920490911690565b61293a612c78565b60408051808201909152600f5473ffffffffffffffffffffffffffffffffffffffff8082168084527401000000000000000000000000000000000000000090920463ffffffff16602084015284161415806129a557508163ffffffff16816020015163ffffffff1614155b15612a645760408051808201825273ffffffffffffffffffffffffffffffffffffffff85811680835263ffffffff8681166020948501819052600f80547fffffffffffffffff00000000000000000000000000000000000000000000000016841774010000000000000000000000000000000000000000830217905586518786015187519316835294820152909392909116917fb04e3a37abe9c0fcdfebdeae019a8e2b12ddf53f5d55ffb0caccc1bedaca1541910160405180910390a35b505050565b73ffffffffffffffffffffffffffffffffffffffff8281165f90815260166020526040902054163314612ac8576040517fb97d016a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163303612b17576040517f79df0c6600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8083165f90815260176020526040902080548383167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117909255909116908114612a645760405173ffffffffffffffffffffffffffffffffffffffff8084169133918616907f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e38367905f90a4505050565b612bc6612c78565b6113d081613ba0565b612bd7612c78565b6113d081613c94565b5f805f805f80612bee613138565b63ffffffff9081165f8181526011602090815260409182902082516060810184529054601781900b8083527801000000000000000000000000000000000000000000000000820487169383018490527c0100000000000000000000000000000000000000000000000000000000909104909516920182905291999298509096509450879350915050565b5f5473ffffffffffffffffffffffffffffffffffffffff163314610975576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016111ba565b601454600d54604080516103e081019182905273ffffffffffffffffffffffffffffffffffffffff90931692660100000000000090920463ffffffff16915f91600790601f908285855b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d42579050505050505090505f6006805480602002602001604051908101604052809291908181526020018280548015612de757602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612dbc575b505050505090505f5b815181101561312a575f60035f848481518110612e0f57612e0f615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f0160029054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff1690505f60035f858581518110612e9157612e91615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f0160026101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505f8483601f8110612f1557612f15615507565b6020020151600d5490870363ffffffff9081169250760100000000000000000000000000000000000000000000909104168102633b9aca00028201801561311f575f60165f878781518110612f6c57612f6c615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683529082019290925260409081015f205490517fa9059cbb00000000000000000000000000000000000000000000000000000000815290821660048201819052602482018590529250908a169063a9059cbb906044016020604051808303815f875af1158015613002573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061302691906155bf565b61305c576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b878786601f811061306f5761306f615507565b602002019063ffffffff16908163ffffffff16815250508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff168787815181106130c6576130c6615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c8560405161311591815260200190565b60405180910390a4505b505050600101612df0565b5061188f600783601f614d7f565b600d545f9063ffffffff660100000000000082048116916a010000000000000000000081049091169060ff7e010000000000000000000000000000000000000000000000000000000000009091041673ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036132305760125463ffffffff8381165f90815260116020526040902054429261320d92908116917c0100000000000000000000000000000000000000000000000000000000900416615692565b63ffffffff16101561322957613221613d35565b935050505090565b5092915050565b8163ffffffff168363ffffffff160361329557808015613285575063ffffffff8084165f908152601160205260409020547c010000000000000000000000000000000000000000000000000000000090041642145b1561329557613221600184615561565b509092915050565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526002602052604081205460ff16806110ad57505060015474010000000000000000000000000000000000000000900460ff161592915050565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600360209081526040918290208251606081018452905460ff80821615158084526101008304909116938301939093526201000090046bffffffffffffffffffffffff169281019290925261335f575050565b5f61336983610977565b90508015612a645773ffffffffffffffffffffffffffffffffffffffff8381165f90815260166020526040908190205460145491517fa9059cbb000000000000000000000000000000000000000000000000000000008152908316600482018190526024820185905292919091169063a9059cbb906044016020604051808303815f875af11580156133fd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061342191906155bf565b613457576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5f0160069054906101000a900463ffffffff166007846020015160ff16601f811061348657613486615507565b6008810491909101805460079092166004026101000a63ffffffff81810219909316939092169190910291909117905573ffffffffffffffffffffffffffffffffffffffff8481165f8181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff169055601454915186815291841693851692917fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c910160405180910390a450505050565b5f5a90506135568a898887613e1d565b5f6135958a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250613f3192505050565b6040805161012081018252600d5460ff808216835261010080830464ffffffffff1660208501526601000000000000830463ffffffff908116958501959095526a01000000000000000000008304851660608501526e01000000000000000000000000000083048516608085015272010000000000000000000000000000000000008304851660a08501527601000000000000000000000000000000000000000000008304851660c08501527a010000000000000000000000000000000000000000000000000000830490941660e08401527e01000000000000000000000000000000000000000000000000000000000000909104161515918101919091529091508315613781575f806136a884613fc7565b91509150811561377e578063ffffffff16836060015163ffffffff16106136fb576040517ff803a2ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff166a010000000000000000000063ffffffff8416908102919091179091556040517f8d530b9ddc4b318d28fdd4c3a21fcfecece54c1a72a824f262985b99afef009b905f90a261377483855f015187614170565b50505050506138b8565b50505b602081810151908d01359064ffffffffff80831691161415806137a75750816101000151155b1561381a57816020015164ffffffffff168164ffffffffff16116137f7576040517ff803a2ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6138078d8d8d8d8d8d8d8d6142ab565b613815828e358386896144f8565b613858565b600d54604051660100000000000090910463ffffffff16907fda2435684a37fba6f7841e49b59e6ad975e462bbebd28ec9da4ed9746a6992be905f90a25b600d80547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000008715150217905582516138b390839086614170565b505050505b505050505050505050565b5f80600680548060200260200160405190810160405280929190818152602001828054801561392657602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116138fb575b50508351600d54604080516103e08101918290529697509195660100000000000090910463ffffffff1694505f93509150600790601f908285855b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411613961579050505050505090505f5b838110156139e9578181601f81106139c0576139c0615507565b60200201516139cf9084615561565b6139df9063ffffffff1687615595565b95506001016139a6565b50600d54613a1b90760100000000000000000000000000000000000000000000900463ffffffff16633b9aca0061557e565b613a25908661557e565b94505f5b83811015613a9e5760035f868381518110613a4657613a46615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f2054613a94906201000090046bffffffffffffffffffffffff1687615595565b9550600101613a29565b505050505090565b5f81831015613ab65750816110b0565b50919050565b5f81116113d0576040517f39d1a4d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f808a8a8a8a8a8a8a8a8a604051602001613b1899989796959493929190615792565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150505b9998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613c1f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016111ba565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182179092555f8054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60155473ffffffffffffffffffffffffffffffffffffffff9081169082168114610d3f57601580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560408051928416835260208301919091527f793cb73064f3c8cde7e187ae515511e6e56d1ee89bf08b82fa60fb70f8d48912910161193b565b600d545f906601000000000000900463ffffffff16805b63ffffffff811615613dff5763ffffffff7f000000000000000000000000000000000000000000000000000000000000000016613d898284615561565b63ffffffff1614613dff5760125463ffffffff8281165f908152601160205260409020544292613ddd92908116917c0100000000000000000000000000000000000000000000000000000000900416615692565b63ffffffff161015613def5792915050565b613df881615826565b9050613d4c565b5050600d546a0100000000000000000000900463ffffffff16919050565b335f9081526003602052604090205460ff16613e65576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b54843514613ea1576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613eac8383836149eb565b600d54613ebd9060ff166001615863565b60ff168214613ef8576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808214611ede576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516080810182525f80825260208201526060918101829052818101919091525f805f8085806020019051810190613f6b919061588d565b9350935093509350613f7d8683614a67565b815160408051602081018690525f910160408051918152928152825160808101845260179490940b845263ffffffff90961660208401525081019390935260608301525092915050565b600d545f9081906601000000000000900463ffffffff16805b63ffffffff8116156141645763ffffffff7f00000000000000000000000000000000000000000000000000000000000000001661401d8284615561565b63ffffffff160361405a576040517fc3d2e25400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8082165f9081526011602090815260409182902082516060810184529054601781900b82527801000000000000000000000000000000000000000000000000810485168284018190527c01000000000000000000000000000000000000000000000000000000009091048516938201939093529088015190921611156140ec57505f958695509350505050565b856020015163ffffffff16816020015163ffffffff1614801561414157506060860151805161411d90600290615950565b8151811061412d5761412d615507565b602002602001015160170b815f015160170b145b15614153575060019590945092505050565b5061415d81615826565b9050613fe0565b505f9485945092505050565b5f8260170b121561418057505050565b5f6141a6633b9aca003a048560a0015163ffffffff16866080015163ffffffff16614ac7565b9050601036025f5a600c549091505f906141d19063ffffffff8716908690869062ffffff1686614aec565b90505f670de0b6b3a764000077ffffffffffffffffffffffffffffffffffffffffffffffff88168302335f9081526003602052604090205460e08b01519290910492506201000090046bffffffffffffffffffffffff9081169163ffffffff16633b9aca0002828401019081168211156142515750505050505050505050565b335f90815260036020526040902080546bffffffffffffffffffffffff90921662010000027fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff909216919091179055505050505050505050565b5f87876040516142bc929190615988565b6040519081900381206142d3918b90602001615997565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092525f808452908301819052909250905f5b8781101561449d575f60018587846020811061433d5761433d615507565b61434a91901a601b615863565b8c8c8681811061435c5761435c615507565b905060200201358b8b8781811061437557614375615507565b905060200201356040515f81526020016040526040516143b1949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa1580156143d1573d5f803e3d5ffd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81165f9081526004602090815290849020838501909452925460ff808216151580855261010090920416938301939093529095509250905061447e576040517fcd2467c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b840193505080600101905061431f565b5081827e0101010101010101010101010101010101010101010101010101010101010116146127af576040517f8044bb3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8260600151511115614538576040517fff6c220500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845f015160ff168260600151511161457c576040517f5765bdd700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b64ffffffffff83166020860152606082015180515f919061459f90600290615950565b815181106145af576145af615507565b602002602001015190508060170b7f000000000000000000000000000000000000000000000000000000000000000060170b138061461257507f000000000000000000000000000000000000000000000000000000000000000060170b8160170b135b15614649576040517fca191b2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408601805190614659826159ad565b63ffffffff90811690915260408051606081018252601785900b815260208781015184168183019081524285168385019081528c85015186165f908152601190935293909120915182549151935185167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff949095167801000000000000000000000000000000000000000000000000027fffffffff0000000000000000000000000000000000000000000000000000000090921677ffffffffffffffffffffffffffffffffffffffffffffffff9091161717919091169190911790555081156147925760408087015163ffffffff166060880181905290517f8d530b9ddc4b318d28fdd4c3a21fcfecece54c1a72a824f262985b99afef009b905f90a25b85600d5f820151815f015f6101000a81548160ff021916908360ff1602179055506020820151815f0160016101000a81548164ffffffffff021916908364ffffffffff1602179055506040820151815f0160066101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01600a6101000a81548163ffffffff021916908363ffffffff1602179055506080820151815f01600e6101000a81548163ffffffff021916908363ffffffff16021790555060a0820151815f0160126101000a81548163ffffffff021916908363ffffffff16021790555060c0820151815f0160166101000a81548163ffffffff021916908363ffffffff16021790555060e0820151815f01601a6101000a81548163ffffffff021916908363ffffffff160217905550610100820151815f01601e6101000a81548160ff021916908315150217905550905050856040015163ffffffff167fc797025feeeaf2cd924c99e9205acb8ec04d5cad21c41ce637a38fb6dee6016a8233866020015187606001518860400151895f01518c8c6040516149399897969594939291906159cf565b60405180910390a2604080870151602080860151925163ffffffff93841681525f93909216917f0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac60271910160405180910390a3856040015163ffffffff168160170b7f0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f426040516149ca91815260200190565b60405180910390a36149e386604001518260170b614b39565b505050505050565b5f6149f782602061557e565b614a0284602061557e565b614a0e86610144615595565b614a189190615595565b614a229190615595565b614a2c905f615595565b9050368114611ede576040517fb4d895d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f81516020614a76919061557e565b614a819060a0615595565b614a8b905f615595565b905080835114612a64576040517fd4e1416000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8383811015614ad957600285850304015b614ae38184613aa6565b95945050505050565b5f81861015614b27576040517ffbf484ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50633b9aca0094039190910101020290565b60408051808201909152600f5473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff166020830152614b8d57505050565b5f614b99600185615561565b63ffffffff8181165f81815260116020526040808220549051602481019390935260170b60448301819052928816606483015260848201879052929350909190614c6b9060a401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbeed9b510000000000000000000000000000000000000000000000000000000017905286519087015163ffffffff16611388614ca5565b915050806149e3576040517f1c26714c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805a838110614cd257839003604081048103851015614cd2575f80885160208a015f8a8af19250600191505b5094509492505050565b5080545f8255905f5260205f20908101906113d09190614e11565b828054828255905f5260205f20908101928215614d6f579160200282015b82811115614d6f57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190614d15565b50614d7b929150614e11565b5090565b600483019183908215614d6f579160200282015f5b83821115614dd857835183826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302614d94565b8015614e085782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614dd8565b5050614d7b9291505b5b80821115614d7b575f8155600101614e12565b73ffffffffffffffffffffffffffffffffffffffff811681146113d0575f80fd5b5f60208284031215614e56575f80fd5b813561287e81614e25565b5f81518084525f5b81811015614e8557602081850181015186830182015201614e69565b505f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6110ad6020830184614e61565b5f8060408385031215614ee5575f80fd5b8235614ef081614e25565b91506020830135614f0081614e25565b809150509250929050565b63ffffffff811681146113d0575f80fd5b5f805f805f60a08688031215614f30575f80fd5b8535614f3b81614f0b565b94506020860135614f4b81614f0b565b93506040860135614f5b81614f0b565b92506060860135614f6b81614f0b565b9150608086013562ffffff81168114614f82575f80fd5b809150509295509295909350565b5f815180845260208085019450602084015f5b83811015614fd557815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614fa3565b509495945050505050565b602081525f6110ad6020830184614f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561506657615066614ff2565b604052919050565b5f82601f83011261507d575f80fd5b813567ffffffffffffffff81111561509757615097614ff2565b6150c860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161501f565b8181528460208386010111156150dc575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8060408385031215615109575f80fd5b823561511481614e25565b9150602083013567ffffffffffffffff81111561512f575f80fd5b61513b8582860161506e565b9150509250929050565b5f60208284031215615155575f80fd5b813569ffffffffffffffffffff8116811461287e575f80fd5b5f8083601f84011261517e575f80fd5b50813567ffffffffffffffff811115615195575f80fd5b6020830191508360208260051b85010111156151af575f80fd5b9250929050565b5f805f80604085870312156151c9575f80fd5b843567ffffffffffffffff808211156151e0575f80fd5b6151ec8883890161516e565b90965094506020870135915080821115615204575f80fd5b506152118782880161516e565b95989497509550505050565b5f6020828403121561522d575f80fd5b813561287e81614f0b565b5f805f805f805f8060e0898b03121561524f575f80fd5b606089018a81111561525f575f80fd5b8998503567ffffffffffffffff80821115615278575f80fd5b818b0191508b601f83011261528b575f80fd5b813581811115615299575f80fd5b8c60208285010111156152aa575f80fd5b6020830199508098505060808b01359150808211156152c7575f80fd5b6152d38c838d0161516e565b909750955060a08b01359150808211156152eb575f80fd5b506152f88b828c0161516e565b999c989b50969995989497949560c00135949350505050565b5f60208284031215615321575f80fd5b5035919050565b5f8060408385031215615339575f80fd5b823561534481614e25565b946020939093013593505050565b5f67ffffffffffffffff82111561536b5761536b614ff2565b5060051b60200190565b5f82601f830112615384575f80fd5b8135602061539961539483615352565b61501f565b8083825260208201915060208460051b8701019350868411156153ba575f80fd5b602086015b848110156153df5780356153d281614e25565b83529183019183016153bf565b509695505050505050565b803560ff811681146153fa575f80fd5b919050565b803567ffffffffffffffff811681146153fa575f80fd5b5f805f805f8060c0878903121561542b575f80fd5b863567ffffffffffffffff80821115615442575f80fd5b61544e8a838b01615375565b97506020890135915080821115615463575f80fd5b61546f8a838b01615375565b965061547d60408a016153ea565b95506060890135915080821115615492575f80fd5b61549e8a838b0161506e565b94506154ac60808a016153ff565b935060a08901359150808211156154c1575f80fd5b506154ce89828a0161506e565b9150509295509295509295565b5f80604083850312156154ec575f80fd5b82356154f781614e25565b91506020830135614f0081614f0b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b63ffffffff82811682821603908082111561322957613229615534565b80820281158282048414176110b0576110b0615534565b808201808211156110b0576110b0615534565b5f602082840312156155b8575f80fd5b5051919050565b5f602082840312156155cf575f80fd5b8151801515811461287e575f80fd5b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152816040820152818360608301375f818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b600181811c9082168061565b57607f821691505b602082108103613ab6577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b63ffffffff81811683821601908082111561322957613229615534565b818103818111156110b0576110b0615534565b8181035f83128015838313168383128216171561322957613229615534565b60ff818116838216029081169081811461322957613229615534565b5f61012063ffffffff808d1684528b6020850152808b1660408501525080606084015261572c8184018a614f90565b905082810360808401526157408189614f90565b905060ff871660a084015282810360c084015261575d8187614e61565b905067ffffffffffffffff851660e08401528281036101008401526157828185614e61565b9c9b505050505050505050505050565b5f6101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b1660408501528160608501526157d88285018b614f90565b915083820360808501526157ec828a614f90565b915060ff881660a085015283820360c08501526158098288614e61565b90861660e085015283810361010085015290506157828185614e61565b5f63ffffffff82168061583b5761583b615534565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60ff81811683821601908111156110b0576110b0615534565b8051601781900b81146153fa575f80fd5b5f805f80608085870312156158a0575f80fd5b84516158ab81614f0b565b809450506020808601519350604086015167ffffffffffffffff8111156158d0575f80fd5b8601601f810188136158e0575f80fd5b80516158ee61539482615352565b81815260059190911b8201830190838101908a83111561590c575f80fd5b928401925b82841015615931576159228461587c565b82529284019290840190615911565b80965050505050506159456060860161587c565b905092959194509250565b5f82615983577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b818382375f9101908152919050565b8281526080810160608360208401379392505050565b5f63ffffffff8083168181036159c5576159c5615534565b6001019392505050565b5f61010080830160178c60170b8552602073ffffffffffffffffffffffffffffffffffffffff8d16602087015263ffffffff8c1660408701528360608701528293508a518084526101208701945060208c0193505f5b81811015615a43578451840b86529482019493820193600101615a25565b50505050508281036080840152615a5a8188614e61565b915050615a6c60a083018660170b9052565b8360c0830152613b9360e083018464ffffffffff16905256fea164736f6c6343000818000a0000000000000000000000001ac2ee68b8d038c982c1e1f73f596927dd70de59000000000000000000000000000000000000000000000000000000000000000100000000000000000000ffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000058bbb01e021024a280f7a84f40de366df875957000000000000000000000000ddfcbf700abfbed39c2f69f3a427faee0efff9e2000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000001400000000000000000000000009df2b2ddfa0799d67fdee10fe7e5d349e6129775000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000009425443202f205553440000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561000f575f80fd5b506004361061031a575f3560e01c80639bd2c0b1116101a8578063c4c92b37116100f3578063e5fe45771161009e578063eb5dcd6c11610079578063eb5dcd6c146108b8578063f2fde38b146108cb578063fbffd2c1146108de578063feaf968c146108f1575f80fd5b8063e5fe45771461083d578063e76d516814610887578063eb457163146108a5575f80fd5b8063dc7f0124116100ce578063dc7f0124146107dd578063e3d0e71214610802578063e4902f8214610815575f80fd5b8063c4c92b3714610799578063d09dc339146107b7578063daffc4b5146107bf575f80fd5b8063b17f2a6b11610153578063b633620c1161012e578063b633620c14610760578063ba0cb29e14610773578063c107532914610786575f80fd5b8063b17f2a6b14610727578063b1dc65a41461073a578063b5ab58dc1461074d575f80fd5b8063a118f24911610183578063a118f249146106d1578063afcb95d7146106e4578063b121e14714610714575f80fd5b80639bd2c0b11461064c5780639c849b30146106ab5780639e3ceeab146106be575f80fd5b80636b14daf8116102685780638205bf6a116102135780638da5cb5b116101ee5780638da5cb5b146105a157806398e5b12a146105df5780639a6fc8f514610602575f80fd5b80638205bf6a146105735780638823da6c1461057b5780638ac28d5a1461058e575f80fd5b806379ba50971161024357806379ba5097146105335780638038e4a11461053b57806381ff704814610543575f80fd5b80636b14daf8146104df57806370da2f67146105025780637284e4161461052b575f80fd5b80634fb17470116102c8578063643dc105116102a3578063643dc105146104af578063666cab8d146104c2578063668a0f02146104d7575f80fd5b80634fb174701461048d57806350d25bcd146104a057806354fd4d50146104a8575f80fd5b806322adbc78116102f857806322adbc781461039757806329937268146103c0578063313ce5671461045c575f80fd5b80630a7569831461031e5780630eafb25b14610328578063181f5a771461034e575b5f80fd5b6103266108f9565b005b61033b610336366004614e46565b610977565b6040519081526020015b60405180910390f35b61038a6040518060400160405280601481526020017f4475616c41676772656761746f7220312e302e3000000000000000000000000081525081565b6040516103459190614ec2565b7f000000000000000000000000000000000000000000000000000000000000000160170b61033b565b600d54600c546040805163ffffffff6e010000000000000000000000000000850481168252720100000000000000000000000000000000000085048116602083015276010000000000000000000000000000000000000000000085048116928201929092527a01000000000000000000000000000000000000000000000000000090930416606083015262ffffff16608082015260a001610345565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000012168152602001610345565b61032661049b366004614ed4565b610aa6565b61033b610d43565b600661033b565b6103266104bd366004614f1c565b610d6d565b6104ca611001565b6040516103459190614fe0565b61033b61106e565b6104f26104ed3660046150f8565b611082565b6040519015158152602001610345565b7f00000000000000000000ffffffffffffffffffffffffffffffffffffffffffff60170b61033b565b61038a6110b6565b61032661113d565b61032661123e565b600e54600b546040805163ffffffff80851682526401000000009094049093166020840152820152606001610345565b61033b6112d1565b610326610589366004614e46565b61131b565b61032661059c366004614e46565b6113d3565b5f5473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610345565b6105e761143b565b60405169ffffffffffffffffffff9091168152602001610345565b610615610610366004615145565b6115b7565b6040805169ffffffffffffffffffff968716815260208101959095528401929092526060830152909116608082015260a001610345565b604080518082018252600f5473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff16602092830181905283519182529181019190915201610345565b6103266106b93660046151b6565b61167b565b6103266106cc366004614e46565b611896565b6103266106df366004614e46565b611947565b600b54600d54604080515f8152602081019390935261010090910460081c63ffffffff1690820152606001610345565b610326610722366004614e46565b6119f9565b61032661073536600461521d565b611aef565b610326610748366004615238565b611b5a565b61033b61075b366004615311565b611b75565b61033b61076e366004615311565b611bad565b610326610781366004615238565b611c05565b610326610794366004615328565b611c17565b60155473ffffffffffffffffffffffffffffffffffffffff166105ba565b61033b611ee4565b60105473ffffffffffffffffffffffffffffffffffffffff166105ba565b6001546104f29074010000000000000000000000000000000000000000900460ff1681565b610326610810366004615416565b611f96565b610828610823366004614e46565b6127bc565b60405163ffffffff9091168152602001610345565b610845612885565b6040805195865263ffffffff909416602086015260ff9092169284019290925260179190910b606083015267ffffffffffffffff16608082015260a001610345565b60145473ffffffffffffffffffffffffffffffffffffffff166105ba565b6103266108b33660046154db565b612932565b6103266108c6366004614ed4565b612a69565b6103266108d9366004614e46565b612bbe565b6103266108ec366004614e46565b612bcf565b610615612be0565b610901612c78565b60015474010000000000000000000000000000000000000000900460ff161561097557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f3be8a977a014527b50ae38adda80b56911c267328965c98ddc385d248f539638905f90a15b565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526003602090815260408083208151606081018352905460ff80821615158084526101008304909116948301949094526201000090046bffffffffffffffffffffffff1691810191909152906109e957505f92915050565b600d5460208201515f91760100000000000000000000000000000000000000000000900463ffffffff169060079060ff16601f8110610a2a57610a2a615507565b600881049190910154600d54610a60926007166004026101000a90910463ffffffff908116916601000000000000900416615561565b63ffffffff16610a70919061557e565b610a7e90633b9aca0061557e565b905081604001516bffffffffffffffffffffffff1681610a9e9190615595565b949350505050565b610aae612c78565b60145473ffffffffffffffffffffffffffffffffffffffff908116908316819003610ad857505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa158015610b40573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b6491906155a8565b50610b6d612cf8565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610bd7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfb91906155a8565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192509083169063a9059cbb906044016020604051808303815f875af1158015610c71573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c9591906155bf565b610ccb576040517f7725087a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86811691821790925560405190918416907f4966a50c93f855342ccf6c5c0d358b85b91335b2acedc7da0932f691f351711a905f90a350505b5050565b5f60115f610d4f613138565b63ffffffff16815260208101919091526040015f205460170b919050565b5f5473ffffffffffffffffffffffffffffffffffffffff16331480610e2657506015546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890610de79033905f9036906004016155de565b602060405180830381865afa158015610e02573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2691906155bf565b610e5c576040517f91ed77c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e64612cf8565b600d80547fffffffffffffffffffff0000000000000000ffffffffffffffffffffffffffff166e01000000000000000000000000000063ffffffff8881169182027fffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffff16929092177201000000000000000000000000000000000000888416908102919091177fffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffff167601000000000000000000000000000000000000000000008885169081027fffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffff16919091177a01000000000000000000000000000000000000000000000000000094881694850217909455600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000001662ffffff871690811790915560408051938452602084019290925290820193909352606081019190915260808101919091527f0bf184bf1bba9699114bdceddaf338a1b364252c5e497cc01918dde92031713f9060a00160405180910390a15050505050565b6060600680548060200260200160405190810160405280929190818152602001828054801561106457602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611039575b5050505050905090565b5f611077613138565b63ffffffff16905090565b5f61108d838361329d565b806110ad575073ffffffffffffffffffffffffffffffffffffffff831632145b90505b92915050565b6060601380546110c590615647565b80601f01602080910402602001604051908101604052809291908181526020018280546110f190615647565b80156110645780601f1061111357610100808354040283529160200191611064565b820191905f5260205f20905b81548152906001019060200180831161111f57509395945050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146111c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b5f8054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611246612c78565b60015474010000000000000000000000000000000000000000900460ff1661097557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556040517faebf329500988c6488a0074e5a0a9ff304561fc5c6fc877aeb1d59c8282c3480905f90a1565b5f60115f6112dd613138565b63ffffffff908116825260208201929092526040015f20547c0100000000000000000000000000000000000000000000000000000000900416919050565b611323612c78565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff16156113d05773ffffffffffffffffffffffffffffffffffffffff81165f8181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f3d68a6fce901d20453d1a7aa06bf3950302a735948037deb182a8db66df2a0d191015b60405180910390a15b50565b73ffffffffffffffffffffffffffffffffffffffff8181165f90815260166020526040902054163314611432576040517f2ab4a3db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d0816132f1565b5f805473ffffffffffffffffffffffffffffffffffffffff1633148015906114f957506010546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf8906114b89033905f9036906004016155de565b602060405180830381865afa1580156114d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114f791906155bf565b155b15611530576040517f4cdc445800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d54600b546040805191825263ffffffff6101008404600881901c8216602085015260ff811684840152915164ffffffffff9092169366010000000000009004169133917f41e3990591fd372502daa15842da15bc7f41c75309ab3ff4f56f1848c178825c9181900360600190a26115aa816001615692565b63ffffffff169250505090565b5f805f805f6115c4613138565b63ffffffff168669ffffffffffffffffffff1611156115f057505f935083925082915081905080611672565b5050505063ffffffff8281165f9081526011602090815260409182902082516060810184529054601781900b8083527801000000000000000000000000000000000000000000000000820486169383018490527c0100000000000000000000000000000000000000000000000000000000909104909416920182905284935090835b91939590929450565b611683612c78565b8281146116bc576040517f3d2f942900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8381101561188f575f8585838181106116d9576116d9615507565b90506020020160208101906116ee9190614e46565b90505f84848481811061170357611703615507565b90506020020160208101906117189190614e46565b73ffffffffffffffffffffffffffffffffffffffff8084165f9081526016602052604090205491925016801580158161177d57508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b156117b4576040517faeae062800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8481165f90815260166020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001685831690811790915590831614611880578273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b360405160405180910390a45b505050508060010190506116be565b5050505050565b61189e612c78565b60105473ffffffffffffffffffffffffffffffffffffffff9081169082168114610d3f57601080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560408051928416835260208301919091527f27b89aede8b560578baaa25ee5ce3852c5eecad1e114b941bbd89e1eb4bae63491015b60405180910390a15050565b61194f612c78565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff166113d05773ffffffffffffffffffffffffffffffffffffffff81165f8181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db491016113c7565b73ffffffffffffffffffffffffffffffffffffffff8181165f90815260176020526040902054163314611a58576040517f6599cbbe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8181165f8181526016602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556017909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b611af7612c78565b601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff83169081179091556040519081527fb24a681ce3399a408a89fd0c2b59dfc24bdad592b1c7ec7671cf060596c1c4d1906020016113c7565b611b6b88888888888888885f613546565b5050505050505050565b5f611b7e613138565b63ffffffff16821115611b9257505f919050565b5063ffffffff165f9081526011602052604090205460170b90565b5f611bb6613138565b63ffffffff16821115611bca57505f919050565b5063ffffffff9081165f908152601160205260409020547c010000000000000000000000000000000000000000000000000000000090041690565b611b6b88888888888888886001613546565b5f5473ffffffffffffffffffffffffffffffffffffffff163314801590611cd457506015546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890611c939033905f9036906004016155de565b602060405180830381865afa158015611cae573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cd291906155bf565b155b15611d0b576040517f91ed77c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611d146138c3565b6014546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529192505f9173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611d85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611da991906155a8565b905081811015611de5576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60145473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85611e17611e1186866156af565b87613aa6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303815f875af1158015611e84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ea891906155bf565b611ede576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6014546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f91829173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611f54573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7891906155a8565b90505f611f836138c3565b9050611f8f81836156c2565b9250505090565b611f9e612c78565b601f86511115611fda576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8451865114612015576040517f250a65b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85516120228560036156e1565b60ff161061205c576040517f20c9729a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120688460ff16613abc565b604080517f010000000000000000000000000000000000000000000000000000000000000060208201527f0000000000000000000000000000000000000000000000000000000000000001821b60218201527f00000000000000000000ffffffffffffffffffffffffffffffffffffffffffff90911b60398201526051016040516020818303038152906040528051906020012083805190602001201461213b576040517fa8811dc600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c08101825267ffffffffffffffff8416815260ff86166020820152908101849052606081018290526080810187905260a08101869052600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ff1690556121a6612cf8565b6005545f5b81811015612291575f600582815481106121c7576121c7615507565b5f9182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff9092169350908490811061220057612200615507565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff948516835260048252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905594168252600390529190912080547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000169055506001016121ab565b5061229d60055f614cdc565b6122a860065f614cdc565b5f5b8260800151518110156125b55760045f846080015183815181106122d0576122d0615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f205460ff161561233a576040517f16c6131500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff1681525060045f8560800151848151811061236e5761236e615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040015f90812083518154949093015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff931515939093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093179190911790915560a0840151805160039291908490811061242357612423615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f205460ff161561248d576040517fd63d347400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180606001604052806001151581526020018260ff1681526020015f6bffffffffffffffffffffffff1681525060035f8560a0015184815181106124d5576124d5615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260409081015f20835181549385015194909201516bffffffffffffffffffffffff1662010000027fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff931515939093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009094169390931791909117929092161790556001016122aa565b50608082015180516125cf91600591602090910190614cf7565b5060a082015180516125e991600691602090910190614cf7565b506020820151600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600e80547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff811664010000000063ffffffff4381168202928317855590830481169360019390925f9261267a928692908216911617615692565b92506101000a81548163ffffffff021916908363ffffffff1602179055506126d74630600e5f9054906101000a900463ffffffff1663ffffffff1686608001518760a00151886020015189604001518a5f01518b60600151613af5565b600b819055600e54608085015160a08601516020870151604080890151895160608b015192517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e059861273d988b98919763ffffffff9091169691959094919391926156fd565b60405180910390a1600d546601000000000000900463ffffffff165f5b8460800151518110156127af5781600782601f811061277b5761277b615507565b600891828204019190066004026101000a81548163ffffffff021916908363ffffffff16021790555080600101905061275a565b5050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526003602090815260408083208151606081018352905460ff80821615158084526101008304909116948301949094526201000090046bffffffffffffffffffffffff16918101919091529061282e57505f92915050565b6007816020015160ff16601f811061284857612848615507565b600881049190910154600d5461287e926007166004026101000a90910463ffffffff908116916601000000000000900416615561565b9392505050565b5f808080803332146128c3576040517f74e2cd5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600b54600d5463ffffffff6601000000000000820481165f908152601160205260409020549296610100909204600881901c8216965064ffffffffff169450601783900b93507c010000000000000000000000000000000000000000000000000000000090920490911690565b61293a612c78565b60408051808201909152600f5473ffffffffffffffffffffffffffffffffffffffff8082168084527401000000000000000000000000000000000000000090920463ffffffff16602084015284161415806129a557508163ffffffff16816020015163ffffffff1614155b15612a645760408051808201825273ffffffffffffffffffffffffffffffffffffffff85811680835263ffffffff8681166020948501819052600f80547fffffffffffffffff00000000000000000000000000000000000000000000000016841774010000000000000000000000000000000000000000830217905586518786015187519316835294820152909392909116917fb04e3a37abe9c0fcdfebdeae019a8e2b12ddf53f5d55ffb0caccc1bedaca1541910160405180910390a35b505050565b73ffffffffffffffffffffffffffffffffffffffff8281165f90815260166020526040902054163314612ac8576040517fb97d016a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163303612b17576040517f79df0c6600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8083165f90815260176020526040902080548383167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117909255909116908114612a645760405173ffffffffffffffffffffffffffffffffffffffff8084169133918616907f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e38367905f90a4505050565b612bc6612c78565b6113d081613ba0565b612bd7612c78565b6113d081613c94565b5f805f805f80612bee613138565b63ffffffff9081165f8181526011602090815260409182902082516060810184529054601781900b8083527801000000000000000000000000000000000000000000000000820487169383018490527c0100000000000000000000000000000000000000000000000000000000909104909516920182905291999298509096509450879350915050565b5f5473ffffffffffffffffffffffffffffffffffffffff163314610975576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016111ba565b601454600d54604080516103e081019182905273ffffffffffffffffffffffffffffffffffffffff90931692660100000000000090920463ffffffff16915f91600790601f908285855b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d42579050505050505090505f6006805480602002602001604051908101604052809291908181526020018280548015612de757602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612dbc575b505050505090505f5b815181101561312a575f60035f848481518110612e0f57612e0f615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f0160029054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff1690505f60035f858581518110612e9157612e91615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f0160026101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505f8483601f8110612f1557612f15615507565b6020020151600d5490870363ffffffff9081169250760100000000000000000000000000000000000000000000909104168102633b9aca00028201801561311f575f60165f878781518110612f6c57612f6c615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683529082019290925260409081015f205490517fa9059cbb00000000000000000000000000000000000000000000000000000000815290821660048201819052602482018590529250908a169063a9059cbb906044016020604051808303815f875af1158015613002573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061302691906155bf565b61305c576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b878786601f811061306f5761306f615507565b602002019063ffffffff16908163ffffffff16815250508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff168787815181106130c6576130c6615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c8560405161311591815260200190565b60405180910390a4505b505050600101612df0565b5061188f600783601f614d7f565b600d545f9063ffffffff660100000000000082048116916a010000000000000000000081049091169060ff7e010000000000000000000000000000000000000000000000000000000000009091041673ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000009df2b2ddfa0799d67fdee10fe7e5d349e61297751633036132305760125463ffffffff8381165f90815260116020526040902054429261320d92908116917c0100000000000000000000000000000000000000000000000000000000900416615692565b63ffffffff16101561322957613221613d35565b935050505090565b5092915050565b8163ffffffff168363ffffffff160361329557808015613285575063ffffffff8084165f908152601160205260409020547c010000000000000000000000000000000000000000000000000000000090041642145b1561329557613221600184615561565b509092915050565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526002602052604081205460ff16806110ad57505060015474010000000000000000000000000000000000000000900460ff161592915050565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600360209081526040918290208251606081018452905460ff80821615158084526101008304909116938301939093526201000090046bffffffffffffffffffffffff169281019290925261335f575050565b5f61336983610977565b90508015612a645773ffffffffffffffffffffffffffffffffffffffff8381165f90815260166020526040908190205460145491517fa9059cbb000000000000000000000000000000000000000000000000000000008152908316600482018190526024820185905292919091169063a9059cbb906044016020604051808303815f875af11580156133fd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061342191906155bf565b613457576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5f0160069054906101000a900463ffffffff166007846020015160ff16601f811061348657613486615507565b6008810491909101805460079092166004026101000a63ffffffff81810219909316939092169190910291909117905573ffffffffffffffffffffffffffffffffffffffff8481165f8181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff169055601454915186815291841693851692917fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c910160405180910390a450505050565b5f5a90506135568a898887613e1d565b5f6135958a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250613f3192505050565b6040805161012081018252600d5460ff808216835261010080830464ffffffffff1660208501526601000000000000830463ffffffff908116958501959095526a01000000000000000000008304851660608501526e01000000000000000000000000000083048516608085015272010000000000000000000000000000000000008304851660a08501527601000000000000000000000000000000000000000000008304851660c08501527a010000000000000000000000000000000000000000000000000000830490941660e08401527e01000000000000000000000000000000000000000000000000000000000000909104161515918101919091529091508315613781575f806136a884613fc7565b91509150811561377e578063ffffffff16836060015163ffffffff16106136fb576040517ff803a2ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff166a010000000000000000000063ffffffff8416908102919091179091556040517f8d530b9ddc4b318d28fdd4c3a21fcfecece54c1a72a824f262985b99afef009b905f90a261377483855f015187614170565b50505050506138b8565b50505b602081810151908d01359064ffffffffff80831691161415806137a75750816101000151155b1561381a57816020015164ffffffffff168164ffffffffff16116137f7576040517ff803a2ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6138078d8d8d8d8d8d8d8d6142ab565b613815828e358386896144f8565b613858565b600d54604051660100000000000090910463ffffffff16907fda2435684a37fba6f7841e49b59e6ad975e462bbebd28ec9da4ed9746a6992be905f90a25b600d80547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000008715150217905582516138b390839086614170565b505050505b505050505050505050565b5f80600680548060200260200160405190810160405280929190818152602001828054801561392657602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116138fb575b50508351600d54604080516103e08101918290529697509195660100000000000090910463ffffffff1694505f93509150600790601f908285855b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411613961579050505050505090505f5b838110156139e9578181601f81106139c0576139c0615507565b60200201516139cf9084615561565b6139df9063ffffffff1687615595565b95506001016139a6565b50600d54613a1b90760100000000000000000000000000000000000000000000900463ffffffff16633b9aca0061557e565b613a25908661557e565b94505f5b83811015613a9e5760035f868381518110613a4657613a46615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f2054613a94906201000090046bffffffffffffffffffffffff1687615595565b9550600101613a29565b505050505090565b5f81831015613ab65750816110b0565b50919050565b5f81116113d0576040517f39d1a4d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f808a8a8a8a8a8a8a8a8a604051602001613b1899989796959493929190615792565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150505b9998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613c1f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016111ba565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182179092555f8054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60155473ffffffffffffffffffffffffffffffffffffffff9081169082168114610d3f57601580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560408051928416835260208301919091527f793cb73064f3c8cde7e187ae515511e6e56d1ee89bf08b82fa60fb70f8d48912910161193b565b600d545f906601000000000000900463ffffffff16805b63ffffffff811615613dff5763ffffffff7f000000000000000000000000000000000000000000000000000000000000001416613d898284615561565b63ffffffff1614613dff5760125463ffffffff8281165f908152601160205260409020544292613ddd92908116917c0100000000000000000000000000000000000000000000000000000000900416615692565b63ffffffff161015613def5792915050565b613df881615826565b9050613d4c565b5050600d546a0100000000000000000000900463ffffffff16919050565b335f9081526003602052604090205460ff16613e65576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b54843514613ea1576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613eac8383836149eb565b600d54613ebd9060ff166001615863565b60ff168214613ef8576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808214611ede576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516080810182525f80825260208201526060918101829052818101919091525f805f8085806020019051810190613f6b919061588d565b9350935093509350613f7d8683614a67565b815160408051602081018690525f910160408051918152928152825160808101845260179490940b845263ffffffff90961660208401525081019390935260608301525092915050565b600d545f9081906601000000000000900463ffffffff16805b63ffffffff8116156141645763ffffffff7f00000000000000000000000000000000000000000000000000000000000000141661401d8284615561565b63ffffffff160361405a576040517fc3d2e25400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8082165f9081526011602090815260409182902082516060810184529054601781900b82527801000000000000000000000000000000000000000000000000810485168284018190527c01000000000000000000000000000000000000000000000000000000009091048516938201939093529088015190921611156140ec57505f958695509350505050565b856020015163ffffffff16816020015163ffffffff1614801561414157506060860151805161411d90600290615950565b8151811061412d5761412d615507565b602002602001015160170b815f015160170b145b15614153575060019590945092505050565b5061415d81615826565b9050613fe0565b505f9485945092505050565b5f8260170b121561418057505050565b5f6141a6633b9aca003a048560a0015163ffffffff16866080015163ffffffff16614ac7565b9050601036025f5a600c549091505f906141d19063ffffffff8716908690869062ffffff1686614aec565b90505f670de0b6b3a764000077ffffffffffffffffffffffffffffffffffffffffffffffff88168302335f9081526003602052604090205460e08b01519290910492506201000090046bffffffffffffffffffffffff9081169163ffffffff16633b9aca0002828401019081168211156142515750505050505050505050565b335f90815260036020526040902080546bffffffffffffffffffffffff90921662010000027fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff909216919091179055505050505050505050565b5f87876040516142bc929190615988565b6040519081900381206142d3918b90602001615997565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092525f808452908301819052909250905f5b8781101561449d575f60018587846020811061433d5761433d615507565b61434a91901a601b615863565b8c8c8681811061435c5761435c615507565b905060200201358b8b8781811061437557614375615507565b905060200201356040515f81526020016040526040516143b1949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa1580156143d1573d5f803e3d5ffd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81165f9081526004602090815290849020838501909452925460ff808216151580855261010090920416938301939093529095509250905061447e576040517fcd2467c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b840193505080600101905061431f565b5081827e0101010101010101010101010101010101010101010101010101010101010116146127af576040517f8044bb3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8260600151511115614538576040517fff6c220500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845f015160ff168260600151511161457c576040517f5765bdd700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b64ffffffffff83166020860152606082015180515f919061459f90600290615950565b815181106145af576145af615507565b602002602001015190508060170b7f000000000000000000000000000000000000000000000000000000000000000160170b138061461257507f00000000000000000000ffffffffffffffffffffffffffffffffffffffffffff60170b8160170b135b15614649576040517fca191b2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408601805190614659826159ad565b63ffffffff90811690915260408051606081018252601785900b815260208781015184168183019081524285168385019081528c85015186165f908152601190935293909120915182549151935185167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff949095167801000000000000000000000000000000000000000000000000027fffffffff0000000000000000000000000000000000000000000000000000000090921677ffffffffffffffffffffffffffffffffffffffffffffffff9091161717919091169190911790555081156147925760408087015163ffffffff166060880181905290517f8d530b9ddc4b318d28fdd4c3a21fcfecece54c1a72a824f262985b99afef009b905f90a25b85600d5f820151815f015f6101000a81548160ff021916908360ff1602179055506020820151815f0160016101000a81548164ffffffffff021916908364ffffffffff1602179055506040820151815f0160066101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01600a6101000a81548163ffffffff021916908363ffffffff1602179055506080820151815f01600e6101000a81548163ffffffff021916908363ffffffff16021790555060a0820151815f0160126101000a81548163ffffffff021916908363ffffffff16021790555060c0820151815f0160166101000a81548163ffffffff021916908363ffffffff16021790555060e0820151815f01601a6101000a81548163ffffffff021916908363ffffffff160217905550610100820151815f01601e6101000a81548160ff021916908315150217905550905050856040015163ffffffff167fc797025feeeaf2cd924c99e9205acb8ec04d5cad21c41ce637a38fb6dee6016a8233866020015187606001518860400151895f01518c8c6040516149399897969594939291906159cf565b60405180910390a2604080870151602080860151925163ffffffff93841681525f93909216917f0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac60271910160405180910390a3856040015163ffffffff168160170b7f0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f426040516149ca91815260200190565b60405180910390a36149e386604001518260170b614b39565b505050505050565b5f6149f782602061557e565b614a0284602061557e565b614a0e86610144615595565b614a189190615595565b614a229190615595565b614a2c905f615595565b9050368114611ede576040517fb4d895d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f81516020614a76919061557e565b614a819060a0615595565b614a8b905f615595565b905080835114612a64576040517fd4e1416000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8383811015614ad957600285850304015b614ae38184613aa6565b95945050505050565b5f81861015614b27576040517ffbf484ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50633b9aca0094039190910101020290565b60408051808201909152600f5473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff166020830152614b8d57505050565b5f614b99600185615561565b63ffffffff8181165f81815260116020526040808220549051602481019390935260170b60448301819052928816606483015260848201879052929350909190614c6b9060a401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbeed9b510000000000000000000000000000000000000000000000000000000017905286519087015163ffffffff16611388614ca5565b915050806149e3576040517f1c26714c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805a838110614cd257839003604081048103851015614cd2575f80885160208a015f8a8af19250600191505b5094509492505050565b5080545f8255905f5260205f20908101906113d09190614e11565b828054828255905f5260205f20908101928215614d6f579160200282015b82811115614d6f57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190614d15565b50614d7b929150614e11565b5090565b600483019183908215614d6f579160200282015f5b83821115614dd857835183826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302614d94565b8015614e085782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614dd8565b5050614d7b9291505b5b80821115614d7b575f8155600101614e12565b73ffffffffffffffffffffffffffffffffffffffff811681146113d0575f80fd5b5f60208284031215614e56575f80fd5b813561287e81614e25565b5f81518084525f5b81811015614e8557602081850181015186830182015201614e69565b505f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6110ad6020830184614e61565b5f8060408385031215614ee5575f80fd5b8235614ef081614e25565b91506020830135614f0081614e25565b809150509250929050565b63ffffffff811681146113d0575f80fd5b5f805f805f60a08688031215614f30575f80fd5b8535614f3b81614f0b565b94506020860135614f4b81614f0b565b93506040860135614f5b81614f0b565b92506060860135614f6b81614f0b565b9150608086013562ffffff81168114614f82575f80fd5b809150509295509295909350565b5f815180845260208085019450602084015f5b83811015614fd557815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614fa3565b509495945050505050565b602081525f6110ad6020830184614f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561506657615066614ff2565b604052919050565b5f82601f83011261507d575f80fd5b813567ffffffffffffffff81111561509757615097614ff2565b6150c860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161501f565b8181528460208386010111156150dc575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8060408385031215615109575f80fd5b823561511481614e25565b9150602083013567ffffffffffffffff81111561512f575f80fd5b61513b8582860161506e565b9150509250929050565b5f60208284031215615155575f80fd5b813569ffffffffffffffffffff8116811461287e575f80fd5b5f8083601f84011261517e575f80fd5b50813567ffffffffffffffff811115615195575f80fd5b6020830191508360208260051b85010111156151af575f80fd5b9250929050565b5f805f80604085870312156151c9575f80fd5b843567ffffffffffffffff808211156151e0575f80fd5b6151ec8883890161516e565b90965094506020870135915080821115615204575f80fd5b506152118782880161516e565b95989497509550505050565b5f6020828403121561522d575f80fd5b813561287e81614f0b565b5f805f805f805f8060e0898b03121561524f575f80fd5b606089018a81111561525f575f80fd5b8998503567ffffffffffffffff80821115615278575f80fd5b818b0191508b601f83011261528b575f80fd5b813581811115615299575f80fd5b8c60208285010111156152aa575f80fd5b6020830199508098505060808b01359150808211156152c7575f80fd5b6152d38c838d0161516e565b909750955060a08b01359150808211156152eb575f80fd5b506152f88b828c0161516e565b999c989b50969995989497949560c00135949350505050565b5f60208284031215615321575f80fd5b5035919050565b5f8060408385031215615339575f80fd5b823561534481614e25565b946020939093013593505050565b5f67ffffffffffffffff82111561536b5761536b614ff2565b5060051b60200190565b5f82601f830112615384575f80fd5b8135602061539961539483615352565b61501f565b8083825260208201915060208460051b8701019350868411156153ba575f80fd5b602086015b848110156153df5780356153d281614e25565b83529183019183016153bf565b509695505050505050565b803560ff811681146153fa575f80fd5b919050565b803567ffffffffffffffff811681146153fa575f80fd5b5f805f805f8060c0878903121561542b575f80fd5b863567ffffffffffffffff80821115615442575f80fd5b61544e8a838b01615375565b97506020890135915080821115615463575f80fd5b61546f8a838b01615375565b965061547d60408a016153ea565b95506060890135915080821115615492575f80fd5b61549e8a838b0161506e565b94506154ac60808a016153ff565b935060a08901359150808211156154c1575f80fd5b506154ce89828a0161506e565b9150509295509295509295565b5f80604083850312156154ec575f80fd5b82356154f781614e25565b91506020830135614f0081614f0b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b63ffffffff82811682821603908082111561322957613229615534565b80820281158282048414176110b0576110b0615534565b808201808211156110b0576110b0615534565b5f602082840312156155b8575f80fd5b5051919050565b5f602082840312156155cf575f80fd5b8151801515811461287e575f80fd5b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152816040820152818360608301375f818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b600181811c9082168061565b57607f821691505b602082108103613ab6577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b63ffffffff81811683821601908082111561322957613229615534565b818103818111156110b0576110b0615534565b8181035f83128015838313168383128216171561322957613229615534565b60ff818116838216029081169081811461322957613229615534565b5f61012063ffffffff808d1684528b6020850152808b1660408501525080606084015261572c8184018a614f90565b905082810360808401526157408189614f90565b905060ff871660a084015282810360c084015261575d8187614e61565b905067ffffffffffffffff851660e08401528281036101008401526157828185614e61565b9c9b505050505050505050505050565b5f6101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b1660408501528160608501526157d88285018b614f90565b915083820360808501526157ec828a614f90565b915060ff881660a085015283820360c08501526158098288614e61565b90861660e085015283810361010085015290506157828185614e61565b5f63ffffffff82168061583b5761583b615534565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60ff81811683821601908111156110b0576110b0615534565b8051601781900b81146153fa575f80fd5b5f805f80608085870312156158a0575f80fd5b84516158ab81614f0b565b809450506020808601519350604086015167ffffffffffffffff8111156158d0575f80fd5b8601601f810188136158e0575f80fd5b80516158ee61539482615352565b81815260059190911b8201830190838101908a83111561590c575f80fd5b928401925b82841015615931576159228461587c565b82529284019290840190615911565b80965050505050506159456060860161587c565b905092959194509250565b5f82615983577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b818382375f9101908152919050565b8281526080810160608360208401379392505050565b5f63ffffffff8083168181036159c5576159c5615534565b6001019392505050565b5f61010080830160178c60170b8552602073ffffffffffffffffffffffffffffffffffffffff8d16602087015263ffffffff8c1660408701528360608701528293508a518084526101208701945060208c0193505f5b81811015615a43578451840b86529482019493820193600101615a25565b50505050508281036080840152615a5a8188614e61565b915050615a6c60a083018660170b9052565b8360c0830152613b9360e083018464ffffffffff16905256fea164736f6c6343000818000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001ac2ee68b8d038c982c1e1f73f596927dd70de59000000000000000000000000000000000000000000000000000000000000000100000000000000000000ffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000058bbb01e021024a280f7a84f40de366df875957000000000000000000000000ddfcbf700abfbed39c2f69f3a427faee0efff9e2000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000001400000000000000000000000009df2b2ddfa0799d67fdee10fe7e5d349e6129775000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000009425443202f205553440000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : link (address): 0x1AC2EE68b8d038C982C1E1f73F596927dd70De59
Arg [1] : minAnswer_ (int192): 1
Arg [2] : maxAnswer_ (int192): 95780971304118053647396689196894323976171195136475135
Arg [3] : billingAccessController (address): 0x058BBB01E021024A280f7A84F40dE366Df875957
Arg [4] : requesterAccessController (address): 0xdDfCbf700AbFBEd39C2f69f3a427FaEe0efFF9E2
Arg [5] : decimals_ (uint8): 18
Arg [6] : description_ (string): BTC / USD
Arg [7] : secondaryProxy_ (address): 0x9dF2B2dDfa0799d67fDeE10Fe7e5D349E6129775
Arg [8] : cutoffTime_ (uint32): 10
Arg [9] : maxSyncIterations_ (uint32): 20
-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 0000000000000000000000001ac2ee68b8d038c982c1e1f73f596927dd70de59
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [2] : 00000000000000000000ffffffffffffffffffffffffffffffffffffffffffff
Arg [3] : 000000000000000000000000058bbb01e021024a280f7a84f40de366df875957
Arg [4] : 000000000000000000000000ddfcbf700abfbed39c2f69f3a427faee0efff9e2
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [7] : 0000000000000000000000009df2b2ddfa0799d67fdee10fe7e5d349e6129775
Arg [8] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000014
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000009
Arg [11] : 425443202f205553440000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in HYPE
Multichain Portfolio | 35 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.