ETH Price: $2,088.65 (+0.81%)

Transaction Decoder

Block:
24618628 at Mar-09-2026 08:09:35 AM +UTC
Transaction Fee:
0.0000029859491285 ETH $0.006237
Gas Used:
62,285 Gas / 0.0479401 Gwei

Emitted Events:

1462 DualAggregator.PrimaryFeedUnlocked( primaryRoundId=12114 )

Account State Difference:

  Address   Before After State Difference Code
0x634438d8...983734391
4.446621276070574879 Eth
Nonce: 40024
4.446618290121446379 Eth
Nonce: 40025
0.0000029859491285
0xcd07B31D...Eae8FE62f
(BuilderNet)
137.215236223195415173 Eth137.215236806482294598 Eth0.000000583286879425

Execution Trace

AuthorizedForwarder.forward( to=0xcd07B31D85756098334eDdC92DE755dEae8FE62f, data=0xB1DC65A400013BE2E496FAE8335B6B74BCBBC798484DEF37217B1A6353D188231AC9858400000000000000000000000000000000000000000000000000000000003B7D016981B769FF2B5E905454640CB1CED7616D283858E7F96D32A32769EB52B34FCC00000000000000000000000000000000000000000000000000000000000000E000000000000000000000000000000000000000000000000000000000000003E000000000000000000000000000000000000000000000000000000000000004E0000001010101010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E00000000000000000000000000000000000000000000000000000000069AE80280A12080209070B050D000F10060C0E1103010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000027C35E026000000000000000000000000000000000000000000000000000000027C35E4B8000000000000000000000000000000000000000000000000000000027C3A8AD0000000000000000000000000000000000000000000000000000000027C3A8AD0000000000000000000000000000000000000000000000000000000027C3A8AD0000000000000000000000000000000000000000000000000000000027C3A8AD0000000000000000000000000000000000000000000000000000000027C445E70000000000000000000000000000000000000000000000000000000027C482730000000000000000000000000000000000000000000000000000000027C482730000000000000000000000000000000000000000000000000000000027C496780000000000000000000000000000000000000000000000000000000027C4B1F7B000000000000000000000000000000000000000000000000000000027C4B1F7B000000000000000000000000000000000000000000000000000000027C4B1F80000000000000000000000000000000000000000000000000000000027C4E5058000000000000000000000000000000000000000000000000000000027C4E5058000000000000000000000000000000000000000000000000000000027C4E5058000000000000000000000000000000000000000000000000000000027C65CD31000000000000000000000000000000000000000000000000000000027C65CD310000000000000000000000000000000000000000000000000000000000000007A2C48CA6D384A6D710B08DC394DE950B806A47A2BA5AE657905099D70DC260F75CD033F123D9F2C60677F42EA935B27439901647B5C968BB96C5E2DB28D984A3B620A16F627A505E4C0C588B357636E64DBD3E1022632925670D3C761BD705BACDFEB0308EA8B0C033086E1F64496174075F57D84DF7823621AA85A7D867E1D8798E18AEAB3C993C34327387D023288703E3907D99ED5999DAD63724C7650D5EEB31351A58E7BFABDDEDE4E2F6C6D9E9E98BA5CAAE84F6C32B258188569F3F86618F9BAEA56231B1D9396BFA3AE2380B84CFC85D7FFDF89CC75DE61A0826342600000000000000000000000000000000000000000000000000000000000000072DDD4E8EF575B672BB01DED462254092D16D3EF0959977699FC70FDD4677D4FE3E986E697A5A5B3C7870E003300B4FE93B61CFA257566D3504CD052B01AC674E686E0ECA51D9C4F88DE978D3FFF3AE3B58C186750F00FAD7E1785EA535FB1F1E2D781D8EA1A24D996B4A87521414E4FCCD886753549ECC2580AA5BB49CF26690541C6FC71E012247A343C3258115CE2631EB75C41B1C85D6642A47FC7532025B6736A65421BA0C1687F108E5F60DD5C364AC9877AF1593D1D6D53A088C5483AB09902E3F85735DB386756B4F3DC7C1F869ED03B8DE352E6A6E42B7D3B74629D3 )
  • DualAggregator.transmit( )
    File 1 of 2: AuthorizedForwarder
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
    pragma solidity ^0.8.1;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev Returns true if `account` is a contract.
         *
         * [IMPORTANT]
         * ====
         * It is unsafe to assume that an address for which this function returns
         * false is an externally-owned account (EOA) and not a contract.
         *
         * Among others, `isContract` will return false for the following
         * types of addresses:
         *
         *  - an externally-owned account
         *  - a contract in construction
         *  - an address where a contract will be created
         *  - an address where a contract lived, but was destroyed
         *
         * Furthermore, `isContract` will also return true if the target contract within
         * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
         * which only has an effect at the end of a transaction.
         * ====
         *
         * [IMPORTANT]
         * ====
         * You shouldn't rely on `isContract` to protect against flash loan attacks!
         *
         * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
         * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
         * constructor.
         * ====
         */
        function isContract(address account) internal view returns (bool) {
            // This method relies on extcodesize/address.code.length, which returns 0
            // for contracts in construction, since the code is only stored at the end
            // of the constructor execution.
            return account.code.length > 0;
        }
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason, it is bubbled up by this
         * function (like regular Solidity function calls).
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         *
         * _Available since v3.1._
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, "Address: low-level call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
         * `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
        }
        /**
         * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
         * with `errorMessage` as a fallback revert reason when `target` reverts.
         *
         * _Available since v3.1._
         */
        function functionCallWithValue(
            address target,
            bytes memory data,
            uint256 value,
            string memory errorMessage
        ) internal returns (bytes memory) {
            require(address(this).balance >= value, "Address: insufficient balance for call");
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            return functionStaticCall(target, data, "Address: low-level static call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a static call.
         *
         * _Available since v3.3._
         */
        function functionStaticCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionDelegateCall(target, data, "Address: low-level delegate call failed");
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
         * but performing a delegate call.
         *
         * _Available since v3.4._
         */
        function functionDelegateCall(
            address target,
            bytes memory data,
            string memory errorMessage
        ) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata, errorMessage);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
         * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
         *
         * _Available since v4.8._
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal view returns (bytes memory) {
            if (success) {
                if (returndata.length == 0) {
                    // only check isContract if the call was successful and the return data is empty
                    // otherwise we already know that it was a contract
                    require(isContract(target), "Address: call to non-contract");
                }
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
         * revert reason or using the provided one.
         *
         * _Available since v4.3._
         */
        function verifyCallResult(
            bool success,
            bytes memory returndata,
            string memory errorMessage
        ) internal pure returns (bytes memory) {
            if (success) {
                return returndata;
            } else {
                _revert(returndata, errorMessage);
            }
        }
        function _revert(bytes memory returndata, string memory errorMessage) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.19;
    import {ConfirmedOwnerWithProposal} from "../shared/access/ConfirmedOwnerWithProposal.sol";
    import {AuthorizedReceiver} from "./AuthorizedReceiver.sol";
    import {Address} from "@openzeppelin/contracts/utils/Address.sol";
    // solhint-disable gas-custom-errors
    contract AuthorizedForwarder is ConfirmedOwnerWithProposal, AuthorizedReceiver {
      using Address for address;
      // solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i
      address public immutable linkToken;
      event OwnershipTransferRequestedWithMessage(address indexed from, address indexed to, bytes message);
      constructor(
        address link,
        address owner,
        address recipient,
        bytes memory message
      ) ConfirmedOwnerWithProposal(owner, recipient) {
        require(link != address(0), "Link token cannot be a zero address");
        linkToken = link;
        if (recipient != address(0)) {
          emit OwnershipTransferRequestedWithMessage(owner, recipient, message);
        }
      }
      string public constant typeAndVersion = "AuthorizedForwarder 1.1.0";
      // @notice Forward a call to another contract
      // @dev Only callable by an authorized sender
      // @param to address
      // @param data to forward
      function forward(address to, bytes calldata data) external validateAuthorizedSender {
        require(to != linkToken, "Cannot forward to Link token");
        _forward(to, data);
      }
      //  @notice Forward multiple calls to other contracts in a multicall style
      //  @dev Only callable by an authorized sender
      //  @param tos An array of addresses to forward the calls to
      //  @param datas An array of data to forward to each corresponding address
      function multiForward(address[] calldata tos, bytes[] calldata datas) external validateAuthorizedSender {
        require(tos.length == datas.length, "Arrays must have the same length");
        for (uint256 i = 0; i < tos.length; ++i) {
          address to = tos[i];
          require(to != linkToken, "Cannot forward to Link token");
          // Perform the forward operation
          _forward(to, datas[i]);
        }
      }
      // @notice Forward a call to another contract
      // @dev Only callable by the owner
      // @param to address
      // @param data to forward
      function ownerForward(address to, bytes calldata data) external onlyOwner {
        _forward(to, data);
      }
      // @notice Transfer ownership with instructions for recipient
      // @param to address proposed recipient of ownership
      // @param message instructions for recipient upon accepting ownership
      function transferOwnershipWithMessage(address to, bytes calldata message) external {
        transferOwnership(to);
        emit OwnershipTransferRequestedWithMessage(msg.sender, to, message);
      }
      // @notice concrete implementation of AuthorizedReceiver
      // @return bool of whether sender is authorized
      function _canSetAuthorizedSenders() internal view override returns (bool) {
        return owner() == msg.sender;
      }
      // @notice common forwarding functionality and validation
      function _forward(address to, bytes calldata data) private {
        require(to.isContract(), "Must forward to a contract");
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory result) = to.call(data);
        if (!success) {
          if (result.length == 0) revert("Forwarded call reverted without reason");
          assembly {
            revert(add(32, result), mload(result))
          }
        }
      }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.19;
    import {IAuthorizedReceiver} from "./interfaces/IAuthorizedReceiver.sol";
    // solhint-disable gas-custom-errors
    abstract contract AuthorizedReceiver is IAuthorizedReceiver {
      mapping(address sender => bool authorized) private s_authorizedSenders;
      address[] private s_authorizedSenderList;
      event AuthorizedSendersChanged(address[] senders, address changedBy);
      // @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow.
      // @param senders The addresses of the authorized Chainlink node
      function setAuthorizedSenders(address[] calldata senders) external override validateAuthorizedSenderSetter {
        require(senders.length > 0, "Must have at least 1 sender");
        // Set previous authorized senders to false
        uint256 authorizedSendersLength = s_authorizedSenderList.length;
        for (uint256 i = 0; i < authorizedSendersLength; ++i) {
          s_authorizedSenders[s_authorizedSenderList[i]] = false;
        }
        // Set new to true
        for (uint256 i = 0; i < senders.length; ++i) {
          require(s_authorizedSenders[senders[i]] == false, "Must not have duplicate senders");
          s_authorizedSenders[senders[i]] = true;
        }
        // Replace list
        s_authorizedSenderList = senders;
        emit AuthorizedSendersChanged(senders, msg.sender);
      }
      // @notice Retrieve a list of authorized senders
      // @return array of addresses
      function getAuthorizedSenders() external view override returns (address[] memory) {
        return s_authorizedSenderList;
      }
      // @notice Use this to check if a node is authorized for fulfilling requests
      // @param sender The address of the Chainlink node
      // @return The authorization status of the node
      function isAuthorizedSender(address sender) public view override returns (bool) {
        return s_authorizedSenders[sender];
      }
      // @notice customizable guard of who can update the authorized sender list
      // @return bool whether sender can update authorized sender list
      function _canSetAuthorizedSenders() internal virtual returns (bool);
      // @notice validates the sender is an authorized sender
      function _validateIsAuthorizedSender() internal view {
        require(isAuthorizedSender(msg.sender), "Not authorized sender");
      }
      // @notice prevents non-authorized addresses from calling this method
      modifier validateAuthorizedSender() {
        _validateIsAuthorizedSender();
        _;
      }
      // @notice prevents non-authorized addresses from calling this method
      modifier validateAuthorizedSenderSetter() {
        require(_canSetAuthorizedSenders(), "Cannot set authorized senders");
        _;
      }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface IAuthorizedReceiver {
      function isAuthorizedSender(address sender) external view returns (bool);
      function getAuthorizedSenders() external returns (address[] memory);
      function setAuthorizedSenders(address[] calldata senders) external;
    }
    // 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;
    interface IOwnable {
      function owner() external returns (address);
      function transferOwnership(address recipient) external;
      function acceptOwnership() external;
    }
    

    File 2 of 2: DualAggregator
    // 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;
    // 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;
    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 AggregatorValidatorInterface {
      function validate(
        uint256 previousRoundId,
        int256 previousAnswer,
        uint256 currentRoundId,
        int256 currentAnswer
      ) external returns (bool);
    }
    // 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 {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;
    /// @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;
    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.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;
    // 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;
    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;
    interface ITypeAndVersion {
      function typeAndVersion() external pure returns (string memory);
    }
    // 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;
    interface IOwnable {
      function owner() external returns (address);
      function transferOwnership(address recipient) external;
      function acceptOwnership() external;
    }