Source Code
Overview
HYPE Balance
HYPE Value
$0.00Latest 6 from a total of 6 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Add Access | 25179854 | 4 days ago | IN | 0 HYPE | 0.00004615 | ||||
| Add Access | 25179793 | 4 days ago | IN | 0 HYPE | 0.00001185 | ||||
| Transfer Ownersh... | 25179671 | 4 days ago | IN | 0 HYPE | 0.0000049 | ||||
| Set Config | 25179549 | 4 days ago | IN | 0 HYPE | 0.00153513 | ||||
| Set Billing | 25179488 | 4 days ago | IN | 0 HYPE | 0.00001983 | ||||
| Set Payees | 25179427 | 4 days ago | IN | 0 HYPE | 0.00008421 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x4F2745A9...72F398b25 The constructor portion of the code might be different and could alter the actual behaviour of the contract
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
0x61012060405234801562000011575f80fd5b50604051620062fa380380620062fa8339810160408190526200003491620005c0565b33805f816200008a5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b5f80546001600160a01b0319166001600160a01b0384811691909117909155811615620000bc57620000bc81620001cd565b50506001805460ff60a01b1916600160a01b1790555060ff851661010052601789810b60805288900b60a0526001600160a01b0380841660c05263ffffffff821660e05260148054918c166001600160a01b0319909216821790556040515f907f4966a50c93f855342ccf6c5c0d358b85b91335b2acedc7da0932f691f351711a908290a36200014c8762000277565b6200015786620002f0565b620001635f806200036b565b6012805463ffffffff191663ffffffff84169081179091556040519081527fb24a681ce3399a408a89fd0c2b59dfc24bdad592b1c7ec7671cf060596c1c4d19060200160405180910390a16013620001bc85826200073b565b505050505050505050505062000807565b336001600160a01b03821603620002275760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000081565b600180546001600160a01b0319166001600160a01b038381169182179092555f8054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6015546001600160a01b039081169082168114620002ec57601580546001600160a01b0319166001600160a01b0384811691821790925560408051928416835260208301919091527f793cb73064f3c8cde7e187ae515511e6e56d1ee89bf08b82fa60fb70f8d4891291015b60405180910390a15b5050565b620002fa62000452565b6010546001600160a01b039081169082168114620002ec57601080546001600160a01b0319166001600160a01b0384811691821790925560408051928416835260208301919091527f27b89aede8b560578baaa25ee5ce3852c5eecad1e114b941bbd89e1eb4bae6349101620002e3565b6200037562000452565b60408051808201909152600f546001600160a01b03808216808452600160a01b90920463ffffffff1660208401528416141580620003c357508163ffffffff16816020015163ffffffff1614155b156200044d576040805180820182526001600160a01b0385811680835263ffffffff8681166020948501819052600f80546001600160c01b0319168417600160a01b830217905586518786015187519316835294820152909392909116917fb04e3a37abe9c0fcdfebdeae019a8e2b12ddf53f5d55ffb0caccc1bedaca1541910160405180910390a35b505050565b5f546001600160a01b03163314620004ad5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000081565b565b6001600160a01b0381168114620004c4575f80fd5b50565b8051601781900b8114620004d9575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f83011262000502575f80fd5b81516001600160401b03808211156200051f576200051f620004de565b604051601f8301601f19908116603f011681019082821181831017156200054a576200054a620004de565b816040528381526020925086602085880101111562000567575f80fd5b5f91505b838210156200058a57858201830151818301840152908201906200056b565b5f602085830101528094505050505092915050565b8051620004d981620004af565b805163ffffffff81168114620004d9575f80fd5b5f805f805f805f805f806101408b8d031215620005db575f80fd5b8a51620005e881620004af565b9950620005f860208c01620004c7565b98506200060860408c01620004c7565b975060608b01516200061a81620004af565b60808c01519097506200062d81620004af565b60a08c015190965060ff8116811462000644575f80fd5b60c08c01519095506001600160401b0381111562000660575f80fd5b6200066e8d828e01620004f2565b9450506200067f60e08c016200059f565b9250620006906101008c01620005ac565b9150620006a16101208c01620005ac565b90509295989b9194979a5092959850565b600181811c90821680620006c757607f821691505b602082108103620006e657634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200044d57805f5260205f20601f840160051c81016020851015620007135750805b601f840160051c820191505b8181101562000734575f81556001016200071f565b5050505050565b81516001600160401b03811115620007575762000757620004de565b6200076f81620007688454620006b2565b84620006ec565b602080601f831160018114620007a5575f84156200078d5750858301515b5f19600386901b1c1916600185901b178555620007ff565b5f85815260208120601f198616915b82811015620007d557888601518255948401946001909101908401620007b4565b5085821015620007f357878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b60805160a05160c05160e05161010051615a92620008685f395f61046301525f8181613d5f0152613ff301525f61319e01525f8181610504015281816120bc01526145ea01525f81816103990152818161209401526145bf0152615a925ff3fe608060405234801561000f575f80fd5b506004361061031a575f3560e01c80639bd2c0b1116101a8578063c4c92b37116100f3578063e5fe45771161009e578063eb5dcd6c11610079578063eb5dcd6c146108b8578063f2fde38b146108cb578063fbffd2c1146108de578063feaf968c146108f1575f80fd5b8063e5fe45771461083d578063e76d516814610887578063eb457163146108a5575f80fd5b8063dc7f0124116100ce578063dc7f0124146107dd578063e3d0e71214610802578063e4902f8214610815575f80fd5b8063c4c92b3714610799578063d09dc339146107b7578063daffc4b5146107bf575f80fd5b8063b17f2a6b11610153578063b633620c1161012e578063b633620c14610760578063ba0cb29e14610773578063c107532914610786575f80fd5b8063b17f2a6b14610727578063b1dc65a41461073a578063b5ab58dc1461074d575f80fd5b8063a118f24911610183578063a118f249146106d1578063afcb95d7146106e4578063b121e14714610714575f80fd5b80639bd2c0b11461064c5780639c849b30146106ab5780639e3ceeab146106be575f80fd5b80636b14daf8116102685780638205bf6a116102135780638da5cb5b116101ee5780638da5cb5b146105a157806398e5b12a146105df5780639a6fc8f514610602575f80fd5b80638205bf6a146105735780638823da6c1461057b5780638ac28d5a1461058e575f80fd5b806379ba50971161024357806379ba5097146105335780638038e4a11461053b57806381ff704814610543575f80fd5b80636b14daf8146104df57806370da2f67146105025780637284e4161461052b575f80fd5b80634fb17470116102c8578063643dc105116102a3578063643dc105146104af578063666cab8d146104c2578063668a0f02146104d7575f80fd5b80634fb174701461048d57806350d25bcd146104a057806354fd4d50146104a8575f80fd5b806322adbc78116102f857806322adbc781461039757806329937268146103c0578063313ce5671461045c575f80fd5b80630a7569831461031e5780630eafb25b14610328578063181f5a771461034e575b5f80fd5b6103266108f9565b005b61033b610336366004614e46565b610977565b6040519081526020015b60405180910390f35b61038a6040518060400160405280601481526020017f4475616c41676772656761746f7220312e302e3000000000000000000000000081525081565b6040516103459190614ec2565b7f000000000000000000000000000000000000000000000000000000000000000060170b61033b565b600d54600c546040805163ffffffff6e010000000000000000000000000000850481168252720100000000000000000000000000000000000085048116602083015276010000000000000000000000000000000000000000000085048116928201929092527a01000000000000000000000000000000000000000000000000000090930416606083015262ffffff16608082015260a001610345565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610345565b61032661049b366004614ed4565b610aa6565b61033b610d43565b600661033b565b6103266104bd366004614f1c565b610d6d565b6104ca611001565b6040516103459190614fe0565b61033b61106e565b6104f26104ed3660046150f8565b611082565b6040519015158152602001610345565b7f000000000000000000000000000000000000000000000000000000000000000060170b61033b565b61038a6110b6565b61032661113d565b61032661123e565b600e54600b546040805163ffffffff80851682526401000000009094049093166020840152820152606001610345565b61033b6112d1565b610326610589366004614e46565b61131b565b61032661059c366004614e46565b6113d3565b5f5473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610345565b6105e761143b565b60405169ffffffffffffffffffff9091168152602001610345565b610615610610366004615145565b6115b7565b6040805169ffffffffffffffffffff968716815260208101959095528401929092526060830152909116608082015260a001610345565b604080518082018252600f5473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff16602092830181905283519182529181019190915201610345565b6103266106b93660046151b6565b61167b565b6103266106cc366004614e46565b611896565b6103266106df366004614e46565b611947565b600b54600d54604080515f8152602081019390935261010090910460081c63ffffffff1690820152606001610345565b610326610722366004614e46565b6119f9565b61032661073536600461521d565b611aef565b610326610748366004615238565b611b5a565b61033b61075b366004615311565b611b75565b61033b61076e366004615311565b611bad565b610326610781366004615238565b611c05565b610326610794366004615328565b611c17565b60155473ffffffffffffffffffffffffffffffffffffffff166105ba565b61033b611ee4565b60105473ffffffffffffffffffffffffffffffffffffffff166105ba565b6001546104f29074010000000000000000000000000000000000000000900460ff1681565b610326610810366004615416565b611f96565b610828610823366004614e46565b6127bc565b60405163ffffffff9091168152602001610345565b610845612885565b6040805195865263ffffffff909416602086015260ff9092169284019290925260179190910b606083015267ffffffffffffffff16608082015260a001610345565b60145473ffffffffffffffffffffffffffffffffffffffff166105ba565b6103266108b33660046154db565b612932565b6103266108c6366004614ed4565b612a69565b6103266108d9366004614e46565b612bbe565b6103266108ec366004614e46565b612bcf565b610615612be0565b610901612c78565b60015474010000000000000000000000000000000000000000900460ff161561097557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f3be8a977a014527b50ae38adda80b56911c267328965c98ddc385d248f539638905f90a15b565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526003602090815260408083208151606081018352905460ff80821615158084526101008304909116948301949094526201000090046bffffffffffffffffffffffff1691810191909152906109e957505f92915050565b600d5460208201515f91760100000000000000000000000000000000000000000000900463ffffffff169060079060ff16601f8110610a2a57610a2a615507565b600881049190910154600d54610a60926007166004026101000a90910463ffffffff908116916601000000000000900416615561565b63ffffffff16610a70919061557e565b610a7e90633b9aca0061557e565b905081604001516bffffffffffffffffffffffff1681610a9e9190615595565b949350505050565b610aae612c78565b60145473ffffffffffffffffffffffffffffffffffffffff908116908316819003610ad857505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa158015610b40573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b6491906155a8565b50610b6d612cf8565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610bd7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfb91906155a8565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192509083169063a9059cbb906044016020604051808303815f875af1158015610c71573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c9591906155bf565b610ccb576040517f7725087a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86811691821790925560405190918416907f4966a50c93f855342ccf6c5c0d358b85b91335b2acedc7da0932f691f351711a905f90a350505b5050565b5f60115f610d4f613138565b63ffffffff16815260208101919091526040015f205460170b919050565b5f5473ffffffffffffffffffffffffffffffffffffffff16331480610e2657506015546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890610de79033905f9036906004016155de565b602060405180830381865afa158015610e02573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2691906155bf565b610e5c576040517f91ed77c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e64612cf8565b600d80547fffffffffffffffffffff0000000000000000ffffffffffffffffffffffffffff166e01000000000000000000000000000063ffffffff8881169182027fffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffff16929092177201000000000000000000000000000000000000888416908102919091177fffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffff167601000000000000000000000000000000000000000000008885169081027fffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffff16919091177a01000000000000000000000000000000000000000000000000000094881694850217909455600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000001662ffffff871690811790915560408051938452602084019290925290820193909352606081019190915260808101919091527f0bf184bf1bba9699114bdceddaf338a1b364252c5e497cc01918dde92031713f9060a00160405180910390a15050505050565b6060600680548060200260200160405190810160405280929190818152602001828054801561106457602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611039575b5050505050905090565b5f611077613138565b63ffffffff16905090565b5f61108d838361329d565b806110ad575073ffffffffffffffffffffffffffffffffffffffff831632145b90505b92915050565b6060601380546110c590615647565b80601f01602080910402602001604051908101604052809291908181526020018280546110f190615647565b80156110645780601f1061111357610100808354040283529160200191611064565b820191905f5260205f20905b81548152906001019060200180831161111f57509395945050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146111c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b5f8054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611246612c78565b60015474010000000000000000000000000000000000000000900460ff1661097557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556040517faebf329500988c6488a0074e5a0a9ff304561fc5c6fc877aeb1d59c8282c3480905f90a1565b5f60115f6112dd613138565b63ffffffff908116825260208201929092526040015f20547c0100000000000000000000000000000000000000000000000000000000900416919050565b611323612c78565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff16156113d05773ffffffffffffffffffffffffffffffffffffffff81165f8181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f3d68a6fce901d20453d1a7aa06bf3950302a735948037deb182a8db66df2a0d191015b60405180910390a15b50565b73ffffffffffffffffffffffffffffffffffffffff8181165f90815260166020526040902054163314611432576040517f2ab4a3db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d0816132f1565b5f805473ffffffffffffffffffffffffffffffffffffffff1633148015906114f957506010546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf8906114b89033905f9036906004016155de565b602060405180830381865afa1580156114d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114f791906155bf565b155b15611530576040517f4cdc445800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d54600b546040805191825263ffffffff6101008404600881901c8216602085015260ff811684840152915164ffffffffff9092169366010000000000009004169133917f41e3990591fd372502daa15842da15bc7f41c75309ab3ff4f56f1848c178825c9181900360600190a26115aa816001615692565b63ffffffff169250505090565b5f805f805f6115c4613138565b63ffffffff168669ffffffffffffffffffff1611156115f057505f935083925082915081905080611672565b5050505063ffffffff8281165f9081526011602090815260409182902082516060810184529054601781900b8083527801000000000000000000000000000000000000000000000000820486169383018490527c0100000000000000000000000000000000000000000000000000000000909104909416920182905284935090835b91939590929450565b611683612c78565b8281146116bc576040517f3d2f942900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8381101561188f575f8585838181106116d9576116d9615507565b90506020020160208101906116ee9190614e46565b90505f84848481811061170357611703615507565b90506020020160208101906117189190614e46565b73ffffffffffffffffffffffffffffffffffffffff8084165f9081526016602052604090205491925016801580158161177d57508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b156117b4576040517faeae062800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8481165f90815260166020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001685831690811790915590831614611880578273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b360405160405180910390a45b505050508060010190506116be565b5050505050565b61189e612c78565b60105473ffffffffffffffffffffffffffffffffffffffff9081169082168114610d3f57601080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560408051928416835260208301919091527f27b89aede8b560578baaa25ee5ce3852c5eecad1e114b941bbd89e1eb4bae63491015b60405180910390a15050565b61194f612c78565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff166113d05773ffffffffffffffffffffffffffffffffffffffff81165f8181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db491016113c7565b73ffffffffffffffffffffffffffffffffffffffff8181165f90815260176020526040902054163314611a58576040517f6599cbbe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8181165f8181526016602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556017909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b611af7612c78565b601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff83169081179091556040519081527fb24a681ce3399a408a89fd0c2b59dfc24bdad592b1c7ec7671cf060596c1c4d1906020016113c7565b611b6b88888888888888885f613546565b5050505050505050565b5f611b7e613138565b63ffffffff16821115611b9257505f919050565b5063ffffffff165f9081526011602052604090205460170b90565b5f611bb6613138565b63ffffffff16821115611bca57505f919050565b5063ffffffff9081165f908152601160205260409020547c010000000000000000000000000000000000000000000000000000000090041690565b611b6b88888888888888886001613546565b5f5473ffffffffffffffffffffffffffffffffffffffff163314801590611cd457506015546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890611c939033905f9036906004016155de565b602060405180830381865afa158015611cae573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cd291906155bf565b155b15611d0b576040517f91ed77c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611d146138c3565b6014546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529192505f9173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611d85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611da991906155a8565b905081811015611de5576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60145473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85611e17611e1186866156af565b87613aa6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303815f875af1158015611e84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ea891906155bf565b611ede576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6014546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f91829173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611f54573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7891906155a8565b90505f611f836138c3565b9050611f8f81836156c2565b9250505090565b611f9e612c78565b601f86511115611fda576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8451865114612015576040517f250a65b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85516120228560036156e1565b60ff161061205c576040517f20c9729a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120688460ff16613abc565b604080517f010000000000000000000000000000000000000000000000000000000000000060208201527f0000000000000000000000000000000000000000000000000000000000000000821b60218201527f000000000000000000000000000000000000000000000000000000000000000090911b60398201526051016040516020818303038152906040528051906020012083805190602001201461213b576040517fa8811dc600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c08101825267ffffffffffffffff8416815260ff86166020820152908101849052606081018290526080810187905260a08101869052600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ff1690556121a6612cf8565b6005545f5b81811015612291575f600582815481106121c7576121c7615507565b5f9182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff9092169350908490811061220057612200615507565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff948516835260048252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905594168252600390529190912080547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000169055506001016121ab565b5061229d60055f614cdc565b6122a860065f614cdc565b5f5b8260800151518110156125b55760045f846080015183815181106122d0576122d0615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f205460ff161561233a576040517f16c6131500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff1681525060045f8560800151848151811061236e5761236e615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040015f90812083518154949093015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff931515939093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093179190911790915560a0840151805160039291908490811061242357612423615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f205460ff161561248d576040517fd63d347400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180606001604052806001151581526020018260ff1681526020015f6bffffffffffffffffffffffff1681525060035f8560a0015184815181106124d5576124d5615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260409081015f20835181549385015194909201516bffffffffffffffffffffffff1662010000027fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff931515939093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009094169390931791909117929092161790556001016122aa565b50608082015180516125cf91600591602090910190614cf7565b5060a082015180516125e991600691602090910190614cf7565b506020820151600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600e80547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff811664010000000063ffffffff4381168202928317855590830481169360019390925f9261267a928692908216911617615692565b92506101000a81548163ffffffff021916908363ffffffff1602179055506126d74630600e5f9054906101000a900463ffffffff1663ffffffff1686608001518760a00151886020015189604001518a5f01518b60600151613af5565b600b819055600e54608085015160a08601516020870151604080890151895160608b015192517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e059861273d988b98919763ffffffff9091169691959094919391926156fd565b60405180910390a1600d546601000000000000900463ffffffff165f5b8460800151518110156127af5781600782601f811061277b5761277b615507565b600891828204019190066004026101000a81548163ffffffff021916908363ffffffff16021790555080600101905061275a565b5050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526003602090815260408083208151606081018352905460ff80821615158084526101008304909116948301949094526201000090046bffffffffffffffffffffffff16918101919091529061282e57505f92915050565b6007816020015160ff16601f811061284857612848615507565b600881049190910154600d5461287e926007166004026101000a90910463ffffffff908116916601000000000000900416615561565b9392505050565b5f808080803332146128c3576040517f74e2cd5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600b54600d5463ffffffff6601000000000000820481165f908152601160205260409020549296610100909204600881901c8216965064ffffffffff169450601783900b93507c010000000000000000000000000000000000000000000000000000000090920490911690565b61293a612c78565b60408051808201909152600f5473ffffffffffffffffffffffffffffffffffffffff8082168084527401000000000000000000000000000000000000000090920463ffffffff16602084015284161415806129a557508163ffffffff16816020015163ffffffff1614155b15612a645760408051808201825273ffffffffffffffffffffffffffffffffffffffff85811680835263ffffffff8681166020948501819052600f80547fffffffffffffffff00000000000000000000000000000000000000000000000016841774010000000000000000000000000000000000000000830217905586518786015187519316835294820152909392909116917fb04e3a37abe9c0fcdfebdeae019a8e2b12ddf53f5d55ffb0caccc1bedaca1541910160405180910390a35b505050565b73ffffffffffffffffffffffffffffffffffffffff8281165f90815260166020526040902054163314612ac8576040517fb97d016a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163303612b17576040517f79df0c6600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8083165f90815260176020526040902080548383167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117909255909116908114612a645760405173ffffffffffffffffffffffffffffffffffffffff8084169133918616907f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e38367905f90a4505050565b612bc6612c78565b6113d081613ba0565b612bd7612c78565b6113d081613c94565b5f805f805f80612bee613138565b63ffffffff9081165f8181526011602090815260409182902082516060810184529054601781900b8083527801000000000000000000000000000000000000000000000000820487169383018490527c0100000000000000000000000000000000000000000000000000000000909104909516920182905291999298509096509450879350915050565b5f5473ffffffffffffffffffffffffffffffffffffffff163314610975576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016111ba565b601454600d54604080516103e081019182905273ffffffffffffffffffffffffffffffffffffffff90931692660100000000000090920463ffffffff16915f91600790601f908285855b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d42579050505050505090505f6006805480602002602001604051908101604052809291908181526020018280548015612de757602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612dbc575b505050505090505f5b815181101561312a575f60035f848481518110612e0f57612e0f615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f0160029054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff1690505f60035f858581518110612e9157612e91615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f0160026101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505f8483601f8110612f1557612f15615507565b6020020151600d5490870363ffffffff9081169250760100000000000000000000000000000000000000000000909104168102633b9aca00028201801561311f575f60165f878781518110612f6c57612f6c615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683529082019290925260409081015f205490517fa9059cbb00000000000000000000000000000000000000000000000000000000815290821660048201819052602482018590529250908a169063a9059cbb906044016020604051808303815f875af1158015613002573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061302691906155bf565b61305c576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b878786601f811061306f5761306f615507565b602002019063ffffffff16908163ffffffff16815250508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff168787815181106130c6576130c6615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c8560405161311591815260200190565b60405180910390a4505b505050600101612df0565b5061188f600783601f614d7f565b600d545f9063ffffffff660100000000000082048116916a010000000000000000000081049091169060ff7e010000000000000000000000000000000000000000000000000000000000009091041673ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036132305760125463ffffffff8381165f90815260116020526040902054429261320d92908116917c0100000000000000000000000000000000000000000000000000000000900416615692565b63ffffffff16101561322957613221613d35565b935050505090565b5092915050565b8163ffffffff168363ffffffff160361329557808015613285575063ffffffff8084165f908152601160205260409020547c010000000000000000000000000000000000000000000000000000000090041642145b1561329557613221600184615561565b509092915050565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526002602052604081205460ff16806110ad57505060015474010000000000000000000000000000000000000000900460ff161592915050565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600360209081526040918290208251606081018452905460ff80821615158084526101008304909116938301939093526201000090046bffffffffffffffffffffffff169281019290925261335f575050565b5f61336983610977565b90508015612a645773ffffffffffffffffffffffffffffffffffffffff8381165f90815260166020526040908190205460145491517fa9059cbb000000000000000000000000000000000000000000000000000000008152908316600482018190526024820185905292919091169063a9059cbb906044016020604051808303815f875af11580156133fd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061342191906155bf565b613457576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5f0160069054906101000a900463ffffffff166007846020015160ff16601f811061348657613486615507565b6008810491909101805460079092166004026101000a63ffffffff81810219909316939092169190910291909117905573ffffffffffffffffffffffffffffffffffffffff8481165f8181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff169055601454915186815291841693851692917fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c910160405180910390a450505050565b5f5a90506135568a898887613e1d565b5f6135958a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250613f3192505050565b6040805161012081018252600d5460ff808216835261010080830464ffffffffff1660208501526601000000000000830463ffffffff908116958501959095526a01000000000000000000008304851660608501526e01000000000000000000000000000083048516608085015272010000000000000000000000000000000000008304851660a08501527601000000000000000000000000000000000000000000008304851660c08501527a010000000000000000000000000000000000000000000000000000830490941660e08401527e01000000000000000000000000000000000000000000000000000000000000909104161515918101919091529091508315613781575f806136a884613fc7565b91509150811561377e578063ffffffff16836060015163ffffffff16106136fb576040517ff803a2ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff166a010000000000000000000063ffffffff8416908102919091179091556040517f8d530b9ddc4b318d28fdd4c3a21fcfecece54c1a72a824f262985b99afef009b905f90a261377483855f015187614170565b50505050506138b8565b50505b602081810151908d01359064ffffffffff80831691161415806137a75750816101000151155b1561381a57816020015164ffffffffff168164ffffffffff16116137f7576040517ff803a2ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6138078d8d8d8d8d8d8d8d6142ab565b613815828e358386896144f8565b613858565b600d54604051660100000000000090910463ffffffff16907fda2435684a37fba6f7841e49b59e6ad975e462bbebd28ec9da4ed9746a6992be905f90a25b600d80547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000008715150217905582516138b390839086614170565b505050505b505050505050505050565b5f80600680548060200260200160405190810160405280929190818152602001828054801561392657602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116138fb575b50508351600d54604080516103e08101918290529697509195660100000000000090910463ffffffff1694505f93509150600790601f908285855b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411613961579050505050505090505f5b838110156139e9578181601f81106139c0576139c0615507565b60200201516139cf9084615561565b6139df9063ffffffff1687615595565b95506001016139a6565b50600d54613a1b90760100000000000000000000000000000000000000000000900463ffffffff16633b9aca0061557e565b613a25908661557e565b94505f5b83811015613a9e5760035f868381518110613a4657613a46615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f2054613a94906201000090046bffffffffffffffffffffffff1687615595565b9550600101613a29565b505050505090565b5f81831015613ab65750816110b0565b50919050565b5f81116113d0576040517f39d1a4d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f808a8a8a8a8a8a8a8a8a604051602001613b1899989796959493929190615792565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150505b9998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613c1f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016111ba565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182179092555f8054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60155473ffffffffffffffffffffffffffffffffffffffff9081169082168114610d3f57601580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560408051928416835260208301919091527f793cb73064f3c8cde7e187ae515511e6e56d1ee89bf08b82fa60fb70f8d48912910161193b565b600d545f906601000000000000900463ffffffff16805b63ffffffff811615613dff5763ffffffff7f000000000000000000000000000000000000000000000000000000000000000016613d898284615561565b63ffffffff1614613dff5760125463ffffffff8281165f908152601160205260409020544292613ddd92908116917c0100000000000000000000000000000000000000000000000000000000900416615692565b63ffffffff161015613def5792915050565b613df881615826565b9050613d4c565b5050600d546a0100000000000000000000900463ffffffff16919050565b335f9081526003602052604090205460ff16613e65576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b54843514613ea1576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613eac8383836149eb565b600d54613ebd9060ff166001615863565b60ff168214613ef8576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808214611ede576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516080810182525f80825260208201526060918101829052818101919091525f805f8085806020019051810190613f6b919061588d565b9350935093509350613f7d8683614a67565b815160408051602081018690525f910160408051918152928152825160808101845260179490940b845263ffffffff90961660208401525081019390935260608301525092915050565b600d545f9081906601000000000000900463ffffffff16805b63ffffffff8116156141645763ffffffff7f00000000000000000000000000000000000000000000000000000000000000001661401d8284615561565b63ffffffff160361405a576040517fc3d2e25400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8082165f9081526011602090815260409182902082516060810184529054601781900b82527801000000000000000000000000000000000000000000000000810485168284018190527c01000000000000000000000000000000000000000000000000000000009091048516938201939093529088015190921611156140ec57505f958695509350505050565b856020015163ffffffff16816020015163ffffffff1614801561414157506060860151805161411d90600290615950565b8151811061412d5761412d615507565b602002602001015160170b815f015160170b145b15614153575060019590945092505050565b5061415d81615826565b9050613fe0565b505f9485945092505050565b5f8260170b121561418057505050565b5f6141a6633b9aca003a048560a0015163ffffffff16866080015163ffffffff16614ac7565b9050601036025f5a600c549091505f906141d19063ffffffff8716908690869062ffffff1686614aec565b90505f670de0b6b3a764000077ffffffffffffffffffffffffffffffffffffffffffffffff88168302335f9081526003602052604090205460e08b01519290910492506201000090046bffffffffffffffffffffffff9081169163ffffffff16633b9aca0002828401019081168211156142515750505050505050505050565b335f90815260036020526040902080546bffffffffffffffffffffffff90921662010000027fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff909216919091179055505050505050505050565b5f87876040516142bc929190615988565b6040519081900381206142d3918b90602001615997565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092525f808452908301819052909250905f5b8781101561449d575f60018587846020811061433d5761433d615507565b61434a91901a601b615863565b8c8c8681811061435c5761435c615507565b905060200201358b8b8781811061437557614375615507565b905060200201356040515f81526020016040526040516143b1949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa1580156143d1573d5f803e3d5ffd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81165f9081526004602090815290849020838501909452925460ff808216151580855261010090920416938301939093529095509250905061447e576040517fcd2467c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b840193505080600101905061431f565b5081827e0101010101010101010101010101010101010101010101010101010101010116146127af576040517f8044bb3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8260600151511115614538576040517fff6c220500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845f015160ff168260600151511161457c576040517f5765bdd700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b64ffffffffff83166020860152606082015180515f919061459f90600290615950565b815181106145af576145af615507565b602002602001015190508060170b7f000000000000000000000000000000000000000000000000000000000000000060170b138061461257507f000000000000000000000000000000000000000000000000000000000000000060170b8160170b135b15614649576040517fca191b2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408601805190614659826159ad565b63ffffffff90811690915260408051606081018252601785900b815260208781015184168183019081524285168385019081528c85015186165f908152601190935293909120915182549151935185167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff949095167801000000000000000000000000000000000000000000000000027fffffffff0000000000000000000000000000000000000000000000000000000090921677ffffffffffffffffffffffffffffffffffffffffffffffff9091161717919091169190911790555081156147925760408087015163ffffffff166060880181905290517f8d530b9ddc4b318d28fdd4c3a21fcfecece54c1a72a824f262985b99afef009b905f90a25b85600d5f820151815f015f6101000a81548160ff021916908360ff1602179055506020820151815f0160016101000a81548164ffffffffff021916908364ffffffffff1602179055506040820151815f0160066101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01600a6101000a81548163ffffffff021916908363ffffffff1602179055506080820151815f01600e6101000a81548163ffffffff021916908363ffffffff16021790555060a0820151815f0160126101000a81548163ffffffff021916908363ffffffff16021790555060c0820151815f0160166101000a81548163ffffffff021916908363ffffffff16021790555060e0820151815f01601a6101000a81548163ffffffff021916908363ffffffff160217905550610100820151815f01601e6101000a81548160ff021916908315150217905550905050856040015163ffffffff167fc797025feeeaf2cd924c99e9205acb8ec04d5cad21c41ce637a38fb6dee6016a8233866020015187606001518860400151895f01518c8c6040516149399897969594939291906159cf565b60405180910390a2604080870151602080860151925163ffffffff93841681525f93909216917f0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac60271910160405180910390a3856040015163ffffffff168160170b7f0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f426040516149ca91815260200190565b60405180910390a36149e386604001518260170b614b39565b505050505050565b5f6149f782602061557e565b614a0284602061557e565b614a0e86610144615595565b614a189190615595565b614a229190615595565b614a2c905f615595565b9050368114611ede576040517fb4d895d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f81516020614a76919061557e565b614a819060a0615595565b614a8b905f615595565b905080835114612a64576040517fd4e1416000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8383811015614ad957600285850304015b614ae38184613aa6565b95945050505050565b5f81861015614b27576040517ffbf484ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50633b9aca0094039190910101020290565b60408051808201909152600f5473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff166020830152614b8d57505050565b5f614b99600185615561565b63ffffffff8181165f81815260116020526040808220549051602481019390935260170b60448301819052928816606483015260848201879052929350909190614c6b9060a401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbeed9b510000000000000000000000000000000000000000000000000000000017905286519087015163ffffffff16611388614ca5565b915050806149e3576040517f1c26714c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805a838110614cd257839003604081048103851015614cd2575f80885160208a015f8a8af19250600191505b5094509492505050565b5080545f8255905f5260205f20908101906113d09190614e11565b828054828255905f5260205f20908101928215614d6f579160200282015b82811115614d6f57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190614d15565b50614d7b929150614e11565b5090565b600483019183908215614d6f579160200282015f5b83821115614dd857835183826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302614d94565b8015614e085782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614dd8565b5050614d7b9291505b5b80821115614d7b575f8155600101614e12565b73ffffffffffffffffffffffffffffffffffffffff811681146113d0575f80fd5b5f60208284031215614e56575f80fd5b813561287e81614e25565b5f81518084525f5b81811015614e8557602081850181015186830182015201614e69565b505f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6110ad6020830184614e61565b5f8060408385031215614ee5575f80fd5b8235614ef081614e25565b91506020830135614f0081614e25565b809150509250929050565b63ffffffff811681146113d0575f80fd5b5f805f805f60a08688031215614f30575f80fd5b8535614f3b81614f0b565b94506020860135614f4b81614f0b565b93506040860135614f5b81614f0b565b92506060860135614f6b81614f0b565b9150608086013562ffffff81168114614f82575f80fd5b809150509295509295909350565b5f815180845260208085019450602084015f5b83811015614fd557815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614fa3565b509495945050505050565b602081525f6110ad6020830184614f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561506657615066614ff2565b604052919050565b5f82601f83011261507d575f80fd5b813567ffffffffffffffff81111561509757615097614ff2565b6150c860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161501f565b8181528460208386010111156150dc575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8060408385031215615109575f80fd5b823561511481614e25565b9150602083013567ffffffffffffffff81111561512f575f80fd5b61513b8582860161506e565b9150509250929050565b5f60208284031215615155575f80fd5b813569ffffffffffffffffffff8116811461287e575f80fd5b5f8083601f84011261517e575f80fd5b50813567ffffffffffffffff811115615195575f80fd5b6020830191508360208260051b85010111156151af575f80fd5b9250929050565b5f805f80604085870312156151c9575f80fd5b843567ffffffffffffffff808211156151e0575f80fd5b6151ec8883890161516e565b90965094506020870135915080821115615204575f80fd5b506152118782880161516e565b95989497509550505050565b5f6020828403121561522d575f80fd5b813561287e81614f0b565b5f805f805f805f8060e0898b03121561524f575f80fd5b606089018a81111561525f575f80fd5b8998503567ffffffffffffffff80821115615278575f80fd5b818b0191508b601f83011261528b575f80fd5b813581811115615299575f80fd5b8c60208285010111156152aa575f80fd5b6020830199508098505060808b01359150808211156152c7575f80fd5b6152d38c838d0161516e565b909750955060a08b01359150808211156152eb575f80fd5b506152f88b828c0161516e565b999c989b50969995989497949560c00135949350505050565b5f60208284031215615321575f80fd5b5035919050565b5f8060408385031215615339575f80fd5b823561534481614e25565b946020939093013593505050565b5f67ffffffffffffffff82111561536b5761536b614ff2565b5060051b60200190565b5f82601f830112615384575f80fd5b8135602061539961539483615352565b61501f565b8083825260208201915060208460051b8701019350868411156153ba575f80fd5b602086015b848110156153df5780356153d281614e25565b83529183019183016153bf565b509695505050505050565b803560ff811681146153fa575f80fd5b919050565b803567ffffffffffffffff811681146153fa575f80fd5b5f805f805f8060c0878903121561542b575f80fd5b863567ffffffffffffffff80821115615442575f80fd5b61544e8a838b01615375565b97506020890135915080821115615463575f80fd5b61546f8a838b01615375565b965061547d60408a016153ea565b95506060890135915080821115615492575f80fd5b61549e8a838b0161506e565b94506154ac60808a016153ff565b935060a08901359150808211156154c1575f80fd5b506154ce89828a0161506e565b9150509295509295509295565b5f80604083850312156154ec575f80fd5b82356154f781614e25565b91506020830135614f0081614f0b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b63ffffffff82811682821603908082111561322957613229615534565b80820281158282048414176110b0576110b0615534565b808201808211156110b0576110b0615534565b5f602082840312156155b8575f80fd5b5051919050565b5f602082840312156155cf575f80fd5b8151801515811461287e575f80fd5b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152816040820152818360608301375f818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b600181811c9082168061565b57607f821691505b602082108103613ab6577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b63ffffffff81811683821601908082111561322957613229615534565b818103818111156110b0576110b0615534565b8181035f83128015838313168383128216171561322957613229615534565b60ff818116838216029081169081811461322957613229615534565b5f61012063ffffffff808d1684528b6020850152808b1660408501525080606084015261572c8184018a614f90565b905082810360808401526157408189614f90565b905060ff871660a084015282810360c084015261575d8187614e61565b905067ffffffffffffffff851660e08401528281036101008401526157828185614e61565b9c9b505050505050505050505050565b5f6101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b1660408501528160608501526157d88285018b614f90565b915083820360808501526157ec828a614f90565b915060ff881660a085015283820360c08501526158098288614e61565b90861660e085015283810361010085015290506157828185614e61565b5f63ffffffff82168061583b5761583b615534565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60ff81811683821601908111156110b0576110b0615534565b8051601781900b81146153fa575f80fd5b5f805f80608085870312156158a0575f80fd5b84516158ab81614f0b565b809450506020808601519350604086015167ffffffffffffffff8111156158d0575f80fd5b8601601f810188136158e0575f80fd5b80516158ee61539482615352565b81815260059190911b8201830190838101908a83111561590c575f80fd5b928401925b82841015615931576159228461587c565b82529284019290840190615911565b80965050505050506159456060860161587c565b905092959194509250565b5f82615983577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b818382375f9101908152919050565b8281526080810160608360208401379392505050565b5f63ffffffff8083168181036159c5576159c5615534565b6001019392505050565b5f61010080830160178c60170b8552602073ffffffffffffffffffffffffffffffffffffffff8d16602087015263ffffffff8c1660408701528360608701528293508a518084526101208701945060208c0193505f5b81811015615a43578451840b86529482019493820193600101615a25565b50505050508281036080840152615a5a8188614e61565b915050615a6c60a083018660170b9052565b8360c0830152613b9360e083018464ffffffffff16905256fea164736f6c6343000818000a0000000000000000000000001ac2ee68b8d038c982c1e1f73f596927dd70de59000000000000000000000000000000000000000000000000000000000000000100000000000000000000ffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000058bbb01e021024a280f7a84f40de366df875957000000000000000000000000ddfcbf700abfbed39c2f69f3a427faee0efff9e20000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000014000000000000000000000000010a63e1e163a8a899280b7b0240c82b8f3896beb000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000a55534f4c202f2055534400000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561000f575f80fd5b506004361061031a575f3560e01c80639bd2c0b1116101a8578063c4c92b37116100f3578063e5fe45771161009e578063eb5dcd6c11610079578063eb5dcd6c146108b8578063f2fde38b146108cb578063fbffd2c1146108de578063feaf968c146108f1575f80fd5b8063e5fe45771461083d578063e76d516814610887578063eb457163146108a5575f80fd5b8063dc7f0124116100ce578063dc7f0124146107dd578063e3d0e71214610802578063e4902f8214610815575f80fd5b8063c4c92b3714610799578063d09dc339146107b7578063daffc4b5146107bf575f80fd5b8063b17f2a6b11610153578063b633620c1161012e578063b633620c14610760578063ba0cb29e14610773578063c107532914610786575f80fd5b8063b17f2a6b14610727578063b1dc65a41461073a578063b5ab58dc1461074d575f80fd5b8063a118f24911610183578063a118f249146106d1578063afcb95d7146106e4578063b121e14714610714575f80fd5b80639bd2c0b11461064c5780639c849b30146106ab5780639e3ceeab146106be575f80fd5b80636b14daf8116102685780638205bf6a116102135780638da5cb5b116101ee5780638da5cb5b146105a157806398e5b12a146105df5780639a6fc8f514610602575f80fd5b80638205bf6a146105735780638823da6c1461057b5780638ac28d5a1461058e575f80fd5b806379ba50971161024357806379ba5097146105335780638038e4a11461053b57806381ff704814610543575f80fd5b80636b14daf8146104df57806370da2f67146105025780637284e4161461052b575f80fd5b80634fb17470116102c8578063643dc105116102a3578063643dc105146104af578063666cab8d146104c2578063668a0f02146104d7575f80fd5b80634fb174701461048d57806350d25bcd146104a057806354fd4d50146104a8575f80fd5b806322adbc78116102f857806322adbc781461039757806329937268146103c0578063313ce5671461045c575f80fd5b80630a7569831461031e5780630eafb25b14610328578063181f5a771461034e575b5f80fd5b6103266108f9565b005b61033b610336366004614e46565b610977565b6040519081526020015b60405180910390f35b61038a6040518060400160405280601481526020017f4475616c41676772656761746f7220312e302e3000000000000000000000000081525081565b6040516103459190614ec2565b7f000000000000000000000000000000000000000000000000000000000000000160170b61033b565b600d54600c546040805163ffffffff6e010000000000000000000000000000850481168252720100000000000000000000000000000000000085048116602083015276010000000000000000000000000000000000000000000085048116928201929092527a01000000000000000000000000000000000000000000000000000090930416606083015262ffffff16608082015260a001610345565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000012168152602001610345565b61032661049b366004614ed4565b610aa6565b61033b610d43565b600661033b565b6103266104bd366004614f1c565b610d6d565b6104ca611001565b6040516103459190614fe0565b61033b61106e565b6104f26104ed3660046150f8565b611082565b6040519015158152602001610345565b7f00000000000000000000ffffffffffffffffffffffffffffffffffffffffffff60170b61033b565b61038a6110b6565b61032661113d565b61032661123e565b600e54600b546040805163ffffffff80851682526401000000009094049093166020840152820152606001610345565b61033b6112d1565b610326610589366004614e46565b61131b565b61032661059c366004614e46565b6113d3565b5f5473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610345565b6105e761143b565b60405169ffffffffffffffffffff9091168152602001610345565b610615610610366004615145565b6115b7565b6040805169ffffffffffffffffffff968716815260208101959095528401929092526060830152909116608082015260a001610345565b604080518082018252600f5473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff16602092830181905283519182529181019190915201610345565b6103266106b93660046151b6565b61167b565b6103266106cc366004614e46565b611896565b6103266106df366004614e46565b611947565b600b54600d54604080515f8152602081019390935261010090910460081c63ffffffff1690820152606001610345565b610326610722366004614e46565b6119f9565b61032661073536600461521d565b611aef565b610326610748366004615238565b611b5a565b61033b61075b366004615311565b611b75565b61033b61076e366004615311565b611bad565b610326610781366004615238565b611c05565b610326610794366004615328565b611c17565b60155473ffffffffffffffffffffffffffffffffffffffff166105ba565b61033b611ee4565b60105473ffffffffffffffffffffffffffffffffffffffff166105ba565b6001546104f29074010000000000000000000000000000000000000000900460ff1681565b610326610810366004615416565b611f96565b610828610823366004614e46565b6127bc565b60405163ffffffff9091168152602001610345565b610845612885565b6040805195865263ffffffff909416602086015260ff9092169284019290925260179190910b606083015267ffffffffffffffff16608082015260a001610345565b60145473ffffffffffffffffffffffffffffffffffffffff166105ba565b6103266108b33660046154db565b612932565b6103266108c6366004614ed4565b612a69565b6103266108d9366004614e46565b612bbe565b6103266108ec366004614e46565b612bcf565b610615612be0565b610901612c78565b60015474010000000000000000000000000000000000000000900460ff161561097557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1690556040517f3be8a977a014527b50ae38adda80b56911c267328965c98ddc385d248f539638905f90a15b565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526003602090815260408083208151606081018352905460ff80821615158084526101008304909116948301949094526201000090046bffffffffffffffffffffffff1691810191909152906109e957505f92915050565b600d5460208201515f91760100000000000000000000000000000000000000000000900463ffffffff169060079060ff16601f8110610a2a57610a2a615507565b600881049190910154600d54610a60926007166004026101000a90910463ffffffff908116916601000000000000900416615561565b63ffffffff16610a70919061557e565b610a7e90633b9aca0061557e565b905081604001516bffffffffffffffffffffffff1681610a9e9190615595565b949350505050565b610aae612c78565b60145473ffffffffffffffffffffffffffffffffffffffff908116908316819003610ad857505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8416906370a0823190602401602060405180830381865afa158015610b40573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b6491906155a8565b50610b6d612cf8565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610bd7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bfb91906155a8565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018390529192509083169063a9059cbb906044016020604051808303815f875af1158015610c71573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c9591906155bf565b610ccb576040517f7725087a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86811691821790925560405190918416907f4966a50c93f855342ccf6c5c0d358b85b91335b2acedc7da0932f691f351711a905f90a350505b5050565b5f60115f610d4f613138565b63ffffffff16815260208101919091526040015f205460170b919050565b5f5473ffffffffffffffffffffffffffffffffffffffff16331480610e2657506015546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890610de79033905f9036906004016155de565b602060405180830381865afa158015610e02573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2691906155bf565b610e5c576040517f91ed77c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e64612cf8565b600d80547fffffffffffffffffffff0000000000000000ffffffffffffffffffffffffffff166e01000000000000000000000000000063ffffffff8881169182027fffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffff16929092177201000000000000000000000000000000000000888416908102919091177fffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffff167601000000000000000000000000000000000000000000008885169081027fffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffff16919091177a01000000000000000000000000000000000000000000000000000094881694850217909455600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000001662ffffff871690811790915560408051938452602084019290925290820193909352606081019190915260808101919091527f0bf184bf1bba9699114bdceddaf338a1b364252c5e497cc01918dde92031713f9060a00160405180910390a15050505050565b6060600680548060200260200160405190810160405280929190818152602001828054801561106457602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611039575b5050505050905090565b5f611077613138565b63ffffffff16905090565b5f61108d838361329d565b806110ad575073ffffffffffffffffffffffffffffffffffffffff831632145b90505b92915050565b6060601380546110c590615647565b80601f01602080910402602001604051908101604052809291908181526020018280546110f190615647565b80156110645780601f1061111357610100808354040283529160200191611064565b820191905f5260205f20905b81548152906001019060200180831161111f57509395945050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146111c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b5f8054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611246612c78565b60015474010000000000000000000000000000000000000000900460ff1661097557600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790556040517faebf329500988c6488a0074e5a0a9ff304561fc5c6fc877aeb1d59c8282c3480905f90a1565b5f60115f6112dd613138565b63ffffffff908116825260208201929092526040015f20547c0100000000000000000000000000000000000000000000000000000000900416919050565b611323612c78565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff16156113d05773ffffffffffffffffffffffffffffffffffffffff81165f8181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f3d68a6fce901d20453d1a7aa06bf3950302a735948037deb182a8db66df2a0d191015b60405180910390a15b50565b73ffffffffffffffffffffffffffffffffffffffff8181165f90815260166020526040902054163314611432576040517f2ab4a3db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113d0816132f1565b5f805473ffffffffffffffffffffffffffffffffffffffff1633148015906114f957506010546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf8906114b89033905f9036906004016155de565b602060405180830381865afa1580156114d3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114f791906155bf565b155b15611530576040517f4cdc445800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d54600b546040805191825263ffffffff6101008404600881901c8216602085015260ff811684840152915164ffffffffff9092169366010000000000009004169133917f41e3990591fd372502daa15842da15bc7f41c75309ab3ff4f56f1848c178825c9181900360600190a26115aa816001615692565b63ffffffff169250505090565b5f805f805f6115c4613138565b63ffffffff168669ffffffffffffffffffff1611156115f057505f935083925082915081905080611672565b5050505063ffffffff8281165f9081526011602090815260409182902082516060810184529054601781900b8083527801000000000000000000000000000000000000000000000000820486169383018490527c0100000000000000000000000000000000000000000000000000000000909104909416920182905284935090835b91939590929450565b611683612c78565b8281146116bc576040517f3d2f942900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8381101561188f575f8585838181106116d9576116d9615507565b90506020020160208101906116ee9190614e46565b90505f84848481811061170357611703615507565b90506020020160208101906117189190614e46565b73ffffffffffffffffffffffffffffffffffffffff8084165f9081526016602052604090205491925016801580158161177d57508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b156117b4576040517faeae062800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8481165f90815260166020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001685831690811790915590831614611880578273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b360405160405180910390a45b505050508060010190506116be565b5050505050565b61189e612c78565b60105473ffffffffffffffffffffffffffffffffffffffff9081169082168114610d3f57601080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560408051928416835260208301919091527f27b89aede8b560578baaa25ee5ce3852c5eecad1e114b941bbd89e1eb4bae63491015b60405180910390a15050565b61194f612c78565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff166113d05773ffffffffffffffffffffffffffffffffffffffff81165f8181526002602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db491016113c7565b73ffffffffffffffffffffffffffffffffffffffff8181165f90815260176020526040902054163314611a58576040517f6599cbbe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8181165f8181526016602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556017909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b611af7612c78565b601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff83169081179091556040519081527fb24a681ce3399a408a89fd0c2b59dfc24bdad592b1c7ec7671cf060596c1c4d1906020016113c7565b611b6b88888888888888885f613546565b5050505050505050565b5f611b7e613138565b63ffffffff16821115611b9257505f919050565b5063ffffffff165f9081526011602052604090205460170b90565b5f611bb6613138565b63ffffffff16821115611bca57505f919050565b5063ffffffff9081165f908152601160205260409020547c010000000000000000000000000000000000000000000000000000000090041690565b611b6b88888888888888886001613546565b5f5473ffffffffffffffffffffffffffffffffffffffff163314801590611cd457506015546040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b14daf890611c939033905f9036906004016155de565b602060405180830381865afa158015611cae573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cd291906155bf565b155b15611d0b576040517f91ed77c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611d146138c3565b6014546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529192505f9173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611d85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611da991906155a8565b905081811015611de5576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60145473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85611e17611e1186866156af565b87613aa6565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044016020604051808303815f875af1158015611e84573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ea891906155bf565b611ede576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6014546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f91829173ffffffffffffffffffffffffffffffffffffffff909116906370a0823190602401602060405180830381865afa158015611f54573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7891906155a8565b90505f611f836138c3565b9050611f8f81836156c2565b9250505090565b611f9e612c78565b601f86511115611fda576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8451865114612015576040517f250a65b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85516120228560036156e1565b60ff161061205c576040517f20c9729a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6120688460ff16613abc565b604080517f010000000000000000000000000000000000000000000000000000000000000060208201527f0000000000000000000000000000000000000000000000000000000000000001821b60218201527f00000000000000000000ffffffffffffffffffffffffffffffffffffffffffff90911b60398201526051016040516020818303038152906040528051906020012083805190602001201461213b576040517fa8811dc600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c08101825267ffffffffffffffff8416815260ff86166020820152908101849052606081018290526080810187905260a08101869052600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ff1690556121a6612cf8565b6005545f5b81811015612291575f600582815481106121c7576121c7615507565b5f9182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff9092169350908490811061220057612200615507565b5f91825260208083209091015473ffffffffffffffffffffffffffffffffffffffff948516835260048252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905594168252600390529190912080547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000169055506001016121ab565b5061229d60055f614cdc565b6122a860065f614cdc565b5f5b8260800151518110156125b55760045f846080015183815181106122d0576122d0615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f205460ff161561233a576040517f16c6131500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff1681525060045f8560800151848151811061236e5761236e615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040015f90812083518154949093015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff931515939093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093179190911790915560a0840151805160039291908490811061242357612423615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f205460ff161561248d576040517fd63d347400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180606001604052806001151581526020018260ff1681526020015f6bffffffffffffffffffffffff1681525060035f8560a0015184815181106124d5576124d5615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260409081015f20835181549385015194909201516bffffffffffffffffffffffff1662010000027fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff931515939093167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009094169390931791909117929092161790556001016122aa565b50608082015180516125cf91600591602090910190614cf7565b5060a082015180516125e991600691602090910190614cf7565b506020820151600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600e80547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff811664010000000063ffffffff4381168202928317855590830481169360019390925f9261267a928692908216911617615692565b92506101000a81548163ffffffff021916908363ffffffff1602179055506126d74630600e5f9054906101000a900463ffffffff1663ffffffff1686608001518760a00151886020015189604001518a5f01518b60600151613af5565b600b819055600e54608085015160a08601516020870151604080890151895160608b015192517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e059861273d988b98919763ffffffff9091169691959094919391926156fd565b60405180910390a1600d546601000000000000900463ffffffff165f5b8460800151518110156127af5781600782601f811061277b5761277b615507565b600891828204019190066004026101000a81548163ffffffff021916908363ffffffff16021790555080600101905061275a565b5050505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526003602090815260408083208151606081018352905460ff80821615158084526101008304909116948301949094526201000090046bffffffffffffffffffffffff16918101919091529061282e57505f92915050565b6007816020015160ff16601f811061284857612848615507565b600881049190910154600d5461287e926007166004026101000a90910463ffffffff908116916601000000000000900416615561565b9392505050565b5f808080803332146128c3576040517f74e2cd5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600b54600d5463ffffffff6601000000000000820481165f908152601160205260409020549296610100909204600881901c8216965064ffffffffff169450601783900b93507c010000000000000000000000000000000000000000000000000000000090920490911690565b61293a612c78565b60408051808201909152600f5473ffffffffffffffffffffffffffffffffffffffff8082168084527401000000000000000000000000000000000000000090920463ffffffff16602084015284161415806129a557508163ffffffff16816020015163ffffffff1614155b15612a645760408051808201825273ffffffffffffffffffffffffffffffffffffffff85811680835263ffffffff8681166020948501819052600f80547fffffffffffffffff00000000000000000000000000000000000000000000000016841774010000000000000000000000000000000000000000830217905586518786015187519316835294820152909392909116917fb04e3a37abe9c0fcdfebdeae019a8e2b12ddf53f5d55ffb0caccc1bedaca1541910160405180910390a35b505050565b73ffffffffffffffffffffffffffffffffffffffff8281165f90815260166020526040902054163314612ac8576040517fb97d016a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81163303612b17576040517f79df0c6600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8083165f90815260176020526040902080548383167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117909255909116908114612a645760405173ffffffffffffffffffffffffffffffffffffffff8084169133918616907f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e38367905f90a4505050565b612bc6612c78565b6113d081613ba0565b612bd7612c78565b6113d081613c94565b5f805f805f80612bee613138565b63ffffffff9081165f8181526011602090815260409182902082516060810184529054601781900b8083527801000000000000000000000000000000000000000000000000820487169383018490527c0100000000000000000000000000000000000000000000000000000000909104909516920182905291999298509096509450879350915050565b5f5473ffffffffffffffffffffffffffffffffffffffff163314610975576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016111ba565b601454600d54604080516103e081019182905273ffffffffffffffffffffffffffffffffffffffff90931692660100000000000090920463ffffffff16915f91600790601f908285855b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612d42579050505050505090505f6006805480602002602001604051908101604052809291908181526020018280548015612de757602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612dbc575b505050505090505f5b815181101561312a575f60035f848481518110612e0f57612e0f615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f0160029054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff1690505f60035f858581518110612e9157612e91615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f0160026101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505f8483601f8110612f1557612f15615507565b6020020151600d5490870363ffffffff9081169250760100000000000000000000000000000000000000000000909104168102633b9aca00028201801561311f575f60165f878781518110612f6c57612f6c615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff90811683529082019290925260409081015f205490517fa9059cbb00000000000000000000000000000000000000000000000000000000815290821660048201819052602482018590529250908a169063a9059cbb906044016020604051808303815f875af1158015613002573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061302691906155bf565b61305c576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b878786601f811061306f5761306f615507565b602002019063ffffffff16908163ffffffff16815250508873ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff168787815181106130c6576130c6615507565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c8560405161311591815260200190565b60405180910390a4505b505050600101612df0565b5061188f600783601f614d7f565b600d545f9063ffffffff660100000000000082048116916a010000000000000000000081049091169060ff7e010000000000000000000000000000000000000000000000000000000000009091041673ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000010a63e1e163a8a899280b7b0240c82b8f3896beb1633036132305760125463ffffffff8381165f90815260116020526040902054429261320d92908116917c0100000000000000000000000000000000000000000000000000000000900416615692565b63ffffffff16101561322957613221613d35565b935050505090565b5092915050565b8163ffffffff168363ffffffff160361329557808015613285575063ffffffff8084165f908152601160205260409020547c010000000000000000000000000000000000000000000000000000000090041642145b1561329557613221600184615561565b509092915050565b73ffffffffffffffffffffffffffffffffffffffff82165f9081526002602052604081205460ff16806110ad57505060015474010000000000000000000000000000000000000000900460ff161592915050565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600360209081526040918290208251606081018452905460ff80821615158084526101008304909116938301939093526201000090046bffffffffffffffffffffffff169281019290925261335f575050565b5f61336983610977565b90508015612a645773ffffffffffffffffffffffffffffffffffffffff8381165f90815260166020526040908190205460145491517fa9059cbb000000000000000000000000000000000000000000000000000000008152908316600482018190526024820185905292919091169063a9059cbb906044016020604051808303815f875af11580156133fd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061342191906155bf565b613457576040517f356680b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5f0160069054906101000a900463ffffffff166007846020015160ff16601f811061348657613486615507565b6008810491909101805460079092166004026101000a63ffffffff81810219909316939092169190910291909117905573ffffffffffffffffffffffffffffffffffffffff8481165f8181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff169055601454915186815291841693851692917fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c910160405180910390a450505050565b5f5a90506135568a898887613e1d565b5f6135958a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250613f3192505050565b6040805161012081018252600d5460ff808216835261010080830464ffffffffff1660208501526601000000000000830463ffffffff908116958501959095526a01000000000000000000008304851660608501526e01000000000000000000000000000083048516608085015272010000000000000000000000000000000000008304851660a08501527601000000000000000000000000000000000000000000008304851660c08501527a010000000000000000000000000000000000000000000000000000830490941660e08401527e01000000000000000000000000000000000000000000000000000000000000909104161515918101919091529091508315613781575f806136a884613fc7565b91509150811561377e578063ffffffff16836060015163ffffffff16106136fb576040517ff803a2ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff166a010000000000000000000063ffffffff8416908102919091179091556040517f8d530b9ddc4b318d28fdd4c3a21fcfecece54c1a72a824f262985b99afef009b905f90a261377483855f015187614170565b50505050506138b8565b50505b602081810151908d01359064ffffffffff80831691161415806137a75750816101000151155b1561381a57816020015164ffffffffff168164ffffffffff16116137f7576040517ff803a2ca00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6138078d8d8d8d8d8d8d8d6142ab565b613815828e358386896144f8565b613858565b600d54604051660100000000000090910463ffffffff16907fda2435684a37fba6f7841e49b59e6ad975e462bbebd28ec9da4ed9746a6992be905f90a25b600d80547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000008715150217905582516138b390839086614170565b505050505b505050505050505050565b5f80600680548060200260200160405190810160405280929190818152602001828054801561392657602002820191905f5260205f20905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116138fb575b50508351600d54604080516103e08101918290529697509195660100000000000090910463ffffffff1694505f93509150600790601f908285855b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411613961579050505050505090505f5b838110156139e9578181601f81106139c0576139c0615507565b60200201516139cf9084615561565b6139df9063ffffffff1687615595565b95506001016139a6565b50600d54613a1b90760100000000000000000000000000000000000000000000900463ffffffff16633b9aca0061557e565b613a25908661557e565b94505f5b83811015613a9e5760035f868381518110613a4657613a46615507565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040015f2054613a94906201000090046bffffffffffffffffffffffff1687615595565b9550600101613a29565b505050505090565b5f81831015613ab65750816110b0565b50919050565b5f81116113d0576040517f39d1a4d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f808a8a8a8a8a8a8a8a8a604051602001613b1899989796959493929190615792565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150505b9998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613c1f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016111ba565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182179092555f8054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60155473ffffffffffffffffffffffffffffffffffffffff9081169082168114610d3f57601580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84811691821790925560408051928416835260208301919091527f793cb73064f3c8cde7e187ae515511e6e56d1ee89bf08b82fa60fb70f8d48912910161193b565b600d545f906601000000000000900463ffffffff16805b63ffffffff811615613dff5763ffffffff7f000000000000000000000000000000000000000000000000000000000000001416613d898284615561565b63ffffffff1614613dff5760125463ffffffff8281165f908152601160205260409020544292613ddd92908116917c0100000000000000000000000000000000000000000000000000000000900416615692565b63ffffffff161015613def5792915050565b613df881615826565b9050613d4c565b5050600d546a0100000000000000000000900463ffffffff16919050565b335f9081526003602052604090205460ff16613e65576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b54843514613ea1576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613eac8383836149eb565b600d54613ebd9060ff166001615863565b60ff168214613ef8576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808214611ede576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516080810182525f80825260208201526060918101829052818101919091525f805f8085806020019051810190613f6b919061588d565b9350935093509350613f7d8683614a67565b815160408051602081018690525f910160408051918152928152825160808101845260179490940b845263ffffffff90961660208401525081019390935260608301525092915050565b600d545f9081906601000000000000900463ffffffff16805b63ffffffff8116156141645763ffffffff7f00000000000000000000000000000000000000000000000000000000000000141661401d8284615561565b63ffffffff160361405a576040517fc3d2e25400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8082165f9081526011602090815260409182902082516060810184529054601781900b82527801000000000000000000000000000000000000000000000000810485168284018190527c01000000000000000000000000000000000000000000000000000000009091048516938201939093529088015190921611156140ec57505f958695509350505050565b856020015163ffffffff16816020015163ffffffff1614801561414157506060860151805161411d90600290615950565b8151811061412d5761412d615507565b602002602001015160170b815f015160170b145b15614153575060019590945092505050565b5061415d81615826565b9050613fe0565b505f9485945092505050565b5f8260170b121561418057505050565b5f6141a6633b9aca003a048560a0015163ffffffff16866080015163ffffffff16614ac7565b9050601036025f5a600c549091505f906141d19063ffffffff8716908690869062ffffff1686614aec565b90505f670de0b6b3a764000077ffffffffffffffffffffffffffffffffffffffffffffffff88168302335f9081526003602052604090205460e08b01519290910492506201000090046bffffffffffffffffffffffff9081169163ffffffff16633b9aca0002828401019081168211156142515750505050505050505050565b335f90815260036020526040902080546bffffffffffffffffffffffff90921662010000027fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff909216919091179055505050505050505050565b5f87876040516142bc929190615988565b6040519081900381206142d3918b90602001615997565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092525f808452908301819052909250905f5b8781101561449d575f60018587846020811061433d5761433d615507565b61434a91901a601b615863565b8c8c8681811061435c5761435c615507565b905060200201358b8b8781811061437557614375615507565b905060200201356040515f81526020016040526040516143b1949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa1580156143d1573d5f803e3d5ffd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81165f9081526004602090815290849020838501909452925460ff808216151580855261010090920416938301939093529095509250905061447e576040517fcd2467c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b840193505080600101905061431f565b5081827e0101010101010101010101010101010101010101010101010101010101010116146127af576040517f8044bb3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8260600151511115614538576040517fff6c220500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845f015160ff168260600151511161457c576040517f5765bdd700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b64ffffffffff83166020860152606082015180515f919061459f90600290615950565b815181106145af576145af615507565b602002602001015190508060170b7f000000000000000000000000000000000000000000000000000000000000000160170b138061461257507f00000000000000000000ffffffffffffffffffffffffffffffffffffffffffff60170b8160170b135b15614649576040517fca191b2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408601805190614659826159ad565b63ffffffff90811690915260408051606081018252601785900b815260208781015184168183019081524285168385019081528c85015186165f908152601190935293909120915182549151935185167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff949095167801000000000000000000000000000000000000000000000000027fffffffff0000000000000000000000000000000000000000000000000000000090921677ffffffffffffffffffffffffffffffffffffffffffffffff9091161717919091169190911790555081156147925760408087015163ffffffff166060880181905290517f8d530b9ddc4b318d28fdd4c3a21fcfecece54c1a72a824f262985b99afef009b905f90a25b85600d5f820151815f015f6101000a81548160ff021916908360ff1602179055506020820151815f0160016101000a81548164ffffffffff021916908364ffffffffff1602179055506040820151815f0160066101000a81548163ffffffff021916908363ffffffff1602179055506060820151815f01600a6101000a81548163ffffffff021916908363ffffffff1602179055506080820151815f01600e6101000a81548163ffffffff021916908363ffffffff16021790555060a0820151815f0160126101000a81548163ffffffff021916908363ffffffff16021790555060c0820151815f0160166101000a81548163ffffffff021916908363ffffffff16021790555060e0820151815f01601a6101000a81548163ffffffff021916908363ffffffff160217905550610100820151815f01601e6101000a81548160ff021916908315150217905550905050856040015163ffffffff167fc797025feeeaf2cd924c99e9205acb8ec04d5cad21c41ce637a38fb6dee6016a8233866020015187606001518860400151895f01518c8c6040516149399897969594939291906159cf565b60405180910390a2604080870151602080860151925163ffffffff93841681525f93909216917f0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac60271910160405180910390a3856040015163ffffffff168160170b7f0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f426040516149ca91815260200190565b60405180910390a36149e386604001518260170b614b39565b505050505050565b5f6149f782602061557e565b614a0284602061557e565b614a0e86610144615595565b614a189190615595565b614a229190615595565b614a2c905f615595565b9050368114611ede576040517fb4d895d500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f81516020614a76919061557e565b614a819060a0615595565b614a8b905f615595565b905080835114612a64576040517fd4e1416000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8383811015614ad957600285850304015b614ae38184613aa6565b95945050505050565b5f81861015614b27576040517ffbf484ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50633b9aca0094039190910101020290565b60408051808201909152600f5473ffffffffffffffffffffffffffffffffffffffff81168083527401000000000000000000000000000000000000000090910463ffffffff166020830152614b8d57505050565b5f614b99600185615561565b63ffffffff8181165f81815260116020526040808220549051602481019390935260170b60448301819052928816606483015260848201879052929350909190614c6b9060a401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fbeed9b510000000000000000000000000000000000000000000000000000000017905286519087015163ffffffff16611388614ca5565b915050806149e3576040517f1c26714c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805a838110614cd257839003604081048103851015614cd2575f80885160208a015f8a8af19250600191505b5094509492505050565b5080545f8255905f5260205f20908101906113d09190614e11565b828054828255905f5260205f20908101928215614d6f579160200282015b82811115614d6f57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190614d15565b50614d7b929150614e11565b5090565b600483019183908215614d6f579160200282015f5b83821115614dd857835183826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302614d94565b8015614e085782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614dd8565b5050614d7b9291505b5b80821115614d7b575f8155600101614e12565b73ffffffffffffffffffffffffffffffffffffffff811681146113d0575f80fd5b5f60208284031215614e56575f80fd5b813561287e81614e25565b5f81518084525f5b81811015614e8557602081850181015186830182015201614e69565b505f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6110ad6020830184614e61565b5f8060408385031215614ee5575f80fd5b8235614ef081614e25565b91506020830135614f0081614e25565b809150509250929050565b63ffffffff811681146113d0575f80fd5b5f805f805f60a08688031215614f30575f80fd5b8535614f3b81614f0b565b94506020860135614f4b81614f0b565b93506040860135614f5b81614f0b565b92506060860135614f6b81614f0b565b9150608086013562ffffff81168114614f82575f80fd5b809150509295509295909350565b5f815180845260208085019450602084015f5b83811015614fd557815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614fa3565b509495945050505050565b602081525f6110ad6020830184614f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561506657615066614ff2565b604052919050565b5f82601f83011261507d575f80fd5b813567ffffffffffffffff81111561509757615097614ff2565b6150c860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161501f565b8181528460208386010111156150dc575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8060408385031215615109575f80fd5b823561511481614e25565b9150602083013567ffffffffffffffff81111561512f575f80fd5b61513b8582860161506e565b9150509250929050565b5f60208284031215615155575f80fd5b813569ffffffffffffffffffff8116811461287e575f80fd5b5f8083601f84011261517e575f80fd5b50813567ffffffffffffffff811115615195575f80fd5b6020830191508360208260051b85010111156151af575f80fd5b9250929050565b5f805f80604085870312156151c9575f80fd5b843567ffffffffffffffff808211156151e0575f80fd5b6151ec8883890161516e565b90965094506020870135915080821115615204575f80fd5b506152118782880161516e565b95989497509550505050565b5f6020828403121561522d575f80fd5b813561287e81614f0b565b5f805f805f805f8060e0898b03121561524f575f80fd5b606089018a81111561525f575f80fd5b8998503567ffffffffffffffff80821115615278575f80fd5b818b0191508b601f83011261528b575f80fd5b813581811115615299575f80fd5b8c60208285010111156152aa575f80fd5b6020830199508098505060808b01359150808211156152c7575f80fd5b6152d38c838d0161516e565b909750955060a08b01359150808211156152eb575f80fd5b506152f88b828c0161516e565b999c989b50969995989497949560c00135949350505050565b5f60208284031215615321575f80fd5b5035919050565b5f8060408385031215615339575f80fd5b823561534481614e25565b946020939093013593505050565b5f67ffffffffffffffff82111561536b5761536b614ff2565b5060051b60200190565b5f82601f830112615384575f80fd5b8135602061539961539483615352565b61501f565b8083825260208201915060208460051b8701019350868411156153ba575f80fd5b602086015b848110156153df5780356153d281614e25565b83529183019183016153bf565b509695505050505050565b803560ff811681146153fa575f80fd5b919050565b803567ffffffffffffffff811681146153fa575f80fd5b5f805f805f8060c0878903121561542b575f80fd5b863567ffffffffffffffff80821115615442575f80fd5b61544e8a838b01615375565b97506020890135915080821115615463575f80fd5b61546f8a838b01615375565b965061547d60408a016153ea565b95506060890135915080821115615492575f80fd5b61549e8a838b0161506e565b94506154ac60808a016153ff565b935060a08901359150808211156154c1575f80fd5b506154ce89828a0161506e565b9150509295509295509295565b5f80604083850312156154ec575f80fd5b82356154f781614e25565b91506020830135614f0081614f0b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b63ffffffff82811682821603908082111561322957613229615534565b80820281158282048414176110b0576110b0615534565b808201808211156110b0576110b0615534565b5f602082840312156155b8575f80fd5b5051919050565b5f602082840312156155cf575f80fd5b8151801515811461287e575f80fd5b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152816040820152818360608301375f818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b600181811c9082168061565b57607f821691505b602082108103613ab6577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b63ffffffff81811683821601908082111561322957613229615534565b818103818111156110b0576110b0615534565b8181035f83128015838313168383128216171561322957613229615534565b60ff818116838216029081169081811461322957613229615534565b5f61012063ffffffff808d1684528b6020850152808b1660408501525080606084015261572c8184018a614f90565b905082810360808401526157408189614f90565b905060ff871660a084015282810360c084015261575d8187614e61565b905067ffffffffffffffff851660e08401528281036101008401526157828185614e61565b9c9b505050505050505050505050565b5f6101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b1660408501528160608501526157d88285018b614f90565b915083820360808501526157ec828a614f90565b915060ff881660a085015283820360c08501526158098288614e61565b90861660e085015283810361010085015290506157828185614e61565b5f63ffffffff82168061583b5761583b615534565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60ff81811683821601908111156110b0576110b0615534565b8051601781900b81146153fa575f80fd5b5f805f80608085870312156158a0575f80fd5b84516158ab81614f0b565b809450506020808601519350604086015167ffffffffffffffff8111156158d0575f80fd5b8601601f810188136158e0575f80fd5b80516158ee61539482615352565b81815260059190911b8201830190838101908a83111561590c575f80fd5b928401925b82841015615931576159228461587c565b82529284019290840190615911565b80965050505050506159456060860161587c565b905092959194509250565b5f82615983577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500490565b818382375f9101908152919050565b8281526080810160608360208401379392505050565b5f63ffffffff8083168181036159c5576159c5615534565b6001019392505050565b5f61010080830160178c60170b8552602073ffffffffffffffffffffffffffffffffffffffff8d16602087015263ffffffff8c1660408701528360608701528293508a518084526101208701945060208c0193505f5b81811015615a43578451840b86529482019493820193600101615a25565b50505050508281036080840152615a5a8188614e61565b915050615a6c60a083018660170b9052565b8360c0830152613b9360e083018464ffffffffff16905256fea164736f6c6343000818000a
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.