编译器
0.8.24+commit.e11b9ed9
文件 1 的 16:AccessControllerInterface.sol
pragma solidity ^0.8.0;
interface AccessControllerInterface {
function hasAccess(address user, bytes calldata data) external view returns (bool);
}
文件 2 的 16:AggregatorInterface.sol
pragma solidity ^0.8.0;
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);
}
文件 3 的 16:AggregatorV2V3Interface.sol
pragma solidity ^0.8.0;
import {AggregatorInterface} from "./AggregatorInterface.sol";
import {AggregatorV3Interface} from "./AggregatorV3Interface.sol";
interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
文件 4 的 16:AggregatorV3Interface.sol
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
文件 5 的 16:AggregatorValidatorInterface.sol
pragma solidity ^0.8.0;
interface AggregatorValidatorInterface {
function validate(
uint256 previousRoundId,
int256 previousAnswer,
uint256 currentRoundId,
int256 currentAnswer
) external returns (bool);
}
文件 6 的 16:CallWithExactGas.sol
pragma solidity ^0.8.0;
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;
function _callWithExactGas(
bytes memory payload,
address target,
uint256 gasLimit,
uint16 gasForCallExactCheck
) internal returns (bool success) {
assembly {
if iszero(extcodesize(target)) {
mstore(0x0, NO_CONTRACT_SIG)
revert(0x0, 0x4)
}
let g := gas()
if lt(g, gasForCallExactCheck) {
mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG)
revert(0x0, 0x4)
}
g := sub(g, gasForCallExactCheck)
if iszero(gt(sub(g, div(g, 64)), gasLimit)) {
mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG)
revert(0x0, 0x4)
}
success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
}
return success;
}
function _callWithExactGasSafeReturnData(
bytes memory payload,
address target,
uint256 gasLimit,
uint16 gasForCallExactCheck,
uint16 maxReturnBytes
) internal returns (bool success, bytes memory retData, uint256 gasUsed) {
retData = new bytes(maxReturnBytes);
assembly {
if iszero(extcodesize(target)) {
mstore(0x0, NO_CONTRACT_SIG)
revert(0x0, 0x4)
}
let g := gas()
if lt(g, gasForCallExactCheck) {
mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG)
revert(0x0, 0x4)
}
g := sub(g, gasForCallExactCheck)
if iszero(gt(sub(g, div(g, 64)), gasLimit)) {
mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG)
revert(0x0, 0x4)
}
let gasBeforeCall := gas()
success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
gasUsed := sub(gasBeforeCall, gas())
let toCopy := returndatasize()
if gt(toCopy, maxReturnBytes) {
toCopy := maxReturnBytes
}
mstore(retData, toCopy)
returndatacopy(add(retData, 0x20), 0x0, toCopy)
}
return (success, retData, gasUsed);
}
function _callWithExactGasEvenIfTargetIsNoContract(
bytes memory payload,
address target,
uint256 gasLimit,
uint16 gasForCallExactCheck
) internal returns (bool success, bool sufficientGas) {
assembly {
let g := gas()
if iszero(lt(g, gasForCallExactCheck)) {
g := sub(g, gasForCallExactCheck)
if gt(sub(g, div(g, 64)), gasLimit) {
success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0)
sufficientGas := true
}
}
}
return (success, sufficientGas);
}
}
文件 7 的 16:ConfirmedOwner.sol
pragma solidity ^0.8.0;
import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}
文件 8 的 16:ConfirmedOwnerWithProposal.sol
pragma solidity ^0.8.0;
import {IOwnable} from "../interfaces/IOwnable.sol";
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) {
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
function acceptOwnership() external override {
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);
}
function owner() public view override returns (address) {
return s_owner;
}
function _transferOwnership(address to) private {
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
function _validateOwnership() internal view {
require(msg.sender == s_owner, "Only callable by owner");
}
modifier onlyOwner() {
_validateOwnership();
_;
}
}
文件 9 的 16:DualAggregator.sol
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";
contract DualAggregator is OCR2Abstract, AggregatorV2V3Interface, SimpleReadAccessController {
string public constant override typeAndVersion = "DualAggregator 1.0.0";
struct Transmitter {
bool active;
uint8 index;
uint96 paymentJuels;
}
struct Signer {
bool active;
uint8 index;
}
struct HotVars {
uint8 f;
uint40 latestEpochAndRound;
uint32 latestAggregatorRoundId;
uint32 latestSecondaryRoundId;
uint32 maximumGasPriceGwei;
uint32 reasonableGasPriceGwei;
uint32 observationPaymentGjuels;
uint32 transmissionPaymentGjuels;
bool isLatestSecondary;
}
mapping(address transmitterAddress => Transmitter transmitter) internal s_transmitters;
mapping(address signerAddress => Signer signer) internal s_signers;
address[] internal s_signersList;
address[] internal s_transmittersList;
uint32[MAX_NUM_ORACLES] internal s_rewardFromAggregatorRoundId;
bytes32 internal s_latestConfigDigest;
uint24 internal s_accountingGas;
HotVars internal s_hotVars;
int192 internal immutable i_minAnswer;
int192 internal immutable i_maxAnswer;
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_;
}
struct SetConfigArgs {
uint64 offchainConfigVersion;
uint8 f;
bytes onchainConfig;
bytes offchainConfig;
address[] signers;
address[] transmitters;
}
error FMustBePositive();
error TooManyOracles();
error OracleLengthMismatch();
error FaultyOracleFTooHigh();
error InvalidOnChainConfig();
error RepeatedSignerAddress();
error RepeatedTransmitterAddress();
uint32 internal s_configCount;
uint32 internal s_latestConfigBlockNumber;
function _requirePositiveF(
uint256 f
) internal pure virtual {
if (f <= 0) {
revert FMustBePositive();
}
}
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), 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();
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;
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;
}
}
function latestConfigDetails()
external
view
override
returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest)
{
return (s_configCount, s_latestConfigBlockNumber, s_latestConfigDigest);
}
function getTransmitters() external view returns (address[] memory) {
return s_transmittersList;
}
function minAnswer() public view returns (int256) {
return i_minAnswer;
}
function maxAnswer() public view returns (int256) {
return i_maxAnswer;
}
struct ValidatorConfig {
AggregatorValidatorInterface validator;
uint32 gasLimit;
}
event ValidatorConfigSet(
AggregatorValidatorInterface indexed previousValidator,
uint32 previousGasLimit,
AggregatorValidatorInterface indexed currentValidator,
uint32 currentGasLimit
);
error InsufficientGas();
uint16 private constant CALL_WITH_EXACT_GAS_CUSHION = 5_000;
ValidatorConfig private s_validatorConfig;
function getValidatorConfig() external view returns (AggregatorValidatorInterface validator, uint32 gasLimit) {
ValidatorConfig memory vc = s_validatorConfig;
return (vc.validator, vc.gasLimit);
}
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);
}
}
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();
}
}
AccessControllerInterface internal s_requesterAccessController;
event RequesterAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);
event RoundRequested(address indexed requester, bytes32 configDigest, uint32 epoch, uint8 round);
error OnlyOwnerAndRequesterCanCall();
function getRequesterAccessController() external view returns (AccessControllerInterface) {
return s_requesterAccessController;
}
function setRequesterAccessController(
AccessControllerInterface requesterAccessController
) public onlyOwner {
AccessControllerInterface oldController = s_requesterAccessController;
if (requesterAccessController != oldController) {
s_requesterAccessController = AccessControllerInterface(requesterAccessController);
emit RequesterAccessControllerSet(oldController, requesterAccessController);
}
}
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;
}
struct Report {
int192 juelsPerFeeCoin;
uint32 observationsTimestamp;
bytes observers;
int192[] observations;
}
struct Transmission {
int192 answer;
uint32 observationsTimestamp;
uint32 recordedTimestamp;
}
event SecondaryRoundIdUpdated(uint32 indexed secondaryRoundId);
event PrimaryFeedUnlocked(uint32 indexed primaryRoundId);
event CutoffTimeSet(uint32 cutoffTime);
error MaxSyncIterationsReached();
mapping(uint32 aggregatorRoundId => Transmission transmission) internal s_transmissions;
address internal immutable i_secondaryProxy;
uint32 internal s_cutoffTime;
uint32 internal immutable i_maxSyncIterations;
function setCutoffTime(
uint32 _cutoffTime
) external onlyOwner {
s_cutoffTime = _cutoffTime;
emit CutoffTimeSet(_cutoffTime);
}
function _doesReportExist(
Report memory report
) internal view returns (bool exist, uint32 roundId) {
uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
for (uint32 round_ = latestAggregatorRoundId; round_ > 0; --round_) {
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);
}
function _getSyncPrimaryRound() internal view returns (uint32 roundId) {
uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
for (uint32 round_ = latestAggregatorRoundId; round_ > 0; --round_) {
if (latestAggregatorRoundId - round_ == i_maxSyncIterations) {
break;
}
if (s_transmissions[round_].recordedTimestamp + s_cutoffTime < block.timestamp) {
return round_;
}
}
return s_hotVars.latestSecondaryRoundId;
}
function _getLatestRound() internal view returns (uint32) {
uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
uint32 latestSecondaryRoundId = s_hotVars.latestSecondaryRoundId;
bool isLatestSecondary = s_hotVars.isLatestSecondary;
if (msg.sender == i_secondaryProxy) {
if (s_transmissions[latestSecondaryRoundId].recordedTimestamp + s_cutoffTime < block.timestamp) {
return _getSyncPrimaryRound();
}
return latestSecondaryRoundId;
}
if (latestAggregatorRoundId == latestSecondaryRoundId) {
if (isLatestSecondary && s_transmissions[latestAggregatorRoundId].recordedTimestamp == block.timestamp) {
return latestAggregatorRoundId - 1;
}
}
return latestAggregatorRoundId;
}
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();
uint256 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = 4
+ 32 * 3
+ 32
+ 32
+ 32
+ 32
+ 32
+ 32
+ 32
+ 0;
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 {
mstore(observers, numObservations)
}
return Report({
observationsTimestamp: observationsTimestamp,
observers: observers,
observations: observations,
juelsPerFeeCoin: juelsPerFeeCoin
});
}
function _requireExpectedMsgDataLength(uint256 reportLength, uint256 rsLength, uint256 ssLength) private pure {
uint256 expected = TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT + reportLength
+ rsLength * 32
+ ssLength * 32
+ 0;
if (msg.data.length != expected) {
revert CalldataLengthMismatch();
}
}
function transmit(
bytes32[3] calldata reportContext,
bytes calldata report,
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 rawVs
) external override {
_transmit(reportContext, report, rs, ss, rawVs, false);
}
function transmitSecondary(
bytes32[3] calldata reportContext,
bytes calldata report,
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 rawVs
) external {
_transmit(reportContext, report, rs, ss, rawVs, true);
}
function _transmit(
bytes32[3] calldata reportContext,
bytes calldata report,
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 rawVs,
bool isSecondary
) internal {
uint256 initialGas = gasleft();
_validateReport(reportContext, report.length, rs.length, ss.length);
Report memory report_ = _decodeReport(report);
HotVars memory hotVars = s_hotVars;
if (isSecondary) {
(bool exist, uint32 roundId) = _doesReportExist(report_);
if (exist) {
if (hotVars.latestSecondaryRoundId >= roundId) {
revert StaleReport();
}
s_hotVars.latestSecondaryRoundId = roundId;
emit SecondaryRoundIdUpdated(roundId);
_payTransmitter(hotVars, report_.juelsPerFeeCoin, uint32(initialGas));
return;
}
}
uint40 epochAndRound = uint40(uint256(reportContext[1]));
if (epochAndRound != hotVars.latestEpochAndRound || !hotVars.isLatestSecondary) {
if (epochAndRound <= hotVars.latestEpochAndRound) {
revert StaleReport();
}
_verifySignatures(reportContext, report, rs, ss, rawVs);
_report(hotVars, reportContext[0], epochAndRound, report_, isSecondary);
} else {
emit PrimaryFeedUnlocked(s_hotVars.latestAggregatorRoundId);
}
s_hotVars.isLatestSecondary = isSecondary;
_payTransmitter(hotVars, report_.juelsPerFeeCoin, uint32(initialGas));
}
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();
}
}
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));
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);
}
}
if (signedCount & 0x0001010101010101010101010101010101010101010101010101010101010101 != signedCount) {
revert DuplicateSigner();
}
}
function latestTransmissionDetails()
external
view
returns (bytes32 configDigest, uint32 epoch, uint8 round, int192 latestAnswer_, uint64 latestTimestamp_)
{
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
);
}
function latestConfigDigestAndEpoch()
external
view
virtual
override
returns (bool scanLogs, bytes32 configDigest, uint32 epoch)
{
return (false, s_latestConfigDigest, uint32(s_hotVars.latestEpochAndRound >> 8));
}
function _requireExpectedReportLength(bytes memory report, int192[] memory observations) private pure {
uint256 expected = 32
+ 32
+ 32
+ 32
+ 32
+ 32 * observations.length
+ 0;
if (report.length != expected) revert ReportLengthMismatch();
}
function _report(
HotVars memory hotVars,
bytes32 configDigest,
uint40 epochAndRound,
Report memory report,
bool isSecondary
) internal {
if (report.observations.length > MAX_NUM_ORACLES) revert NumObservationsOutOfBounds();
if (report.observations.length <= hotVars.f) revert TooFewValuesToTrustMedian();
hotVars.latestEpochAndRound = epochAndRound;
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)
});
if (isSecondary) {
hotVars.latestSecondaryRoundId = hotVars.latestAggregatorRoundId;
emit SecondaryRoundIdUpdated(hotVars.latestSecondaryRoundId);
}
s_hotVars = hotVars;
emit NewTransmission(
hotVars.latestAggregatorRoundId,
median,
msg.sender,
report.observationsTimestamp,
report.observations,
report.observers,
report.juelsPerFeeCoin,
configDigest,
epochAndRound
);
emit NewRound(
hotVars.latestAggregatorRoundId,
address(0x0),
report.observationsTimestamp
);
emit AnswerUpdated(median, hotVars.latestAggregatorRoundId, block.timestamp);
_validateAnswer(hotVars.latestAggregatorRoundId, median);
}
function latestAnswer() public view virtual override returns (int256) {
return s_transmissions[_getLatestRound()].answer;
}
function latestTimestamp() public view virtual override returns (uint256) {
return s_transmissions[_getLatestRound()].recordedTimestamp;
}
function latestRound() public view virtual override returns (uint256) {
return _getLatestRound();
}
function getAnswer(
uint256 roundId
) public view virtual override returns (int256) {
if (roundId > _getLatestRound()) return 0;
return s_transmissions[uint32(roundId)].answer;
}
function getTimestamp(
uint256 roundId
) public view virtual override returns (uint256) {
if (roundId > _getLatestRound()) return 0;
return s_transmissions[uint32(roundId)].recordedTimestamp;
}
error RoundNotFound();
uint8 private immutable i_decimals;
uint256 internal constant VERSION = 6;
string internal s_description;
function decimals() public view virtual override returns (uint8) {
return i_decimals;
}
function version() public view virtual override returns (uint256) {
return VERSION;
}
function description() public view virtual override returns (string memory) {
return s_description;
}
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);
}
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
);
}
event LinkTokenSet(LinkTokenInterface indexed oldLinkToken, LinkTokenInterface indexed newLinkToken);
error TransferRemainingFundsFailed();
LinkTokenInterface internal s_linkToken;
function setLinkToken(LinkTokenInterface linkToken, address recipient) external onlyOwner {
LinkTokenInterface oldLinkToken = s_linkToken;
if (linkToken == oldLinkToken) {
return;
}
linkToken.balanceOf(address(this));
_payOracles();
uint256 remainingBalance = oldLinkToken.balanceOf(address(this));
if (!oldLinkToken.transfer(recipient, remainingBalance)) revert TransferRemainingFundsFailed();
s_linkToken = linkToken;
emit LinkTokenSet(oldLinkToken, linkToken);
}
function getLinkToken() external view returns (LinkTokenInterface linkToken) {
return s_linkToken;
}
event BillingAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);
AccessControllerInterface internal s_billingAccessController;
function _setBillingAccessController(
AccessControllerInterface billingAccessController
) internal {
AccessControllerInterface oldController = s_billingAccessController;
if (billingAccessController != oldController) {
s_billingAccessController = billingAccessController;
emit BillingAccessControllerSet(oldController, billingAccessController);
}
}
function setBillingAccessController(
AccessControllerInterface _billingAccessController
) external onlyOwner {
_setBillingAccessController(_billingAccessController);
}
function getBillingAccessController() external view returns (AccessControllerInterface) {
return s_billingAccessController;
}
event BillingSet(
uint32 maximumGasPriceGwei,
uint32 reasonableGasPriceGwei,
uint32 observationPaymentGjuels,
uint32 transmissionPaymentGjuels,
uint24 accountingGas
);
error OnlyOwnerAndBillingAdminCanCall();
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
);
}
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
);
}
event OraclePaid(
address indexed transmitter, address indexed payee, uint256 amount, LinkTokenInterface indexed linkToken
);
error OnlyPayeeCanWithdraw();
error InsufficientFunds();
error InsufficientBalance();
function withdrawPayment(
address transmitter
) external {
if (msg.sender != s_payees[transmitter]) revert OnlyPayeeCanWithdraw();
_payOracle(transmitter);
}
function owedPayment(
address transmitterAddress
) public view returns (uint256) {
Transmitter memory transmitter = s_transmitters[transmitterAddress];
if (!transmitter.active) return 0;
uint256 juelsAmount = uint256(s_hotVars.latestAggregatorRoundId - s_rewardFromAggregatorRoundId[transmitter.index])
* uint256(s_hotVars.observationPaymentGjuels) * (1 gwei);
juelsAmount += transmitter.paymentJuels;
return juelsAmount;
}
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];
if (!s_linkToken.transfer(payee, juelsAmount)) {
revert InsufficientFunds();
}
s_rewardFromAggregatorRoundId[transmitter.index] = s_hotVars.latestAggregatorRoundId;
s_transmitters[transmitterAddress].paymentJuels = 0;
emit OraclePaid(transmitterAddress, payee, juelsAmount, s_linkToken);
}
}
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]];
if (!linkToken.transfer(payee, juelsAmount)) {
revert InsufficientFunds();
}
rewardFromAggregatorRoundId[transmitteridx] = latestAggregatorRoundId;
emit OraclePaid(transmitters[transmitteridx], payee, juelsAmount, linkToken);
}
}
s_rewardFromAggregatorRoundId = rewardFromAggregatorRoundId;
}
}
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();
}
}
function _totalLinkDue() internal view returns (uint256 linkDue) {
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];
}
linkDue *= uint256(s_hotVars.observationPaymentGjuels) * (1 gwei);
for (uint256 i = 0; i < n; ++i) {
linkDue += uint256(s_transmitters[transmitters[i]].paymentJuels);
}
return linkDue;
}
function linkAvailableForPayment() external view returns (int256 availableBalance) {
int256 balance = int256(s_linkToken.balanceOf(address(this)));
int256 due = int256(_totalLinkDue());
return int256(balance) - int256(due);
}
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];
}
error LeftGasCannotExceedInitialGas();
function _reimbursementGasPriceGwei(
uint256 txGasPriceGwei,
uint256 reasonableGasPriceGwei,
uint256 maximumGasPriceGwei
) internal pure returns (uint256) {
unchecked {
uint256 gasPriceGwei = txGasPriceGwei;
if (txGasPriceGwei < reasonableGasPriceGwei) {
gasPriceGwei += (reasonableGasPriceGwei - txGasPriceGwei) / 2;
}
return _min(gasPriceGwei, maximumGasPriceGwei);
}
}
function _transmitterGasCostWei(
uint256 initialGas,
uint256 gasPriceGwei,
uint256 callDataGas,
uint256 accountingGas,
uint256 leftGas
) internal pure returns (uint256) {
unchecked {
if (initialGas < leftGas) revert LeftGasCannotExceedInitialGas();
uint256 usedGas = initialGas - leftGas
+ callDataGas + accountingGas;
uint256 fullGasCostWei = usedGas * gasPriceGwei * (1 gwei);
return fullGasCostWei;
}
}
function _payTransmitter(HotVars memory hotVars, int192 juelsPerFeeCoin, uint32 initialGas) internal virtual {
unchecked {
if (juelsPerFeeCoin < 0) {
return;
}
uint256 gasPriceGwei = _reimbursementGasPriceGwei(
tx.gasprice / (1 gwei),
hotVars.reasonableGasPriceGwei,
hotVars.maximumGasPriceGwei
);
uint256 callDataGasCost = 16 * msg.data.length;
uint256 gasLeft = gasleft();
uint256 gasCostEthWei =
_transmitterGasCostWei(uint256(initialGas), gasPriceGwei, callDataGasCost, s_accountingGas, gasLeft);
uint256 gasCostJuels = (gasCostEthWei * uint192(juelsPerFeeCoin)) / 1e18;
uint96 oldTransmitterPaymentJuels = s_transmitters[msg.sender].paymentJuels;
uint96 newTransmitterPaymentJuels = uint96(
uint256(oldTransmitterPaymentJuels) + gasCostJuels + uint256(hotVars.transmissionPaymentGjuels) * (1 gwei)
);
if (newTransmitterPaymentJuels < oldTransmitterPaymentJuels) {
return;
}
s_transmitters[msg.sender].paymentJuels = newTransmitterPaymentJuels;
}
}
event PayeeshipTransferRequested(address indexed transmitter, address indexed current, address indexed proposed);
event PayeeshipTransferred(address indexed transmitter, address indexed previous, address indexed current);
error TransmittersSizeNotEqualPayeeSize();
error PayeeAlreadySet();
error OnlyCurrentPayeeCanUpdate();
error CannotTransferPayeeToSelf();
error OnlyProposedPayeesCanAccept();
mapping(address transmitterAddress => address paymentAddress) internal s_payees;
mapping(address transmitterAddress => address paymentAddress) internal s_proposedPayees;
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);
}
}
}
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);
}
}
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);
}
function _min(uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
if (a < b) return a;
return b;
}
}
}
文件 10 的 16:DualAggregatorHelper.sol
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;
}
}
文件 11 的 16:IOwnable.sol
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}
文件 12 的 16:ITypeAndVersion.sol
pragma solidity ^0.8.0;
interface ITypeAndVersion {
function typeAndVersion() external pure returns (string memory);
}
文件 13 的 16:LinkTokenInterface.sol
pragma solidity ^0.8.0;
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);
}
文件 14 的 16:OCR2Abstract.sol
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);
uint256 private constant PREFIX = 0x0001 << (256 - 16);
event ConfigSet(
uint32 previousConfigBlockNumber,
bytes32 configDigest,
uint64 configCount,
address[] signers,
address[] transmitters,
uint8 f,
bytes onchainConfig,
uint64 offchainConfigVersion,
bytes offchainConfig
);
function setConfig(
address[] memory signers,
address[] memory transmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) external virtual;
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));
}
event Transmitted(bytes32 configDigest, uint32 epoch);
function latestConfigDigestAndEpoch()
external
view
virtual
returns (bool scanLogs, bytes32 configDigest, uint32 epoch);
function transmit(
bytes32[3] calldata reportContext,
bytes calldata report,
bytes32[] calldata rs,
bytes32[] calldata ss,
bytes32 rawVs
) external virtual;
}
文件 15 的 16:SimpleReadAccessController.sol
pragma solidity ^0.8.0;
import {SimpleWriteAccessController} from "./SimpleWriteAccessController.sol";
contract SimpleReadAccessController is SimpleWriteAccessController {
function hasAccess(address _user, bytes memory _calldata) public view virtual override returns (bool) {
return super.hasAccess(_user, _calldata) || _user == tx.origin;
}
}
文件 16 的 16:SimpleWriteAccessController.sol
pragma solidity ^0.8.0;
import {ConfirmedOwner} from "./ConfirmedOwner.sol";
import {AccessControllerInterface} from "../interfaces/AccessControllerInterface.sol";
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;
}
function hasAccess(address _user, bytes memory) public view virtual override returns (bool) {
return s_accessList[_user] || !checkEnabled;
}
function addAccess(address _user) external onlyOwner {
if (!s_accessList[_user]) {
s_accessList[_user] = true;
emit AddedAccess(_user);
}
}
function removeAccess(address _user) external onlyOwner {
if (s_accessList[_user]) {
s_accessList[_user] = false;
emit RemovedAccess(_user);
}
}
function enableAccessCheck() external onlyOwner {
if (!checkEnabled) {
checkEnabled = true;
emit CheckAccessEnabled();
}
}
function disableAccessCheck() external onlyOwner {
if (checkEnabled) {
checkEnabled = false;
emit CheckAccessDisabled();
}
}
modifier checkAccess() {
require(hasAccess(msg.sender, msg.data), "No access");
_;
}
}
{
"compilationTarget": {
"src/DualAggregator.sol": "DualAggregator"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 1000000
},
"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/",
":script/=script/",
":src/=src/",
":test/=test/"
]
}
[{"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"}]