ETH Price: $2,439.77 (-4.45%)

Transaction Decoder

Block:
22273216 at Apr-15-2025 08:33:23 AM +UTC
Transaction Fee:
0.000143237556695448 ETH $0.35
Gas Used:
150,389 Gas / 0.952447032 Gwei

Emitted Events:

252 CyberIDPermissionedRelayHook.SigUsed( account=[Sender] 0xc27845fabee173d77e84d5e21eea708bf47645c2, nonce=0, cid=thanglee93, discount=1000, cost=2436393608167541 )
253 ERC1967Proxy.0x72ef1b82f8dd784ea7ca8d18944d488bab7c7f5c3d277faa8a97db495ed5b744( 0x72ef1b82f8dd784ea7ca8d18944d488bab7c7f5c3d277faa8a97db495ed5b744, c43278af6945b9252e4d464f85678ecef0cc87608d44cd8f25885047fb111412, 000000000000000000000000c27845fabee173d77e84d5e21eea708bf47645c2, 000000000000000000000000c137be6b59e824672aada673e55cf4d150669af8, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000000000a0, 0000000000000000000000000000000000000000000000000000000000000104, fc84b4bf00000000000000000000000000000000000000000000000000000000, 0000002000000000000000000000000000000000000000000000000000000000, 0000000100000000000000000000000000000000000000000000000000000000, 0000002000000000000000000000000000000000000000000000000000000000, 00000060000000000000000000000000c27845fabee173d77e84d5e21eea708b, f47645c200000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000a7468616e676c65653933000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x164F005B...A8895323C 0.895155107744758412 Eth0.897591501352925953 Eth0.002436393608167541
(Titan Builder)
20.765876202908893435 Eth20.765963729306893435 Eth0.000087526398
0x7262f9D1...2e6F509b1
0x9071ff33...5BEe976aa
0xC27845fa...BF47645C2
0.003606864402379 Eth
Nonce: 4
0.001027233237516011 Eth
Nonce: 5
0.002579631164862989

Execution Trace

ETH 0.002460757544249216 ERC1967Proxy.abef58e7( )
  • ETH 0.002460757544249216 CyberRelayGate.relayToCyber( requestId=C43278AF6945B9252E4D464F85678ECEF0CC87608D44CD8F25885047FB111412, destination=0xC137Be6B59E824672aaDa673e55Cf4D150669af8, data=0x00000000000000000000000000000000000000000000000000000000000000E0000000000000000000000000C27845FABEE173D77E84D5E21EEA708BF47645C200000000000000000000000000000000000000000000000000000000000003E8000000000000000000000000000000000000000000000000000000000000001B3789134C42C1F6E330156107253C836A85B3803DBCEA6B4770BA163B5304D1062EF56A5DF0850B05EC8A9CF894724925BADAB6E080CF2AA6FA05D3E3D5F39DED0000000000000000000000000000000000000000000000000000000067FE1C13000000000000000000000000000000000000000000000000000000000000000A7468616E676C6565393300000000000000000000000000000000000000000000 )
    • ETH 0.002460757544249216 CyberIDPermissionedRelayHook.processRelay( msgSender=0xC27845faBeE173D77e84D5e21eea708BF47645C2, destination=0xC137Be6B59E824672aaDa673e55Cf4D150669af8, data=0x00000000000000000000000000000000000000000000000000000000000000E0000000000000000000000000C27845FABEE173D77E84D5E21EEA708BF47645C200000000000000000000000000000000000000000000000000000000000003E8000000000000000000000000000000000000000000000000000000000000001B3789134C42C1F6E330156107253C836A85B3803DBCEA6B4770BA163B5304D1062EF56A5DF0850B05EC8A9CF894724925BADAB6E080CF2AA6FA05D3E3D5F39DED0000000000000000000000000000000000000000000000000000000067FE1C13000000000000000000000000000000000000000000000000000000000000000A7468616E676C6565393300000000000000000000000000000000000000000000 ) => ( [{name:to, type:address, order:1, indexed:false, value:0xC137Be6B59E824672aaDa673e55Cf4D150669af8, valueString:0xC137Be6B59E824672aaDa673e55Cf4D150669af8}, {name:value, type:uint256, order:2, indexed:false, value:0, valueString:0}, {name:callData, type:bytes, order:3, indexed:false, value:0xFC84B4BF0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000C27845FABEE173D77E84D5E21EEA708BF47645C20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A7468616E676C6565393300000000000000000000000000000000000000000000, valueString:0xFC84B4BF0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000C27845FABEE173D77E84D5E21EEA708BF47645C20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A7468616E676C6565393300000000000000000000000000000000000000000000}] )
      • Null: 0x000...001.d978e4a3( )
      • EACAggregatorProxy.STATICCALL( )
        • AccessControlledOCR2Aggregator.STATICCALL( )
        • ETH 0.000024363936081675 0xc27845fabee173d77e84d5e21eea708bf47645c2.CALL( )
        • ETH 0.002436393608167541 0x164f005b8d305ec60e10a039c36d099a8895323c.CALL( )
          File 1 of 5: ERC1967Proxy
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
          pragma solidity ^0.8.0;
          import "../Proxy.sol";
          import "./ERC1967Upgrade.sol";
          /**
           * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
           * implementation address that can be changed. This address is stored in storage in the location specified by
           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
           * implementation behind the proxy.
           */
          contract ERC1967Proxy is Proxy, ERC1967Upgrade {
              /**
               * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
               *
               * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
               * function call, and allows initializing the storage of the proxy like a Solidity constructor.
               */
              constructor(address _logic, bytes memory _data) payable {
                  _upgradeToAndCall(_logic, _data, false);
              }
              /**
               * @dev Returns the current implementation address.
               */
              function _implementation() internal view virtual override returns (address impl) {
                  return ERC1967Upgrade._getImplementation();
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
           * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
           * be specified by overriding the virtual {_implementation} function.
           *
           * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
           * different contract through the {_delegate} function.
           *
           * The success and return data of the delegated call will be returned back to the caller of the proxy.
           */
          abstract contract Proxy {
              /**
               * @dev Delegates the current call to `implementation`.
               *
               * This function does not return to its internal call site, it will return directly to the external caller.
               */
              function _delegate(address implementation) internal virtual {
                  assembly {
                      // Copy msg.data. We take full control of memory in this inline assembly
                      // block because it will not return to Solidity code. We overwrite the
                      // Solidity scratch pad at memory position 0.
                      calldatacopy(0, 0, calldatasize())
                      // Call the implementation.
                      // out and outsize are 0 because we don't know the size yet.
                      let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                      // Copy the returned data.
                      returndatacopy(0, 0, returndatasize())
                      switch result
                      // delegatecall returns 0 on error.
                      case 0 {
                          revert(0, returndatasize())
                      }
                      default {
                          return(0, returndatasize())
                      }
                  }
              }
              /**
               * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function
               * and {_fallback} should delegate.
               */
              function _implementation() internal view virtual returns (address);
              /**
               * @dev Delegates the current call to the address returned by `_implementation()`.
               *
               * This function does not return to its internal call site, it will return directly to the external caller.
               */
              function _fallback() internal virtual {
                  _beforeFallback();
                  _delegate(_implementation());
              }
              /**
               * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
               * function in the contract matches the call data.
               */
              fallback() external payable virtual {
                  _fallback();
              }
              /**
               * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
               * is empty.
               */
              receive() external payable virtual {
                  _fallback();
              }
              /**
               * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
               * call, or as part of the Solidity `fallback` or `receive` functions.
               *
               * If overridden should call `super._beforeFallback()`.
               */
              function _beforeFallback() internal virtual {}
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.3) (proxy/ERC1967/ERC1967Upgrade.sol)
          pragma solidity ^0.8.2;
          import "../beacon/IBeacon.sol";
          import "../../interfaces/IERC1967.sol";
          import "../../interfaces/draft-IERC1822.sol";
          import "../../utils/Address.sol";
          import "../../utils/StorageSlot.sol";
          /**
           * @dev This abstract contract provides getters and event emitting update functions for
           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
           *
           * _Available since v4.1._
           *
           * @custom:oz-upgrades-unsafe-allow delegatecall
           */
          abstract contract ERC1967Upgrade is IERC1967 {
              // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
              bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
              /**
               * @dev Storage slot with the address of the current implementation.
               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
               * validated in the constructor.
               */
              bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
              /**
               * @dev Returns the current implementation address.
               */
              function _getImplementation() internal view returns (address) {
                  return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
              }
              /**
               * @dev Stores a new address in the EIP1967 implementation slot.
               */
              function _setImplementation(address newImplementation) private {
                  require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                  StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
              }
              /**
               * @dev Perform implementation upgrade
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeTo(address newImplementation) internal {
                  _setImplementation(newImplementation);
                  emit Upgraded(newImplementation);
              }
              /**
               * @dev Perform implementation upgrade with additional setup call.
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeToAndCall(
                  address newImplementation,
                  bytes memory data,
                  bool forceCall
              ) internal {
                  _upgradeTo(newImplementation);
                  if (data.length > 0 || forceCall) {
                      Address.functionDelegateCall(newImplementation, data);
                  }
              }
              /**
               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeToAndCallUUPS(
                  address newImplementation,
                  bytes memory data,
                  bool forceCall
              ) internal {
                  // Upgrades from old implementations will perform a rollback test. This test requires the new
                  // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                  // this special case will break upgrade paths from old UUPS implementation to new ones.
                  if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                      _setImplementation(newImplementation);
                  } else {
                      try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                          require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                      } catch {
                          revert("ERC1967Upgrade: new implementation is not UUPS");
                      }
                      _upgradeToAndCall(newImplementation, data, forceCall);
                  }
              }
              /**
               * @dev Storage slot with the admin of the contract.
               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
               * validated in the constructor.
               */
              bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
              /**
               * @dev Returns the current admin.
               */
              function _getAdmin() internal view returns (address) {
                  return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
              }
              /**
               * @dev Stores a new address in the EIP1967 admin slot.
               */
              function _setAdmin(address newAdmin) private {
                  require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                  StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
              }
              /**
               * @dev Changes the admin of the proxy.
               *
               * Emits an {AdminChanged} event.
               */
              function _changeAdmin(address newAdmin) internal {
                  emit AdminChanged(_getAdmin(), newAdmin);
                  _setAdmin(newAdmin);
              }
              /**
               * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
               * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
               */
              bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
              /**
               * @dev Returns the current beacon.
               */
              function _getBeacon() internal view returns (address) {
                  return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
              }
              /**
               * @dev Stores a new beacon in the EIP1967 beacon slot.
               */
              function _setBeacon(address newBeacon) private {
                  require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                  require(
                      Address.isContract(IBeacon(newBeacon).implementation()),
                      "ERC1967: beacon implementation is not a contract"
                  );
                  StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
              }
              /**
               * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
               * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
               *
               * Emits a {BeaconUpgraded} event.
               */
              function _upgradeBeaconToAndCall(
                  address newBeacon,
                  bytes memory data,
                  bool forceCall
              ) internal {
                  _setBeacon(newBeacon);
                  emit BeaconUpgraded(newBeacon);
                  if (data.length > 0 || forceCall) {
                      Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev This is the interface that {BeaconProxy} expects of its beacon.
           */
          interface IBeacon {
              /**
               * @dev Must return an address that can be used as a delegate call target.
               *
               * {BeaconProxy} will check that this address is a contract.
               */
              function implementation() external view returns (address);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.3) (interfaces/IERC1967.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
           *
           * _Available since v4.9._
           */
          interface IERC1967 {
              /**
               * @dev Emitted when the implementation is upgraded.
               */
              event Upgraded(address indexed implementation);
              /**
               * @dev Emitted when the admin account has changed.
               */
              event AdminChanged(address previousAdmin, address newAdmin);
              /**
               * @dev Emitted when the beacon is changed.
               */
              event BeaconUpgraded(address indexed beacon);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
           * proxy whose upgrades are fully controlled by the current implementation.
           */
          interface IERC1822Proxiable {
              /**
               * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
               * address.
               *
               * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
               * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
               * function revert if invoked through a proxy.
               */
              function proxiableUUID() external view returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.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
               * ====
               *
               * [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://diligence.consensys.net/posts/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.5.11/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
          // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Library for reading and writing primitive types to specific storage slots.
           *
           * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
           * This library helps with reading and writing to such slots without the need for inline assembly.
           *
           * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
           *
           * Example usage to set ERC1967 implementation slot:
           * ```
           * contract ERC1967 {
           *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
           *
           *     function _getImplementation() internal view returns (address) {
           *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
           *     }
           *
           *     function _setImplementation(address newImplementation) internal {
           *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
           *     }
           * }
           * ```
           *
           * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
           */
          library StorageSlot {
              struct AddressSlot {
                  address value;
              }
              struct BooleanSlot {
                  bool value;
              }
              struct Bytes32Slot {
                  bytes32 value;
              }
              struct Uint256Slot {
                  uint256 value;
              }
              /**
               * @dev Returns an `AddressSlot` with member `value` located at `slot`.
               */
              function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
               */
              function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
               */
              function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
               */
              function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
          }
          

          File 2 of 5: CyberIDPermissionedRelayHook
          // SPDX-License-Identifier: GPL-3.0-or-later
          pragma solidity 0.8.14;
          import { Ownable } from "openzeppelin-contracts/contracts/access/Ownable.sol";
          import { IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
          import { SafeERC20 } from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
          import "../interfaces/ICyberRelayGateHook.sol";
          import "../interfaces/ICyberID.sol";
          import "../interfaces/AggregatorV3Interface.sol";
          import { DataTypes } from "../libraries/DataTypes.sol";
          import { EIP712 } from "../base/EIP712.sol";
          /**
           * @title CyberIDPermissionedRelayHook
           * @author Cyber
           */
          contract CyberIDPermissionedRelayHook is ICyberRelayGateHook, EIP712, Ownable {
              using SafeERC20 for IERC20;
              /*//////////////////////////////////////////////////////////////
                                      STORAGE
              //////////////////////////////////////////////////////////////*/
              address public immutable signer;
              AggregatorV3Interface public immutable usdOracle;
              address public recipient;
              uint256 public price3Letter;
              uint256 public price4Letter;
              uint256 public price5To9Letter;
              uint256 public price10AndMoreLetter;
              uint256 public fixedFee;
              mapping(address => uint256) public nonces;
              bytes32 public constant _REGISTER_TYPEHASH =
                  keccak256(
                      "register(string cid,address to,uint256 discount,uint256 nonce,uint256 deadline)"
                  );
              uint256 internal constant BASE = 1000;
              /*//////////////////////////////////////////////////////////////
                                      EVENTS
              //////////////////////////////////////////////////////////////*/
              event StableFeeChanged(
                  address indexed recipient,
                  uint256 price3Letter,
                  uint256 price4Letter,
                  uint256 price5To9Letter,
                  uint256 price10AndMoreLetter
              );
              event SigUsed(
                  address account,
                  uint256 nonce,
                  string cid,
                  uint256 discount,
                  uint256 cost
              );
              /*//////////////////////////////////////////////////////////////
                                      CONSTRUCTOR
              //////////////////////////////////////////////////////////////*/
              constructor(address _owner, address _signer, address _oracleAddress) {
                  _transferOwnership(_owner);
                  signer = _signer;
                  usdOracle = AggregatorV3Interface(_oracleAddress);
                  fixedFee = 0.000003 ether;
              }
              /*//////////////////////////////////////////////////////////////
                              ICyberRelayGateHook OVERRIDES
              //////////////////////////////////////////////////////////////*/
              function processRelay(
                  address msgSender,
                  address destination,
                  bytes calldata data
              ) external payable override returns (RelayParams memory) {
                  DataTypes.EIP712Signature memory sig;
                  string memory cid;
                  address to;
                  uint256 discount;
                  (cid, to, discount, sig.v, sig.r, sig.s, sig.deadline) = abi.decode(
                      data,
                      (string, address, uint256, uint8, bytes32, bytes32, uint256)
                  );
                  uint256 currentNonce = nonces[to]++;
                  _requiresExpectedSigner(
                      _hashTypedDataV4(
                          keccak256(
                              abi.encode(
                                  _REGISTER_TYPEHASH,
                                  keccak256(bytes(cid)),
                                  to,
                                  discount,
                                  currentNonce,
                                  sig.deadline
                              )
                          )
                      ),
                      signer,
                      sig.v,
                      sig.r,
                      sig.s,
                      sig.deadline
                  );
                  uint256 cost = getPriceWei(cid, discount);
                  _chargeAndRefundOverPayment(cost, msgSender);
                  emit SigUsed(to, currentNonce, cid, discount, cost);
                  RelayParams memory relayParams;
                  relayParams.to = destination;
                  relayParams.value = 0;
                  BatchRegisterCyberIdParams[]
                      memory params = new BatchRegisterCyberIdParams[](1);
                  params[0] = BatchRegisterCyberIdParams(cid, to, false);
                  relayParams.callData = abi.encodeWithSelector(
                      ICyberID.privilegedRegister.selector,
                      params
                  );
                  return relayParams;
              }
              /*//////////////////////////////////////////////////////////////
                                      PUBLIC VIEW
              //////////////////////////////////////////////////////////////*/
              function getPriceWei(
                  string memory cid,
                  uint256 discount
              ) public view returns (uint256) {
                  return (_attoUSDToWei(_getUSDPrice(cid)) * discount) / BASE + fixedFee;
              }
              /*//////////////////////////////////////////////////////////////
                              EIP712 OVERRIDES
              //////////////////////////////////////////////////////////////*/
              function _domainSeparatorName()
                  internal
                  pure
                  override
                  returns (string memory)
              {
                  return "CyberIDPermissionedRelayHook";
              }
              /*//////////////////////////////////////////////////////////////
                              ONLY OWNER
              //////////////////////////////////////////////////////////////*/
              function rescueToken(address token) external onlyOwner {
                  if (token == address(0)) {
                      (bool success, ) = owner().call{ value: address(this).balance }("");
                      require(success, "WITHDRAW_FAILED");
                  } else {
                      IERC20(token).safeTransfer(
                          owner(),
                          IERC20(token).balanceOf(address(this))
                      );
                  }
              }
              function configPrices(
                  address _recipient,
                  uint256[4] memory prices
              ) external onlyOwner {
                  require(_recipient != address(0), "INVALID_RECIPIENT");
                  recipient = _recipient;
                  price3Letter = prices[0];
                  price4Letter = prices[1];
                  price5To9Letter = prices[2];
                  price10AndMoreLetter = prices[3];
                  emit StableFeeChanged(
                      _recipient,
                      prices[0],
                      prices[1],
                      prices[2],
                      prices[3]
                  );
              }
              function setFixedFee(uint256 _fixedFee) external onlyOwner {
                  fixedFee = _fixedFee;
              }
              /*//////////////////////////////////////////////////////////////
                              PRIVATE
              //////////////////////////////////////////////////////////////*/
              function _chargeAndRefundOverPayment(
                  uint256 cost,
                  address refundTo
              ) internal {
                  require(msg.value >= cost, "INSUFFICIENT_FUNDS");
                  /**
                   * Already checked msg.value >= cost
                   */
                  uint256 overpayment;
                  unchecked {
                      overpayment = msg.value - cost;
                  }
                  if (overpayment > 0) {
                      (bool refundSuccess, ) = refundTo.call{ value: overpayment }("");
                      require(refundSuccess, "REFUND_FAILED");
                  }
                  if (cost > 0) {
                      (bool chargeSuccess, ) = recipient.call{ value: cost }("");
                      require(chargeSuccess, "CHARGE_FAILED");
                  }
              }
              function _getUSDPrice(string memory cid) internal view returns (uint256) {
                  // LowerCaseCyberIdMiddleware ensures that each cid character only occupies 1 byte
                  uint256 len = bytes(cid).length;
                  uint256 usdPrice;
                  if (len >= 10) {
                      usdPrice = price10AndMoreLetter;
                  } else if (len >= 5) {
                      usdPrice = price5To9Letter;
                  } else if (len == 4) {
                      usdPrice = price4Letter;
                  } else if (len == 3) {
                      usdPrice = price3Letter;
                  } else {
                      revert("INVALID_LENGTH");
                  }
                  return usdPrice;
              }
              function _getPrice() internal view returns (int256) {
                  // prettier-ignore
                  (
                      uint80 roundID,
                      int price,
                      /* uint startedAt */,
                      uint updatedAt,
                      /*uint80 answeredInRound*/
                  ) = usdOracle.latestRoundData();
                  require(roundID != 0, "INVALID_ORACLE_ROUND_ID");
                  require(price > 0, "INVALID_ORACLE_PRICE");
                  require(updatedAt > block.timestamp - 24 hours, "STALE_ORACLE_PRICE");
                  return price;
              }
              function _attoUSDToWei(uint256 amount) internal view returns (uint256) {
                  uint256 ethPrice = uint256(_getPrice());
                  return (amount * 1e8) / ethPrice;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
          pragma solidity ^0.8.0;
          import "../utils/Context.sol";
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          abstract contract Ownable is Context {
              address private _owner;
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              /**
               * @dev Initializes the contract setting the deployer as the initial owner.
               */
              constructor() {
                  _transferOwnership(_msgSender());
              }
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  _checkOwner();
                  _;
              }
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view virtual returns (address) {
                  return _owner;
              }
              /**
               * @dev Throws if the sender is not the owner.
               */
              function _checkOwner() internal view virtual {
                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
              }
              /**
               * @dev Leaves the contract without owner. It will not be possible to call
               * `onlyOwner` functions anymore. Can only be called by the current owner.
               *
               * NOTE: Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public virtual onlyOwner {
                  _transferOwnership(address(0));
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                  _transferOwnership(newOwner);
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           */
          interface IERC20 {
              /**
               * @dev Emitted when `value` tokens are moved from one account (`from`) to
               * another (`to`).
               *
               * Note that `value` may be zero.
               */
              event Transfer(address indexed from, address indexed to, uint256 value);
              /**
               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
               * a call to {approve}. `value` is the new allowance.
               */
              event Approval(address indexed owner, address indexed spender, uint256 value);
              /**
               * @dev Returns the amount of tokens in existence.
               */
              function totalSupply() external view returns (uint256);
              /**
               * @dev Returns the amount of tokens owned by `account`.
               */
              function balanceOf(address account) external view returns (uint256);
              /**
               * @dev Moves `amount` tokens from the caller's account to `to`.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transfer(address to, uint256 amount) external returns (bool);
              /**
               * @dev Returns the remaining number of tokens that `spender` will be
               * allowed to spend on behalf of `owner` through {transferFrom}. This is
               * zero by default.
               *
               * This value changes when {approve} or {transferFrom} are called.
               */
              function allowance(address owner, address spender) external view returns (uint256);
              /**
               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * IMPORTANT: Beware that changing an allowance with this method brings the risk
               * that someone may use both the old and the new allowance by unfortunate
               * transaction ordering. One possible solution to mitigate this race
               * condition is to first reduce the spender's allowance to 0 and set the
               * desired value afterwards:
               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
               *
               * Emits an {Approval} event.
               */
              function approve(address spender, uint256 amount) external returns (bool);
              /**
               * @dev Moves `amount` tokens from `from` to `to` using the
               * allowance mechanism. `amount` is then deducted from the caller's
               * allowance.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(
                  address from,
                  address to,
                  uint256 amount
              ) external returns (bool);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
          pragma solidity ^0.8.0;
          import "../IERC20.sol";
          import "../extensions/draft-IERC20Permit.sol";
          import "../../../utils/Address.sol";
          /**
           * @title SafeERC20
           * @dev Wrappers around ERC20 operations that throw on failure (when the token
           * contract returns false). Tokens that return no value (and instead revert or
           * throw on failure) are also supported, non-reverting calls are assumed to be
           * successful.
           * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
           * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
           */
          library SafeERC20 {
              using Address for address;
              function safeTransfer(
                  IERC20 token,
                  address to,
                  uint256 value
              ) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
              }
              function safeTransferFrom(
                  IERC20 token,
                  address from,
                  address to,
                  uint256 value
              ) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
              }
              /**
               * @dev Deprecated. This function has issues similar to the ones found in
               * {IERC20-approve}, and its usage is discouraged.
               *
               * Whenever possible, use {safeIncreaseAllowance} and
               * {safeDecreaseAllowance} instead.
               */
              function safeApprove(
                  IERC20 token,
                  address spender,
                  uint256 value
              ) internal {
                  // safeApprove should only be called when setting an initial allowance,
                  // or when resetting it to zero. To increase and decrease it, use
                  // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                  require(
                      (value == 0) || (token.allowance(address(this), spender) == 0),
                      "SafeERC20: approve from non-zero to non-zero allowance"
                  );
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
              }
              function safeIncreaseAllowance(
                  IERC20 token,
                  address spender,
                  uint256 value
              ) internal {
                  uint256 newAllowance = token.allowance(address(this), spender) + value;
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              function safeDecreaseAllowance(
                  IERC20 token,
                  address spender,
                  uint256 value
              ) internal {
                  unchecked {
                      uint256 oldAllowance = token.allowance(address(this), spender);
                      require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                      uint256 newAllowance = oldAllowance - value;
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                  }
              }
              function safePermit(
                  IERC20Permit token,
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) internal {
                  uint256 nonceBefore = token.nonces(owner);
                  token.permit(owner, spender, value, deadline, v, r, s);
                  uint256 nonceAfter = token.nonces(owner);
                  require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
              }
              /**
               * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
               * on the return value: the return value is optional (but if data is returned, it must not be false).
               * @param token The token targeted by the call.
               * @param data The call data (encoded using abi.encode or one of its variants).
               */
              function _callOptionalReturn(IERC20 token, bytes memory data) private {
                  // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                  // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                  // the target address contains contract code and also asserts for success in the low-level call.
                  bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                  if (returndata.length > 0) {
                      // Return data is optional
                      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
              }
          }
          // SPDX-License-Identifier: GPL-3.0-or-later
          pragma solidity 0.8.14;
          struct RelayParams {
              address to;
              uint256 value;
              bytes callData;
          }
          /**
           * @title ICyberRelayGateHook
           * @author Cyber
           */
          interface ICyberRelayGateHook {
              function processRelay(
                  address msgSender,
                  address destination,
                  bytes calldata data
              ) external payable returns (RelayParams memory);
          }
          // SPDX-License-Identifier: GPL-3.0-or-later
          pragma solidity 0.8.14;
          struct BatchRegisterCyberIdParams {
              string cid;
              address to;
              bool setReverse;
          }
          interface ICyberID {
              function privilegedRegister(
                  BatchRegisterCyberIdParams[] calldata params
              ) external;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface AggregatorV3Interface {
              function decimals() external view returns (uint8);
              function description() external view returns (string memory);
              function version() external view returns (uint256);
              function getRoundData(
                  uint80 _roundId
              )
                  external
                  view
                  returns (
                      uint80 roundId,
                      int256 answer,
                      uint256 startedAt,
                      uint256 updatedAt,
                      uint80 answeredInRound
                  );
              function latestRoundData()
                  external
                  view
                  returns (
                      uint80 roundId,
                      int256 answer,
                      uint256 startedAt,
                      uint256 updatedAt,
                      uint80 answeredInRound
                  );
          }
          // SPDX-License-Identifier: GPL-3.0-or-later
          pragma solidity 0.8.14;
          library DataTypes {
              enum Category {
                  Essence,
                  Content,
                  W3ST,
                  Subscribe
              }
              enum ContentType {
                  Content,
                  Comment,
                  Share
              }
              struct EIP712Signature {
                  uint8 v;
                  bytes32 r;
                  bytes32 s;
                  uint256 deadline;
              }
              struct RegisterEssenceParams {
                  address account;
                  string name;
                  string symbol;
                  string tokenURI;
                  address mw;
                  bool transferable;
              }
              struct RegisterSubscriptionParams {
                  address account;
                  string name;
                  string symbol;
                  string tokenURI;
                  uint256 dayPerSub;
                  uint256 pricePerSub;
                  address recipient;
              }
              struct PublishContentParams {
                  address account;
                  string tokenURI;
                  address mw;
                  bool transferable;
              }
              struct ShareParams {
                  address account;
                  address accountShared;
                  uint256 idShared;
              }
              struct InitParams {
                  address soulAddr;
                  address mwManagerAddr;
                  address essImpl;
                  address contentImpl;
                  address w3stImpl;
                  address subImpl;
                  address adminAddr;
              }
              struct CommentParams {
                  address account;
                  string tokenURI;
                  address mw;
                  bool transferable;
                  address accountCommented;
                  uint256 idCommented;
              }
              struct IssueW3stParams {
                  address account;
                  string tokenURI;
                  address mw;
                  bool transferable;
              }
              struct EssenceStruct {
                  address essence;
                  address mw;
                  string name;
                  string symbol;
                  string tokenURI;
                  bool transferable;
              }
              struct SubscribeStruct {
                  address subscribe;
                  string name;
                  string symbol;
                  string tokenURI;
                  uint256 dayPerSub;
                  uint256 pricePerSub;
                  address recipient;
              }
              struct AccountStruct {
                  uint256 essenceCount;
                  address w3st;
                  uint256 w3stCount;
                  address content;
                  uint256 contentCount;
              }
              struct ContentStruct {
                  address mw;
                  string tokenURI;
                  bool transferable;
                  address srcAccount;
                  uint256 srcId;
                  ContentType contentType;
              }
              struct W3stStruct {
                  address mw;
                  string tokenURI;
                  bool transferable;
              }
              struct CollectParams {
                  address account;
                  uint256 id;
                  uint256 amount;
                  address to;
                  Category category;
              }
              struct MwParams {
                  address account;
                  Category category;
                  uint256 id;
                  uint256 amount;
                  address from;
                  address to;
                  address referrerAccount;
                  bytes data;
              }
              struct DeployParameters {
                  address engine;
              }
              struct MetadataPair {
                  string key;
                  string value;
              }
          }
          // SPDX-License-Identifier: GPL-3.0-or-later
          pragma solidity 0.8.14;
          import { DataTypes } from "../libraries/DataTypes.sol";
          abstract contract EIP712 {
              /*//////////////////////////////////////////////////////////////
                                          STATES
              //////////////////////////////////////////////////////////////*/
              bytes32 internal constant _HASHED_VERSION = keccak256("1");
              bytes32 private constant _TYPE_HASH =
                  keccak256(
                      "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                  );
              /*//////////////////////////////////////////////////////////////
                                      PUBLIC VIEW
              //////////////////////////////////////////////////////////////*/
              /**
               * @notice Returns the contract's {EIP712} domain separator.
               *
               * @return bytes32 the contract's {EIP712} domain separator.
               */
              // solhint-disable-next-line func-name-mixedcase
              function DOMAIN_SEPARATOR() public view returns (bytes32) {
                  return
                      keccak256(
                          abi.encode(
                              _TYPE_HASH,
                              keccak256(bytes(_domainSeparatorName())),
                              _HASHED_VERSION,
                              block.chainid,
                              address(this)
                          )
                      );
              }
              /*//////////////////////////////////////////////////////////////
                                        INTERNAL
              //////////////////////////////////////////////////////////////*/
              function _requiresExpectedSigner(
                  bytes32 digest,
                  address expectedSigner,
                  uint8 v,
                  bytes32 r,
                  bytes32 s,
                  uint256 deadline
              ) internal view {
                  require(deadline >= block.timestamp, "DEADLINE_EXCEEDED");
                  require(
                      uint256(s) <=
                          0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
                      "INVALID_SIGNATURE_S_VAULE"
                  );
                  address recoveredAddress = ecrecover(digest, v, r, s);
                  require(recoveredAddress == expectedSigner, "INVALID_SIGNATURE");
              }
              function _requiresExpectedSigner(
                  bytes32 digest,
                  address expectedSigner,
                  DataTypes.EIP712Signature calldata sig
              ) internal view {
                  _requiresExpectedSigner(
                      digest,
                      expectedSigner,
                      sig.v,
                      sig.r,
                      sig.s,
                      sig.deadline
                  );
              }
              function _hashTypedDataV4(
                  bytes32 structHash
              ) internal view virtual returns (bytes32) {
                  return
                      keccak256(
                          abi.encodePacked("\\x19\\x01", DOMAIN_SEPARATOR(), structHash)
                      );
              }
              function _domainSeparatorName()
                  internal
                  view
                  virtual
                  returns (string memory);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
           * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
           *
           * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
           * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
           * need to send a transaction, and thus is not required to hold Ether at all.
           */
          interface IERC20Permit {
              /**
               * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
               * given ``owner``'s signed approval.
               *
               * IMPORTANT: The same issues {IERC20-approve} has related to transaction
               * ordering also apply here.
               *
               * Emits an {Approval} event.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               * - `deadline` must be a timestamp in the future.
               * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
               * over the EIP712-formatted function arguments.
               * - the signature must use ``owner``'s current nonce (see {nonces}).
               *
               * For more information on the signature format, see the
               * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
               * section].
               */
              function permit(
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) external;
              /**
               * @dev Returns the current nonce for `owner`. This value must be
               * included whenever a signature is generated for {permit}.
               *
               * Every successful call to {permit} increases ``owner``'s nonce by one. This
               * prevents a signature from being used multiple times.
               */
              function nonces(address owner) external view returns (uint256);
              /**
               * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
               */
              // solhint-disable-next-line func-name-mixedcase
              function DOMAIN_SEPARATOR() external view returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.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
               * ====
               *
               * [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://diligence.consensys.net/posts/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.5.11/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);
                  }
              }
          }
          

          File 3 of 5: CyberRelayGate
          // SPDX-License-Identifier: GPL-3.0-or-later
          pragma solidity 0.8.14;
          import { IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
          import { SafeERC20 } from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
          import { Ownable } from "openzeppelin-contracts/contracts/access/Ownable.sol";
          import { Pausable } from "openzeppelin-contracts/contracts/security/Pausable.sol";
          import { Initializable } from "openzeppelin-contracts/contracts/proxy/utils/Initializable.sol";
          import { UUPSUpgradeable } from "openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol";
          import { RelayParams, ICyberRelayGateHook } from "../interfaces/ICyberRelayGateHook.sol";
          contract CyberRelayGate is Ownable, Pausable, Initializable, UUPSUpgradeable {
              using SafeERC20 for IERC20;
              /*//////////////////////////////////////////////////////////////
                                      STRUCT
              //////////////////////////////////////////////////////////////*/
              struct RelayDestination {
                  bool enabled;
                  ICyberRelayGateHook hook;
              }
              /*//////////////////////////////////////////////////////////////
                                      EVENT
              //////////////////////////////////////////////////////////////*/
              event Relay(
                  bytes32 requestId,
                  address from,
                  address destination,
                  uint256 value,
                  bytes callData
              );
              /*//////////////////////////////////////////////////////////////
                                      STORAGE
              //////////////////////////////////////////////////////////////*/
              mapping(address => mapping(bytes32 => bool)) public requestIdUsed;
              mapping(address => RelayDestination) public relayDestinations;
              /*//////////////////////////////////////////////////////////////
                                  CONSTRUCTOR & INITIALIZER
              //////////////////////////////////////////////////////////////*/
              constructor() {}
              function initialize(address _owner) external initializer {
                  _transferOwnership(_owner);
              }
              /*//////////////////////////////////////////////////////////////
                                           EXTERNAL
              //////////////////////////////////////////////////////////////*/
              function relayToCyber(
                  bytes32 requestId,
                  address destination,
                  bytes calldata data
              ) external payable whenNotPaused {
                  require(destination != address(0), "INVALID_ADDRESS_ZERO");
                  require(requestId != bytes32(0), "INVALID_REQUEST_ID");
                  RelayDestination memory relayDestination = relayDestinations[
                      destination
                  ];
                  require(relayDestination.enabled, "DESTINATION_DISABLED");
                  require(address(relayDestination.hook) != address(0), "HOOK_NOT_SET");
                  require(!requestIdUsed[destination][requestId], "REQUEST_ID_USED");
                  requestIdUsed[destination][requestId] = true;
                  uint256 valueBefore = address(this).balance;
                  RelayParams memory relayParams = relayDestination.hook.processRelay{
                      value: msg.value
                  }(msg.sender, destination, data);
                  require(
                      address(this).balance == valueBefore - msg.value,
                      "BALANCE_CHANGED"
                  );
                  emit Relay(
                      requestId,
                      msg.sender,
                      relayParams.to,
                      relayParams.value,
                      relayParams.callData
                  );
              }
              /*//////////////////////////////////////////////////////////////
                                      ONLY OWNER 
              //////////////////////////////////////////////////////////////*/
              function withdraw(address token) external onlyOwner {
                  if (token == address(0)) {
                      (bool success, ) = owner().call{ value: address(this).balance }("");
                      require(success, "WITHDRAW_FAILED");
                  } else {
                      IERC20(token).safeTransfer(
                          owner(),
                          IERC20(token).balanceOf(address(this))
                      );
                  }
              }
              function pause() external onlyOwner {
                  _pause();
              }
              function unpause() external onlyOwner {
                  _unpause();
              }
              function setDestination(
                  address destination,
                  bool enabled,
                  address hook
              ) external onlyOwner {
                  relayDestinations[destination] = RelayDestination(
                      enabled,
                      ICyberRelayGateHook(hook)
                  );
              }
              function _authorizeUpgrade(address) internal override onlyOwner {}
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC20 standard as defined in the EIP.
           */
          interface IERC20 {
              /**
               * @dev Emitted when `value` tokens are moved from one account (`from`) to
               * another (`to`).
               *
               * Note that `value` may be zero.
               */
              event Transfer(address indexed from, address indexed to, uint256 value);
              /**
               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
               * a call to {approve}. `value` is the new allowance.
               */
              event Approval(address indexed owner, address indexed spender, uint256 value);
              /**
               * @dev Returns the amount of tokens in existence.
               */
              function totalSupply() external view returns (uint256);
              /**
               * @dev Returns the amount of tokens owned by `account`.
               */
              function balanceOf(address account) external view returns (uint256);
              /**
               * @dev Moves `amount` tokens from the caller's account to `to`.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transfer(address to, uint256 amount) external returns (bool);
              /**
               * @dev Returns the remaining number of tokens that `spender` will be
               * allowed to spend on behalf of `owner` through {transferFrom}. This is
               * zero by default.
               *
               * This value changes when {approve} or {transferFrom} are called.
               */
              function allowance(address owner, address spender) external view returns (uint256);
              /**
               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * IMPORTANT: Beware that changing an allowance with this method brings the risk
               * that someone may use both the old and the new allowance by unfortunate
               * transaction ordering. One possible solution to mitigate this race
               * condition is to first reduce the spender's allowance to 0 and set the
               * desired value afterwards:
               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
               *
               * Emits an {Approval} event.
               */
              function approve(address spender, uint256 amount) external returns (bool);
              /**
               * @dev Moves `amount` tokens from `from` to `to` using the
               * allowance mechanism. `amount` is then deducted from the caller's
               * allowance.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(
                  address from,
                  address to,
                  uint256 amount
              ) external returns (bool);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
          pragma solidity ^0.8.0;
          import "../IERC20.sol";
          import "../extensions/draft-IERC20Permit.sol";
          import "../../../utils/Address.sol";
          /**
           * @title SafeERC20
           * @dev Wrappers around ERC20 operations that throw on failure (when the token
           * contract returns false). Tokens that return no value (and instead revert or
           * throw on failure) are also supported, non-reverting calls are assumed to be
           * successful.
           * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
           * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
           */
          library SafeERC20 {
              using Address for address;
              function safeTransfer(
                  IERC20 token,
                  address to,
                  uint256 value
              ) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
              }
              function safeTransferFrom(
                  IERC20 token,
                  address from,
                  address to,
                  uint256 value
              ) internal {
                  _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
              }
              /**
               * @dev Deprecated. This function has issues similar to the ones found in
               * {IERC20-approve}, and its usage is discouraged.
               *
               * Whenever possible, use {safeIncreaseAllowance} and
               * {safeDecreaseAllowance} instead.
               */
              function safeApprove(
                  IERC20 token,
                  address spender,
                  uint256 value
              ) internal {
                  // safeApprove should only be called when setting an initial allowance,
                  // or when resetting it to zero. To increase and decrease it, use
                  // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                  require(
                      (value == 0) || (token.allowance(address(this), spender) == 0),
                      "SafeERC20: approve from non-zero to non-zero allowance"
                  );
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
              }
              function safeIncreaseAllowance(
                  IERC20 token,
                  address spender,
                  uint256 value
              ) internal {
                  uint256 newAllowance = token.allowance(address(this), spender) + value;
                  _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
              }
              function safeDecreaseAllowance(
                  IERC20 token,
                  address spender,
                  uint256 value
              ) internal {
                  unchecked {
                      uint256 oldAllowance = token.allowance(address(this), spender);
                      require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                      uint256 newAllowance = oldAllowance - value;
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                  }
              }
              function safePermit(
                  IERC20Permit token,
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) internal {
                  uint256 nonceBefore = token.nonces(owner);
                  token.permit(owner, spender, value, deadline, v, r, s);
                  uint256 nonceAfter = token.nonces(owner);
                  require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
              }
              /**
               * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
               * on the return value: the return value is optional (but if data is returned, it must not be false).
               * @param token The token targeted by the call.
               * @param data The call data (encoded using abi.encode or one of its variants).
               */
              function _callOptionalReturn(IERC20 token, bytes memory data) private {
                  // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                  // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                  // the target address contains contract code and also asserts for success in the low-level call.
                  bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                  if (returndata.length > 0) {
                      // Return data is optional
                      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
          pragma solidity ^0.8.0;
          import "../utils/Context.sol";
          /**
           * @dev Contract module which provides a basic access control mechanism, where
           * there is an account (an owner) that can be granted exclusive access to
           * specific functions.
           *
           * By default, the owner account will be the one that deploys the contract. This
           * can later be changed with {transferOwnership}.
           *
           * This module is used through inheritance. It will make available the modifier
           * `onlyOwner`, which can be applied to your functions to restrict their use to
           * the owner.
           */
          abstract contract Ownable is Context {
              address private _owner;
              event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              /**
               * @dev Initializes the contract setting the deployer as the initial owner.
               */
              constructor() {
                  _transferOwnership(_msgSender());
              }
              /**
               * @dev Throws if called by any account other than the owner.
               */
              modifier onlyOwner() {
                  _checkOwner();
                  _;
              }
              /**
               * @dev Returns the address of the current owner.
               */
              function owner() public view virtual returns (address) {
                  return _owner;
              }
              /**
               * @dev Throws if the sender is not the owner.
               */
              function _checkOwner() internal view virtual {
                  require(owner() == _msgSender(), "Ownable: caller is not the owner");
              }
              /**
               * @dev Leaves the contract without owner. It will not be possible to call
               * `onlyOwner` functions anymore. Can only be called by the current owner.
               *
               * NOTE: Renouncing ownership will leave the contract without an owner,
               * thereby removing any functionality that is only available to the owner.
               */
              function renounceOwnership() public virtual onlyOwner {
                  _transferOwnership(address(0));
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Can only be called by the current owner.
               */
              function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), "Ownable: new owner is the zero address");
                  _transferOwnership(newOwner);
              }
              /**
               * @dev Transfers ownership of the contract to a new account (`newOwner`).
               * Internal function without access restriction.
               */
              function _transferOwnership(address newOwner) internal virtual {
                  address oldOwner = _owner;
                  _owner = newOwner;
                  emit OwnershipTransferred(oldOwner, newOwner);
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
          pragma solidity ^0.8.0;
          import "../utils/Context.sol";
          /**
           * @dev Contract module which allows children to implement an emergency stop
           * mechanism that can be triggered by an authorized account.
           *
           * This module is used through inheritance. It will make available the
           * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
           * the functions of your contract. Note that they will not be pausable by
           * simply including this module, only once the modifiers are put in place.
           */
          abstract contract Pausable is Context {
              /**
               * @dev Emitted when the pause is triggered by `account`.
               */
              event Paused(address account);
              /**
               * @dev Emitted when the pause is lifted by `account`.
               */
              event Unpaused(address account);
              bool private _paused;
              /**
               * @dev Initializes the contract in unpaused state.
               */
              constructor() {
                  _paused = false;
              }
              /**
               * @dev Modifier to make a function callable only when the contract is not paused.
               *
               * Requirements:
               *
               * - The contract must not be paused.
               */
              modifier whenNotPaused() {
                  _requireNotPaused();
                  _;
              }
              /**
               * @dev Modifier to make a function callable only when the contract is paused.
               *
               * Requirements:
               *
               * - The contract must be paused.
               */
              modifier whenPaused() {
                  _requirePaused();
                  _;
              }
              /**
               * @dev Returns true if the contract is paused, and false otherwise.
               */
              function paused() public view virtual returns (bool) {
                  return _paused;
              }
              /**
               * @dev Throws if the contract is paused.
               */
              function _requireNotPaused() internal view virtual {
                  require(!paused(), "Pausable: paused");
              }
              /**
               * @dev Throws if the contract is not paused.
               */
              function _requirePaused() internal view virtual {
                  require(paused(), "Pausable: not paused");
              }
              /**
               * @dev Triggers stopped state.
               *
               * Requirements:
               *
               * - The contract must not be paused.
               */
              function _pause() internal virtual whenNotPaused {
                  _paused = true;
                  emit Paused(_msgSender());
              }
              /**
               * @dev Returns to normal state.
               *
               * Requirements:
               *
               * - The contract must be paused.
               */
              function _unpause() internal virtual whenPaused {
                  _paused = false;
                  emit Unpaused(_msgSender());
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)
          pragma solidity ^0.8.2;
          import "../../utils/Address.sol";
          /**
           * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
           * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
           * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
           * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
           *
           * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
           * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
           * case an upgrade adds a module that needs to be initialized.
           *
           * For example:
           *
           * [.hljs-theme-light.nopadding]
           * ```
           * contract MyToken is ERC20Upgradeable {
           *     function initialize() initializer public {
           *         __ERC20_init("MyToken", "MTK");
           *     }
           * }
           * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
           *     function initializeV2() reinitializer(2) public {
           *         __ERC20Permit_init("MyToken");
           *     }
           * }
           * ```
           *
           * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
           * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
           *
           * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
           * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
           *
           * [CAUTION]
           * ====
           * Avoid leaving a contract uninitialized.
           *
           * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
           * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
           * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
           *
           * [.hljs-theme-light.nopadding]
           * ```
           * /// @custom:oz-upgrades-unsafe-allow constructor
           * constructor() {
           *     _disableInitializers();
           * }
           * ```
           * ====
           */
          abstract contract Initializable {
              /**
               * @dev Indicates that the contract has been initialized.
               * @custom:oz-retyped-from bool
               */
              uint8 private _initialized;
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool private _initializing;
              /**
               * @dev Triggered when the contract has been initialized or reinitialized.
               */
              event Initialized(uint8 version);
              /**
               * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
               * `onlyInitializing` functions can be used to initialize parent contracts.
               *
               * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
               * constructor.
               *
               * Emits an {Initialized} event.
               */
              modifier initializer() {
                  bool isTopLevelCall = !_initializing;
                  require(
                      (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
                      "Initializable: contract is already initialized"
                  );
                  _initialized = 1;
                  if (isTopLevelCall) {
                      _initializing = true;
                  }
                  _;
                  if (isTopLevelCall) {
                      _initializing = false;
                      emit Initialized(1);
                  }
              }
              /**
               * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
               * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
               * used to initialize parent contracts.
               *
               * A reinitializer may be used after the original initialization step. This is essential to configure modules that
               * are added through upgrades and that require initialization.
               *
               * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
               * cannot be nested. If one is invoked in the context of another, execution will revert.
               *
               * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
               * a contract, executing them in the right order is up to the developer or operator.
               *
               * WARNING: setting the version to 255 will prevent any future reinitialization.
               *
               * Emits an {Initialized} event.
               */
              modifier reinitializer(uint8 version) {
                  require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                  _initialized = version;
                  _initializing = true;
                  _;
                  _initializing = false;
                  emit Initialized(version);
              }
              /**
               * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
               * {initializer} and {reinitializer} modifiers, directly or indirectly.
               */
              modifier onlyInitializing() {
                  require(_initializing, "Initializable: contract is not initializing");
                  _;
              }
              /**
               * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
               * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
               * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
               * through proxies.
               *
               * Emits an {Initialized} event the first time it is successfully executed.
               */
              function _disableInitializers() internal virtual {
                  require(!_initializing, "Initializable: contract is initializing");
                  if (_initialized < type(uint8).max) {
                      _initialized = type(uint8).max;
                      emit Initialized(type(uint8).max);
                  }
              }
              /**
               * @dev Returns the highest version that has been initialized. See {reinitializer}.
               */
              function _getInitializedVersion() internal view returns (uint8) {
                  return _initialized;
              }
              /**
               * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
               */
              function _isInitializing() internal view returns (bool) {
                  return _initializing;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/UUPSUpgradeable.sol)
          pragma solidity ^0.8.0;
          import "../../interfaces/draft-IERC1822.sol";
          import "../ERC1967/ERC1967Upgrade.sol";
          /**
           * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
           * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
           *
           * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
           * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
           * `UUPSUpgradeable` with a custom implementation of upgrades.
           *
           * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
           *
           * _Available since v4.1._
           */
          abstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade {
              /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
              address private immutable __self = address(this);
              /**
               * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
               * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
               * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
               * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
               * fail.
               */
              modifier onlyProxy() {
                  require(address(this) != __self, "Function must be called through delegatecall");
                  require(_getImplementation() == __self, "Function must be called through active proxy");
                  _;
              }
              /**
               * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
               * callable on the implementing contract but not through proxies.
               */
              modifier notDelegated() {
                  require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
                  _;
              }
              /**
               * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
               * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
               *
               * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
               * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
               * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
               */
              function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
                  return _IMPLEMENTATION_SLOT;
              }
              /**
               * @dev Upgrade the implementation of the proxy to `newImplementation`.
               *
               * Calls {_authorizeUpgrade}.
               *
               * Emits an {Upgraded} event.
               */
              function upgradeTo(address newImplementation) external virtual onlyProxy {
                  _authorizeUpgrade(newImplementation);
                  _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
              }
              /**
               * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
               * encoded in `data`.
               *
               * Calls {_authorizeUpgrade}.
               *
               * Emits an {Upgraded} event.
               */
              function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
                  _authorizeUpgrade(newImplementation);
                  _upgradeToAndCallUUPS(newImplementation, data, true);
              }
              /**
               * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
               * {upgradeTo} and {upgradeToAndCall}.
               *
               * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
               *
               * ```solidity
               * function _authorizeUpgrade(address) internal override onlyOwner {}
               * ```
               */
              function _authorizeUpgrade(address newImplementation) internal virtual;
          }
          // SPDX-License-Identifier: GPL-3.0-or-later
          pragma solidity 0.8.14;
          struct RelayParams {
              address to;
              uint256 value;
              bytes callData;
          }
          /**
           * @title ICyberRelayGateHook
           * @author Cyber
           */
          interface ICyberRelayGateHook {
              function processRelay(
                  address msgSender,
                  address destination,
                  bytes calldata data
              ) external payable returns (RelayParams memory);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
           * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
           *
           * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
           * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
           * need to send a transaction, and thus is not required to hold Ether at all.
           */
          interface IERC20Permit {
              /**
               * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
               * given ``owner``'s signed approval.
               *
               * IMPORTANT: The same issues {IERC20-approve} has related to transaction
               * ordering also apply here.
               *
               * Emits an {Approval} event.
               *
               * Requirements:
               *
               * - `spender` cannot be the zero address.
               * - `deadline` must be a timestamp in the future.
               * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
               * over the EIP712-formatted function arguments.
               * - the signature must use ``owner``'s current nonce (see {nonces}).
               *
               * For more information on the signature format, see the
               * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
               * section].
               */
              function permit(
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
              ) external;
              /**
               * @dev Returns the current nonce for `owner`. This value must be
               * included whenever a signature is generated for {permit}.
               *
               * Every successful call to {permit} increases ``owner``'s nonce by one. This
               * prevents a signature from being used multiple times.
               */
              function nonces(address owner) external view returns (uint256);
              /**
               * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
               */
              // solhint-disable-next-line func-name-mixedcase
              function DOMAIN_SEPARATOR() external view returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.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
               * ====
               *
               * [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://diligence.consensys.net/posts/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.5.11/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
          // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Provides information about the current execution context, including the
           * sender of the transaction and its data. While these are generally available
           * via msg.sender and msg.data, they should not be accessed in such a direct
           * manner, since when dealing with meta-transactions the account sending and
           * paying for execution may not be the actual sender (as far as an application
           * is concerned).
           *
           * This contract is only required for intermediate, library-like contracts.
           */
          abstract contract Context {
              function _msgSender() internal view virtual returns (address) {
                  return msg.sender;
              }
              function _msgData() internal view virtual returns (bytes calldata) {
                  return msg.data;
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
           * proxy whose upgrades are fully controlled by the current implementation.
           */
          interface IERC1822Proxiable {
              /**
               * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
               * address.
               *
               * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
               * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
               * function revert if invoked through a proxy.
               */
              function proxiableUUID() external view returns (bytes32);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.3) (proxy/ERC1967/ERC1967Upgrade.sol)
          pragma solidity ^0.8.2;
          import "../beacon/IBeacon.sol";
          import "../../interfaces/IERC1967.sol";
          import "../../interfaces/draft-IERC1822.sol";
          import "../../utils/Address.sol";
          import "../../utils/StorageSlot.sol";
          /**
           * @dev This abstract contract provides getters and event emitting update functions for
           * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
           *
           * _Available since v4.1._
           *
           * @custom:oz-upgrades-unsafe-allow delegatecall
           */
          abstract contract ERC1967Upgrade is IERC1967 {
              // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
              bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
              /**
               * @dev Storage slot with the address of the current implementation.
               * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
               * validated in the constructor.
               */
              bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
              /**
               * @dev Returns the current implementation address.
               */
              function _getImplementation() internal view returns (address) {
                  return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
              }
              /**
               * @dev Stores a new address in the EIP1967 implementation slot.
               */
              function _setImplementation(address newImplementation) private {
                  require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                  StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
              }
              /**
               * @dev Perform implementation upgrade
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeTo(address newImplementation) internal {
                  _setImplementation(newImplementation);
                  emit Upgraded(newImplementation);
              }
              /**
               * @dev Perform implementation upgrade with additional setup call.
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeToAndCall(
                  address newImplementation,
                  bytes memory data,
                  bool forceCall
              ) internal {
                  _upgradeTo(newImplementation);
                  if (data.length > 0 || forceCall) {
                      Address.functionDelegateCall(newImplementation, data);
                  }
              }
              /**
               * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
               *
               * Emits an {Upgraded} event.
               */
              function _upgradeToAndCallUUPS(
                  address newImplementation,
                  bytes memory data,
                  bool forceCall
              ) internal {
                  // Upgrades from old implementations will perform a rollback test. This test requires the new
                  // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                  // this special case will break upgrade paths from old UUPS implementation to new ones.
                  if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                      _setImplementation(newImplementation);
                  } else {
                      try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                          require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                      } catch {
                          revert("ERC1967Upgrade: new implementation is not UUPS");
                      }
                      _upgradeToAndCall(newImplementation, data, forceCall);
                  }
              }
              /**
               * @dev Storage slot with the admin of the contract.
               * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
               * validated in the constructor.
               */
              bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
              /**
               * @dev Returns the current admin.
               */
              function _getAdmin() internal view returns (address) {
                  return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
              }
              /**
               * @dev Stores a new address in the EIP1967 admin slot.
               */
              function _setAdmin(address newAdmin) private {
                  require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                  StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
              }
              /**
               * @dev Changes the admin of the proxy.
               *
               * Emits an {AdminChanged} event.
               */
              function _changeAdmin(address newAdmin) internal {
                  emit AdminChanged(_getAdmin(), newAdmin);
                  _setAdmin(newAdmin);
              }
              /**
               * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
               * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
               */
              bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
              /**
               * @dev Returns the current beacon.
               */
              function _getBeacon() internal view returns (address) {
                  return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
              }
              /**
               * @dev Stores a new beacon in the EIP1967 beacon slot.
               */
              function _setBeacon(address newBeacon) private {
                  require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                  require(
                      Address.isContract(IBeacon(newBeacon).implementation()),
                      "ERC1967: beacon implementation is not a contract"
                  );
                  StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
              }
              /**
               * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
               * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
               *
               * Emits a {BeaconUpgraded} event.
               */
              function _upgradeBeaconToAndCall(
                  address newBeacon,
                  bytes memory data,
                  bool forceCall
              ) internal {
                  _setBeacon(newBeacon);
                  emit BeaconUpgraded(newBeacon);
                  if (data.length > 0 || forceCall) {
                      Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                  }
              }
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev This is the interface that {BeaconProxy} expects of its beacon.
           */
          interface IBeacon {
              /**
               * @dev Must return an address that can be used as a delegate call target.
               *
               * {BeaconProxy} will check that this address is a contract.
               */
              function implementation() external view returns (address);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.8.3) (interfaces/IERC1967.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
           *
           * _Available since v4.9._
           */
          interface IERC1967 {
              /**
               * @dev Emitted when the implementation is upgraded.
               */
              event Upgraded(address indexed implementation);
              /**
               * @dev Emitted when the admin account has changed.
               */
              event AdminChanged(address previousAdmin, address newAdmin);
              /**
               * @dev Emitted when the beacon is changed.
               */
              event BeaconUpgraded(address indexed beacon);
          }
          // SPDX-License-Identifier: MIT
          // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
          pragma solidity ^0.8.0;
          /**
           * @dev Library for reading and writing primitive types to specific storage slots.
           *
           * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
           * This library helps with reading and writing to such slots without the need for inline assembly.
           *
           * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
           *
           * Example usage to set ERC1967 implementation slot:
           * ```
           * contract ERC1967 {
           *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
           *
           *     function _getImplementation() internal view returns (address) {
           *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
           *     }
           *
           *     function _setImplementation(address newImplementation) internal {
           *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
           *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
           *     }
           * }
           * ```
           *
           * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
           */
          library StorageSlot {
              struct AddressSlot {
                  address value;
              }
              struct BooleanSlot {
                  bool value;
              }
              struct Bytes32Slot {
                  bytes32 value;
              }
              struct Uint256Slot {
                  uint256 value;
              }
              /**
               * @dev Returns an `AddressSlot` with member `value` located at `slot`.
               */
              function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
               */
              function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
               */
              function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
              /**
               * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
               */
              function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                  /// @solidity memory-safe-assembly
                  assembly {
                      r.slot := slot
                  }
              }
          }
          

          File 4 of 5: EACAggregatorProxy
          pragma solidity 0.6.6;
          
          
          /**
           * @title The Owned contract
           * @notice A contract with helpers for basic contract ownership.
           */
          contract Owned {
          
            address payable public owner;
            address private pendingOwner;
          
            event OwnershipTransferRequested(
              address indexed from,
              address indexed to
            );
            event OwnershipTransferred(
              address indexed from,
              address indexed to
            );
          
            constructor() public {
              owner = msg.sender;
            }
          
            /**
             * @dev Allows an owner to begin transferring ownership to a new address,
             * pending.
             */
            function transferOwnership(address _to)
              external
              onlyOwner()
            {
              pendingOwner = _to;
          
              emit OwnershipTransferRequested(owner, _to);
            }
          
            /**
             * @dev Allows an ownership transfer to be completed by the recipient.
             */
            function acceptOwnership()
              external
            {
              require(msg.sender == pendingOwner, "Must be proposed owner");
          
              address oldOwner = owner;
              owner = msg.sender;
              pendingOwner = address(0);
          
              emit OwnershipTransferred(oldOwner, msg.sender);
            }
          
            /**
             * @dev Reverts if called by anyone other than the contract owner.
             */
            modifier onlyOwner() {
              require(msg.sender == owner, "Only callable by owner");
              _;
            }
          
          }
          
          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);
          }
          
          interface AggregatorV3Interface {
          
            function decimals() external view returns (uint8);
            function description() external view returns (string memory);
            function version() external view returns (uint256);
          
            // getRoundData and latestRoundData should both raise "No data present"
            // if they do not have data to report, instead of returning unset values
            // which could be misinterpreted as actual reported values.
            function getRoundData(uint80 _roundId)
              external
              view
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              );
            function latestRoundData()
              external
              view
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              );
          
          }
          
          interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
          {
          }
          
          /**
           * @title A trusted proxy for updating where current answers are read from
           * @notice This contract provides a consistent address for the
           * CurrentAnwerInterface but delegates where it reads from to the owner, who is
           * trusted to update it.
           */
          contract AggregatorProxy is AggregatorV2V3Interface, Owned {
          
            struct Phase {
              uint16 id;
              AggregatorV2V3Interface aggregator;
            }
            Phase private currentPhase;
            AggregatorV2V3Interface public proposedAggregator;
            mapping(uint16 => AggregatorV2V3Interface) public phaseAggregators;
          
            uint256 constant private PHASE_OFFSET = 64;
            uint256 constant private PHASE_SIZE = 16;
            uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1;
          
            constructor(address _aggregator) public Owned() {
              setAggregator(_aggregator);
            }
          
            /**
             * @notice Reads the current answer from aggregator delegated to.
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestAnswer()
              public
              view
              virtual
              override
              returns (int256 answer)
            {
              return currentPhase.aggregator.latestAnswer();
            }
          
            /**
             * @notice Reads the last updated height from aggregator delegated to.
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestTimestamp()
              public
              view
              virtual
              override
              returns (uint256 updatedAt)
            {
              return currentPhase.aggregator.latestTimestamp();
            }
          
            /**
             * @notice get past rounds answers
             * @param _roundId the answer number to retrieve the answer for
             *
             * @dev #[deprecated] Use getRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended getRoundData
             * instead which includes better verification information.
             */
            function getAnswer(uint256 _roundId)
              public
              view
              virtual
              override
              returns (int256 answer)
            {
              if (_roundId > MAX_ID) return 0;
          
              (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
              AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
              if (address(aggregator) == address(0)) return 0;
          
              return aggregator.getAnswer(aggregatorRoundId);
            }
          
            /**
             * @notice get block timestamp when an answer was last updated
             * @param _roundId the answer number to retrieve the updated timestamp for
             *
             * @dev #[deprecated] Use getRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended getRoundData
             * instead which includes better verification information.
             */
            function getTimestamp(uint256 _roundId)
              public
              view
              virtual
              override
              returns (uint256 updatedAt)
            {
              if (_roundId > MAX_ID) return 0;
          
              (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
              AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
              if (address(aggregator) == address(0)) return 0;
          
              return aggregator.getTimestamp(aggregatorRoundId);
            }
          
            /**
             * @notice get the latest completed round where the answer was updated. This
             * ID includes the proxy's phase, to make sure round IDs increase even when
             * switching to a newly deployed aggregator.
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestRound()
              public
              view
              virtual
              override
              returns (uint256 roundId)
            {
              Phase memory phase = currentPhase; // cache storage reads
              return addPhase(phase.id, uint64(phase.aggregator.latestRound()));
            }
          
            /**
             * @notice get data about a round. Consumers are encouraged to check
             * that they're receiving fresh data by inspecting the updatedAt and
             * answeredInRound return values.
             * Note that different underlying implementations of AggregatorV3Interface
             * have slightly different semantics for some of the return values. Consumers
             * should determine what implementations they expect to receive
             * data from and validate that they can properly handle return data from all
             * of them.
             * @param _roundId the requested round ID as presented through the proxy, this
             * is made up of the aggregator's round ID with the phase ID encoded in the
             * two highest order bytes
             * @return roundId is the round ID from the aggregator for which the data was
             * retrieved combined with an phase to ensure that round IDs get larger as
             * time moves forward.
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @dev Note that answer and updatedAt may change between queries.
             */
            function getRoundData(uint80 _roundId)
              public
              view
              virtual
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
          
              (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 ansIn
              ) = phaseAggregators[phaseId].getRoundData(aggregatorRoundId);
          
              return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, phaseId);
            }
          
            /**
             * @notice get data about the latest round. Consumers are encouraged to check
             * that they're receiving fresh data by inspecting the updatedAt and
             * answeredInRound return values.
             * Note that different underlying implementations of AggregatorV3Interface
             * have slightly different semantics for some of the return values. Consumers
             * should determine what implementations they expect to receive
             * data from and validate that they can properly handle return data from all
             * of them.
             * @return roundId is the round ID from the aggregator for which the data was
             * retrieved combined with an phase to ensure that round IDs get larger as
             * time moves forward.
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @dev Note that answer and updatedAt may change between queries.
             */
            function latestRoundData()
              public
              view
              virtual
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              Phase memory current = currentPhase; // cache storage reads
          
              (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 ansIn
              ) = current.aggregator.latestRoundData();
          
              return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, current.id);
            }
          
            /**
             * @notice Used if an aggregator contract has been proposed.
             * @param _roundId the round ID to retrieve the round data for
             * @return roundId is the round ID for which data was retrieved
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
            */
            function proposedGetRoundData(uint80 _roundId)
              public
              view
              virtual
              hasProposal()
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return proposedAggregator.getRoundData(_roundId);
            }
          
            /**
             * @notice Used if an aggregator contract has been proposed.
             * @return roundId is the round ID for which data was retrieved
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
            */
            function proposedLatestRoundData()
              public
              view
              virtual
              hasProposal()
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return proposedAggregator.latestRoundData();
            }
          
            /**
             * @notice returns the current phase's aggregator address.
             */
            function aggregator()
              external
              view
              returns (address)
            {
              return address(currentPhase.aggregator);
            }
          
            /**
             * @notice returns the current phase's ID.
             */
            function phaseId()
              external
              view
              returns (uint16)
            {
              return currentPhase.id;
            }
          
            /**
             * @notice represents the number of decimals the aggregator responses represent.
             */
            function decimals()
              external
              view
              override
              returns (uint8)
            {
              return currentPhase.aggregator.decimals();
            }
          
            /**
             * @notice the version number representing the type of aggregator the proxy
             * points to.
             */
            function version()
              external
              view
              override
              returns (uint256)
            {
              return currentPhase.aggregator.version();
            }
          
            /**
             * @notice returns the description of the aggregator the proxy points to.
             */
            function description()
              external
              view
              override
              returns (string memory)
            {
              return currentPhase.aggregator.description();
            }
          
            /**
             * @notice Allows the owner to propose a new address for the aggregator
             * @param _aggregator The new address for the aggregator contract
             */
            function proposeAggregator(address _aggregator)
              external
              onlyOwner()
            {
              proposedAggregator = AggregatorV2V3Interface(_aggregator);
            }
          
            /**
             * @notice Allows the owner to confirm and change the address
             * to the proposed aggregator
             * @dev Reverts if the given address doesn't match what was previously
             * proposed
             * @param _aggregator The new address for the aggregator contract
             */
            function confirmAggregator(address _aggregator)
              external
              onlyOwner()
            {
              require(_aggregator == address(proposedAggregator), "Invalid proposed aggregator");
              delete proposedAggregator;
              setAggregator(_aggregator);
            }
          
          
            /*
             * Internal
             */
          
            function setAggregator(address _aggregator)
              internal
            {
              uint16 id = currentPhase.id + 1;
              currentPhase = Phase(id, AggregatorV2V3Interface(_aggregator));
              phaseAggregators[id] = AggregatorV2V3Interface(_aggregator);
            }
          
            function addPhase(
              uint16 _phase,
              uint64 _originalId
            )
              internal
              view
              returns (uint80)
            {
              return uint80(uint256(_phase) << PHASE_OFFSET | _originalId);
            }
          
            function parseIds(
              uint256 _roundId
            )
              internal
              view
              returns (uint16, uint64)
            {
              uint16 phaseId = uint16(_roundId >> PHASE_OFFSET);
              uint64 aggregatorRoundId = uint64(_roundId);
          
              return (phaseId, aggregatorRoundId);
            }
          
            function addPhaseIds(
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound,
                uint16 phaseId
            )
              internal
              view
              returns (uint80, int256, uint256, uint256, uint80)
            {
              return (
                addPhase(phaseId, uint64(roundId)),
                answer,
                startedAt,
                updatedAt,
                addPhase(phaseId, uint64(answeredInRound))
              );
            }
          
            /*
             * Modifiers
             */
          
            modifier hasProposal() {
              require(address(proposedAggregator) != address(0), "No proposed aggregator present");
              _;
            }
          
          }
          
          interface AccessControllerInterface {
            function hasAccess(address user, bytes calldata data) external view returns (bool);
          }
          
          /**
           * @title External Access Controlled Aggregator Proxy
           * @notice A trusted proxy for updating where current answers are read from
           * @notice This contract provides a consistent address for the
           * Aggregator and AggregatorV3Interface but delegates where it reads from to the owner, who is
           * trusted to update it.
           * @notice Only access enabled addresses are allowed to access getters for
           * aggregated answers and round information.
           */
          contract EACAggregatorProxy is AggregatorProxy {
          
            AccessControllerInterface public accessController;
          
            constructor(
              address _aggregator,
              address _accessController
            )
              public
              AggregatorProxy(_aggregator)
            {
              setController(_accessController);
            }
          
            /**
             * @notice Allows the owner to update the accessController contract address.
             * @param _accessController The new address for the accessController contract
             */
            function setController(address _accessController)
              public
              onlyOwner()
            {
              accessController = AccessControllerInterface(_accessController);
            }
          
            /**
             * @notice Reads the current answer from aggregator delegated to.
             * @dev overridden function to add the checkAccess() modifier
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestAnswer()
              public
              view
              override
              checkAccess()
              returns (int256)
            {
              return super.latestAnswer();
            }
          
            /**
             * @notice get the latest completed round where the answer was updated. This
             * ID includes the proxy's phase, to make sure round IDs increase even when
             * switching to a newly deployed aggregator.
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestTimestamp()
              public
              view
              override
              checkAccess()
              returns (uint256)
            {
              return super.latestTimestamp();
            }
          
            /**
             * @notice get past rounds answers
             * @param _roundId the answer number to retrieve the answer for
             * @dev overridden function to add the checkAccess() modifier
             *
             * @dev #[deprecated] Use getRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended getRoundData
             * instead which includes better verification information.
             */
            function getAnswer(uint256 _roundId)
              public
              view
              override
              checkAccess()
              returns (int256)
            {
              return super.getAnswer(_roundId);
            }
          
            /**
             * @notice get block timestamp when an answer was last updated
             * @param _roundId the answer number to retrieve the updated timestamp for
             * @dev overridden function to add the checkAccess() modifier
             *
             * @dev #[deprecated] Use getRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended getRoundData
             * instead which includes better verification information.
             */
            function getTimestamp(uint256 _roundId)
              public
              view
              override
              checkAccess()
              returns (uint256)
            {
              return super.getTimestamp(_roundId);
            }
          
            /**
             * @notice get the latest completed round where the answer was updated
             * @dev overridden function to add the checkAccess() modifier
             *
             * @dev #[deprecated] Use latestRoundData instead. This does not error if no
             * answer has been reached, it will simply return 0. Either wait to point to
             * an already answered Aggregator or use the recommended latestRoundData
             * instead which includes better verification information.
             */
            function latestRound()
              public
              view
              override
              checkAccess()
              returns (uint256)
            {
              return super.latestRound();
            }
          
            /**
             * @notice get data about a round. Consumers are encouraged to check
             * that they're receiving fresh data by inspecting the updatedAt and
             * answeredInRound return values.
             * Note that different underlying implementations of AggregatorV3Interface
             * have slightly different semantics for some of the return values. Consumers
             * should determine what implementations they expect to receive
             * data from and validate that they can properly handle return data from all
             * of them.
             * @param _roundId the round ID to retrieve the round data for
             * @return roundId is the round ID from the aggregator for which the data was
             * retrieved combined with a phase to ensure that round IDs get larger as
             * time moves forward.
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @dev Note that answer and updatedAt may change between queries.
             */
            function getRoundData(uint80 _roundId)
              public
              view
              checkAccess()
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.getRoundData(_roundId);
            }
          
            /**
             * @notice get data about the latest round. Consumers are encouraged to check
             * that they're receiving fresh data by inspecting the updatedAt and
             * answeredInRound return values.
             * Note that different underlying implementations of AggregatorV3Interface
             * have slightly different semantics for some of the return values. Consumers
             * should determine what implementations they expect to receive
             * data from and validate that they can properly handle return data from all
             * of them.
             * @return roundId is the round ID from the aggregator for which the data was
             * retrieved combined with a phase to ensure that round IDs get larger as
             * time moves forward.
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @dev Note that answer and updatedAt may change between queries.
             */
            function latestRoundData()
              public
              view
              checkAccess()
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.latestRoundData();
            }
          
            /**
             * @notice Used if an aggregator contract has been proposed.
             * @param _roundId the round ID to retrieve the round data for
             * @return roundId is the round ID for which data was retrieved
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
            */
            function proposedGetRoundData(uint80 _roundId)
              public
              view
              checkAccess()
              hasProposal()
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.proposedGetRoundData(_roundId);
            }
          
            /**
             * @notice Used if an aggregator contract has been proposed.
             * @return roundId is the round ID for which data was retrieved
             * @return answer is the answer for the given round
             * @return startedAt is the timestamp when the round was started.
             * (Only some AggregatorV3Interface implementations return meaningful values)
             * @return updatedAt is the timestamp when the round last was updated (i.e.
             * answer was last computed)
             * @return answeredInRound is the round ID of the round in which the answer
             * was computed.
            */
            function proposedLatestRoundData()
              public
              view
              checkAccess()
              hasProposal()
              override
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.proposedLatestRoundData();
            }
          
            /**
             * @dev reverts if the caller does not have access by the accessController
             * contract or is the contract itself.
             */
            modifier checkAccess() {
              AccessControllerInterface ac = accessController;
              require(address(ac) == address(0) || ac.hasAccess(msg.sender, msg.data), "No access");
              _;
            }
          }

          File 5 of 5: AccessControlledOCR2Aggregator
          // SPDX-License-Identifier: MIT
          pragma solidity =0.8.19;
          import "./OCR2Aggregator.sol";
          import "./SimpleReadAccessController.sol";
          /**
           * @notice Wrapper of OCR2Aggregator which checks read access on Aggregator-interface methods
           */
          contract AccessControlledOCR2Aggregator is OCR2Aggregator, SimpleReadAccessController {
            constructor(
              LinkTokenInterface _link,
              int192 _minAnswer,
              int192 _maxAnswer,
              AccessControllerInterface _billingAccessController,
              AccessControllerInterface _requesterAccessController,
              uint8 _decimals,
              string memory description
            )
              OCR2Aggregator(
                _link,
                _minAnswer,
                _maxAnswer,
                _billingAccessController,
                _requesterAccessController,
                _decimals,
                description
              ) {
              }
            /*
             * Versioning
             */
            function typeAndVersion()
              external
              override
              pure
              virtual
              returns (string memory)
            {
              return "AccessControlledOCR2Aggregator 1.0.0";
            }
            /*
             * v2 Aggregator interface
             */
            /// @inheritdoc OCR2Aggregator
            function latestAnswer()
              public
              override
              view
              checkAccess()
              returns (int256)
            {
              return super.latestAnswer();
            }
            /// @inheritdoc OCR2Aggregator
            function latestTimestamp()
              public
              override
              view
              checkAccess()
              returns (uint256)
            {
              return super.latestTimestamp();
            }
            /// @inheritdoc OCR2Aggregator
            function latestRound()
              public
              override
              view
              checkAccess()
              returns (uint256)
            {
              return super.latestRound();
            }
            /// @inheritdoc OCR2Aggregator
            function getAnswer(uint256 _roundId)
              public
              override
              view
              checkAccess()
              returns (int256)
            {
              return super.getAnswer(_roundId);
            }
            /// @inheritdoc OCR2Aggregator
            function getTimestamp(uint256 _roundId)
              public
              override
              view
              checkAccess()
              returns (uint256)
            {
              return super.getTimestamp(_roundId);
            }
            /*
             * v3 Aggregator interface
             */
            /// @inheritdoc OCR2Aggregator
            function description()
              public
              override
              view
              checkAccess()
              returns (string memory)
            {
              return super.description();
            }
            /// @inheritdoc OCR2Aggregator
            function getRoundData(uint80 _roundId)
              public
              override
              view
              checkAccess()
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.getRoundData(_roundId);
            }
            /// @inheritdoc OCR2Aggregator
            function latestRoundData()
              public
              override
              view
              checkAccess()
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              return super.latestRoundData();
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./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 "./interfaces/OwnableInterface.sol";
          /**
           * @title The ConfirmedOwner contract
           * @notice A contract with helpers for basic contract ownership.
           */
          contract ConfirmedOwnerWithProposal is OwnableInterface {
            address private s_owner;
            address private s_pendingOwner;
            event OwnershipTransferRequested(
              address indexed from,
              address indexed to
            );
            event OwnershipTransferred(
              address indexed from,
              address indexed to
            );
            constructor(
              address newOwner,
              address pendingOwner
            ) {
              require(newOwner != address(0), "Cannot set owner to zero");
              s_owner = newOwner;
              if (pendingOwner != address(0)) {
                _transferOwnership(pendingOwner);
              }
            }
            /**
             * @notice Allows an owner to begin transferring ownership to a new address,
             * pending.
             */
            function transferOwnership(
              address to
            )
              public
              override
              onlyOwner()
            {
              _transferOwnership(to);
            }
            /**
             * @notice Allows an ownership transfer to be completed by the recipient.
             */
            function acceptOwnership()
              external
              override
            {
              require(msg.sender == s_pendingOwner, "Must be proposed owner");
              address oldOwner = s_owner;
              s_owner = msg.sender;
              s_pendingOwner = address(0);
              emit OwnershipTransferred(oldOwner, msg.sender);
            }
            /**
             * @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
            {
              require(to != msg.sender, "Cannot transfer to self");
              s_pendingOwner = to;
              emit OwnershipTransferRequested(s_owner, to);
            }
            /**
             * @notice validate access
             */
            function _validateOwnership()
              internal
              view
            {
              require(msg.sender == s_owner, "Only callable by owner");
            }
            /**
             * @notice Reverts if called by anyone other than the contract owner.
             */
            modifier onlyOwner() {
              _validateOwnership();
              _;
            }
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./interfaces/TypeAndVersionInterface.sol";
          abstract contract OCR2Abstract is TypeAndVersionInterface {
            // Maximum number of oracles the offchain reporting protocol is designed for
            uint256 constant internal maxNumOracles = 31;
            /**
             * @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
              )));
              uint256 prefixMask = type(uint256).max << (256-16); // 0xFFFF00..00
              uint256 prefix = 0x0001 << (256-16); // 0x000100..00
              return bytes32((prefix & prefixMask) | (h & ~prefixMask));
            }
            /**
            * @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 serialized report context containing configDigest, epoch, round, 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 maxNumOracles entries
             * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles 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.19;
          import "./interfaces/AccessControllerInterface.sol";
          import "./interfaces/AggregatorV2V3Interface.sol";
          import "./interfaces/AggregatorValidatorInterface.sol";
          import "./interfaces/LinkTokenInterface.sol";
          import "./interfaces/TypeAndVersionInterface.sol";
          import "./OCR2Abstract.sol";
          import "./OwnerIsCreator.sol";
          /**
           * @notice OCR2Aggregator for numerical data with billing support.
           * @dev
           * If you read or change this, be sure to read or adjust the comments. They
           * track the units of the values under consideration, and are crucial to
           * the readability of the operations it specifies.
           * @notice
           * Billing Trust Model:
           * Nothing in this contract prevents a billing admin from setting insane
           * values for the billing parameters in setBilling. Oracles
           * participating in this contract should regularly check that the
           * parameters make sense. Similarly, the outstanding obligations of this
           * contract to the oracles can exceed the funds held by the contract.
           * Oracles participating in this contract should regularly check that it
           * holds sufficient funds and stop interacting with it if funding runs
           * out.
           * This still leaves oracles with some risk due to TOCTOU issues.
           * However, since the sums involved are pretty small (Ethereum
           * transactions aren't that expensive in the end) and an oracle would
           * likely stop participating in a contract it repeatedly lost money on,
           * this risk is deemed acceptable. Oracles should also regularly
           * withdraw any funds in the contract to prevent issues where the
           * contract becomes underfunded at a later time, and different oracles
           * are competing for the left-over funds.
           * Finally, note that any change to the set of oracles or to the billing
           * parameters will trigger payout of all oracles first (using the old
           * parameters), a billing admin cannot take away funds that are already
           * marked for payment.
           */
          contract OCR2Aggregator is OCR2Abstract, OwnerIsCreator, AggregatorV2V3Interface {
            // This contract is divided into sections. Each section defines a set of
            // variables, events, and functions that belong together.
            /***************************************************************************
             * Section: Variables used in multiple other sections
             **************************************************************************/
            struct Transmitter {
              bool active;
              // Index of oracle in s_signersList/s_transmittersList
              uint8 index;
              // juels-denominated payment for transmitters, covering gas costs incurred
              // by the transmitter plus additional rewards. The entire LINK supply (1e9
              // LINK = 1e27 Juels) will always fit into a uint96.
              uint96 paymentJuels;
            }
            mapping (address /* transmitter address */ => Transmitter) internal s_transmitters;
            struct Signer {
              bool active;
              // Index of oracle in s_signersList/s_transmittersList
              uint8 index;
            }
            mapping (address /* signer address */ => Signer) internal s_signers;
            // s_signersList contains the signing address of each oracle
            address[] internal s_signersList;
            // 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;
            // 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[maxNumOracles] internal s_rewardFromAggregatorRoundId;
            bytes32 s_latestConfigDigest;
            // Storing these fields used on the hot path in a HotVars variable reduces the
            // retrieval of all of them to a single SLOAD.
            struct HotVars {
              // maximum number of faulty oracles
              uint8 f;
              // epoch and round from OCR protocol.
              // 32 most sig bits for epoch, 8 least sig bits for round
              uint40 latestEpochAndRound;
              // Chainlink Aggregators expose a roundId to consumers. The offchain reporting
              // 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 latestAggregatorRoundId;
              // Highest compensated gas price, in gwei uints
              uint32 maximumGasPriceGwei;
              // If gas price is less (in gwei units), transmitter gets half the savings
              uint32 reasonableGasPriceGwei;
              // Fixed LINK reward for each observer
              uint32 observationPaymentGjuels;
              // Fixed reward for transmitter
              uint32 transmissionPaymentGjuels;
              // Overhead incurred by accounting logic
              uint24 accountingGas;
            }
            HotVars internal s_hotVars;
            // 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 transmissionTimestamp; // when was report received onchain
            }
            mapping(uint32 /* aggregator round ID */ => Transmission) internal s_transmissions;
            // Lowest answer the system is allowed to report in response to transmissions
            int192 immutable public minAnswer;
            // Highest answer the system is allowed to report in response to transmissions
            int192 immutable public maxAnswer;
            /***************************************************************************
             * Section: Constructor
             **************************************************************************/
            /**
             * @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 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
             */
            constructor(
              LinkTokenInterface link,
              int192 minAnswer_,
              int192 maxAnswer_,
              AccessControllerInterface billingAccessController,
              AccessControllerInterface requesterAccessController,
              uint8 decimals_,
              string memory description_
            ) {
              s_linkToken = link;
              emit LinkTokenSet(LinkTokenInterface(address(0)), link);
              _setBillingAccessController(billingAccessController);
              decimals = decimals_;
              s_description = description_;
              setRequesterAccessController(requesterAccessController);
              setValidatorConfig(AggregatorValidatorInterface(address(0x0)), 0);
              minAnswer = minAnswer_;
              maxAnswer = maxAnswer_;
            }
            /***************************************************************************
             * Section: OCR2Abstract Configuration
             **************************************************************************/
            // incremented each time a new config is posted. This count is incorporated
            // into the config digest to prevent replay attacks.
            uint32 internal s_configCount;
            // makes it easier for offchain systems to extract config from logs
            uint32 internal s_latestConfigBlockNumber;
            // left as a function so this check can be disabled in derived contracts
            function _requirePositiveF (
              uint256 f
            )
              internal
              pure
              virtual
            {
              require(0 < f, "f must be positive");
            }
            struct SetConfigArgs {
              address[] signers;
              address[] transmitters;
              uint8 f;
              bytes onchainConfig;
              uint64 offchainConfigVersion;
              bytes offchainConfig;
            }
            /// @inheritdoc OCR2Abstract
            function setConfig(
              address[] memory signers,
              address[] memory transmitters,
              uint8 f,
              bytes memory onchainConfig,
              uint64 offchainConfigVersion,
              bytes memory offchainConfig
            )
              external
              override
              onlyOwner()
            {
              require(signers.length <= maxNumOracles, "too many oracles");
              require(signers.length == transmitters.length, "oracle length mismatch");
              require(3*f < signers.length, "faulty-oracle f too high");
              _requirePositiveF(f);
              require(keccak256(onchainConfig) == keccak256(abi.encodePacked(uint8(1) /*version*/, minAnswer, maxAnswer)), "invalid onchainConfig");
              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 (uint i = 0; i < args.signers.length; i++) {
                require(
                  !s_signers[args.signers[i]].active,
                  "repeated signer address"
                );
                s_signers[args.signers[i]] = Signer({
                  active: true,
                  index: uint8(i)
                });
                require(
                  !s_transmitters[args.transmitters[i]].active,
                  "repeated transmitter address"
                );
                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
              override
              view
              returns (
                uint32 configCount,
                uint32 blockNumber,
                bytes32 configDigest
              )
            {
              return (s_configCount, s_latestConfigBlockNumber, s_latestConfigDigest);
            }
            /**
             * @return list of addresses permitted to transmit reports to this contract
             * @dev The list will match the order used to specify the transmitter during setConfig
             */
            function getTransmitters()
              external
              view
              returns(address[] memory)
            {
              return s_transmittersList;
            }
            /***************************************************************************
             * Section: Onchain Validation
             **************************************************************************/
            // Configuration for validator
            struct ValidatorConfig {
              AggregatorValidatorInterface validator;
              uint32 gasLimit;
            }
            ValidatorConfig private s_validatorConfig;
            /**
             * @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
            );
            /**
             * @notice 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);
              }
            }
            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;
              require(
                _callWithExactGasEvenIfTargetIsNoContract(
                  vc.gasLimit,
                  address(vc.validator),
                  abi.encodeWithSignature(
                    "validate(uint256,int256,uint256,int256)",
                    uint256(prevAggregatorRoundId),
                    prevAggregatorRoundAnswer,
                    uint256(aggregatorRoundId),
                    answer
                  )
                ),
                "insufficient gas"
              );
            }
            uint256 private constant CALL_WITH_EXACT_GAS_CUSHION = 5_000;
            /**
             * @dev calls target address with exactly gasAmount gas and data as calldata
             * or reverts if at least gasAmount gas is not available.
             */
            function _callWithExactGasEvenIfTargetIsNoContract(
              uint256 gasAmount,
              address target,
              bytes memory data
            )
              private
              returns (bool sufficientGas)
            {
              // solhint-disable-next-line no-inline-assembly
              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, CALL_WITH_EXACT_GAS_CUSHION)) {
                  g := sub(g, CALL_WITH_EXACT_GAS_CUSHION)
                  // 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)), gasAmount) {
                    // Call and ignore success/return data. Note that we did not check
                    // whether a contract actually exists at the target address.
                    pop(call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0))
                    sufficientGas := true
                  }
                }
              }
            }
            /***************************************************************************
             * Section: RequestNewRound
             **************************************************************************/
            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);
            /**
             * @notice address of the requester access controller contract
             * @return requester access controller address
             */
            function getRequesterAccessController()
              external
              view
              returns (AccessControllerInterface)
            {
              return s_requesterAccessController;
            }
            /**
             * @notice sets the 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
             * @return the aggregatorRoundId 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) {
              require(msg.sender == owner() || s_requesterAccessController.hasAccess(msg.sender, msg.data),
                "Only owner&requester can call");
              uint40 latestEpochAndRound = s_hotVars.latestEpochAndRound;
              uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
              emit RoundRequested(
                msg.sender,
                s_latestConfigDigest,
                uint32(latestEpochAndRound >> 8),
                uint8(latestEpochAndRound)
              );
              return latestAggregatorRoundId + 1;
            }
            /***************************************************************************
             * Section: 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 big-endian 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
            );
            // Used to relieve stack pressure in transmit
            struct Report {
              uint32 observationsTimestamp;
              bytes observers; // ith element is the index of the ith observer
              int192[] observations; // ith element is the ith observation
              int192 juelsPerFeeCoin;
            }
            // _decodeReport decodes a serialized report into a Report struct
            function _decodeReport(bytes memory rawReport)
              internal
              pure
              returns (
                Report memory
              )
            {
              uint32 observationsTimestamp;
              bytes32 rawObservers;
              int192[] memory observations;
              int192 juelsPerFeeCoin;
              (observationsTimestamp, rawObservers, observations, 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
              });
            }
            // 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
            // 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.
            function _requireExpectedMsgDataLength(
              bytes calldata report,
              bytes32[] calldata rs,
              bytes32[] calldata ss
            )
              private
              pure
            {
              // calldata will never be big enough to make this overflow
              uint256 expected = TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT +
                report.length + // one byte per entry in report
                rs.length * 32 + // 32 bytes per entry in rs
                ss.length * 32 + // 32 bytes per entry in ss
                0; // placeholder
              require(msg.data.length == expected, "calldata length mismatch");
            }
            /// @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
            {
              // 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
              HotVars memory hotVars = s_hotVars;
              uint40 epochAndRound = uint40(uint256(reportContext[1]));
              require(hotVars.latestEpochAndRound < epochAndRound, "stale report");
              require(s_transmitters[msg.sender].active, "unauthorized transmitter");
              require(s_latestConfigDigest == reportContext[0], "configDigest mismatch");
              _requireExpectedMsgDataLength(report, rs, ss);
              require(rs.length == hotVars.f + 1, "wrong number of signatures");
              require(rs.length == ss.length, "signatures out of registration");
              // Verify signatures attached to report
              {
                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 (uint i = 0; i < rs.length; i++) {
                  address signerAddress = ecrecover(h, uint8(rawVs[i])+27, rs[i], ss[i]);
                  signer = s_signers[signerAddress];
                  require(signer.active, "signature error");
                  unchecked{
                    signedCount += 1 << (8 * signer.index);
                  }
                }
                // The first byte of the mask can be 0, because we only ever have 31 oracles
                require(signedCount & 0x0001010101010101010101010101010101010101010101010101010101010101 == signedCount, "duplicate signer");
              }
              int192 juelsPerFeeCoin = _report(hotVars, reportContext[0], epochAndRound, report);
              _payTransmitter(hotVars, juelsPerFeeCoin, uint32(initialGas), msg.sender);
            }
            /**
             * @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_
              )
            {
              require(msg.sender == tx.origin, "Only callable by EOA");
              return (
                s_latestConfigDigest,
                uint32(s_hotVars.latestEpochAndRound >> 8),
                uint8(s_hotVars.latestEpochAndRound),
                s_transmissions[s_hotVars.latestAggregatorRoundId].answer,
                s_transmissions[s_hotVars.latestAggregatorRoundId].transmissionTimestamp
              );
            }
            /// @inheritdoc OCR2Abstract
            function latestConfigDigestAndEpoch()
              external
              override
              view
              virtual
              returns(
                bool scanLogs,
                bytes32 configDigest,
                uint32 epoch
              )
            {
              return (false, s_latestConfigDigest, uint32(s_hotVars.latestEpochAndRound >> 8));
            }
            function _requireExpectedReportLength(
              bytes memory report,
              int192[] memory observations
            )
              private
              pure
            {
              uint256 expected =
                32 + // observationsTimestamp
                32 + // rawObservers
                32 + // observations offset
                32 + // juelsPerFeeCoin
                32 + // observations length
                32 * observations.length + // observations payload
                0;
              require(report.length == expected, "report length mismatch");
            }
            function _report(
              HotVars memory hotVars,
              bytes32 configDigest,
              uint40 epochAndRound,
              bytes memory rawReport
            )
              internal
              returns (int192 juelsPerFeeCoin)
            {
              Report memory report = _decodeReport(rawReport);
              require(report.observations.length <= maxNumOracles, "num observations out of bounds");
              // 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.
              require(hotVars.f < report.observations.length, "too few values to trust median");
              hotVars.latestEpochAndRound = epochAndRound;
              // get median, validate its range, store it in new aggregator round
              int192 median = report.observations[report.observations.length/2];
              require(minAnswer <= median && median <= maxAnswer, "median is out of min-max range");
              hotVars.latestAggregatorRoundId++;
              s_transmissions[hotVars.latestAggregatorRoundId] =
                Transmission({
                  answer: median,
                  observationsTimestamp: report.observationsTimestamp,
                  transmissionTimestamp: uint32(block.timestamp)
                });
              // 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);
              return report.juelsPerFeeCoin;
            }
            /***************************************************************************
             * Section: v2 AggregatorInterface
             **************************************************************************/
            /**
             * @notice median from the most recent report
             */
            function latestAnswer()
              public
              override
              view
              virtual
              returns (int256)
            {
              return s_transmissions[s_hotVars.latestAggregatorRoundId].answer;
            }
            /**
             * @notice timestamp of block in which last report was transmitted
             */
            function latestTimestamp()
              public
              override
              view
              virtual
              returns (uint256)
            {
              return s_transmissions[s_hotVars.latestAggregatorRoundId].transmissionTimestamp;
            }
            /**
             * @notice Aggregator round (NOT OCR round) in which last report was transmitted
             */
            function latestRound()
              public
              override
              view
              virtual
              returns (uint256)
            {
              return s_hotVars.latestAggregatorRoundId;
            }
            /**
             * @notice median of report from given aggregator round (NOT OCR round)
             * @param roundId the aggregator round of the target report
             */
            function getAnswer(uint256 roundId)
              public
              override
              view
              virtual
              returns (int256)
            {
              if (roundId > 0xFFFFFFFF) { 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
             */
            function getTimestamp(uint256 roundId)
              public
              override
              view
              virtual
              returns (uint256)
            {
              if (roundId > 0xFFFFFFFF) { return 0; }
              return s_transmissions[uint32(roundId)].transmissionTimestamp;
            }
            /***************************************************************************
             * Section: v3 AggregatorInterface
             **************************************************************************/
            /**
             * @return answers are stored in fixed-point format, with this many digits of precision
             */
            uint8 immutable public override decimals;
            /**
             * @notice aggregator contract version
             */
            uint256 constant public override version = 6;
            string internal s_description;
            /**
             * @notice human-readable description of observable this contract is reporting on
             */
            function description()
              public
              override
              view
              virtual
              returns (string memory)
            {
              return s_description;
            }
            /**
             * @notice details for the given aggregator round
             * @param roundId target aggregator round (NOT OCR 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
              override
              view
              virtual
              returns (
                uint80 roundId_,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              if(roundId > type(uint32).max) { return (0, 0, 0, 0, 0); }
              Transmission memory transmission = s_transmissions[uint32(roundId)];
              return (
                roundId,
                transmission.answer,
                transmission.observationsTimestamp,
                transmission.transmissionTimestamp,
                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
              override
              view
              virtual
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              )
            {
              uint32 latestAggregatorRoundId = s_hotVars.latestAggregatorRoundId;
              Transmission memory transmission = s_transmissions[latestAggregatorRoundId];
              return (
                latestAggregatorRoundId,
                transmission.answer,
                transmission.observationsTimestamp,
                transmission.transmissionTimestamp,
                latestAggregatorRoundId
              );
            }
            /***************************************************************************
             * Section: Configurable LINK Token
             **************************************************************************/
            // We assume that the token contract is correct. This contract is not written
            // to handle misbehaving ERC20 tokens!
            LinkTokenInterface internal s_linkToken;
            /*
             * @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
            );
            /**
             * @notice sets the LINK token contract used for paying oracles
             * @param linkToken the address of the LINK token contract
             * @param recipient remaining funds from the previous token contract are transferred
             * here
             * @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!
             */
            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));
              require(oldLinkToken.transfer(recipient, remainingBalance), "transfer remaining funds failed");
              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;
            }
            /***************************************************************************
             * Section: BillingAccessController Management
             **************************************************************************/
            // 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 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);
            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
             * @dev only owner can call this
             */
            function setBillingAccessController(AccessControllerInterface _billingAccessController)
              external
              onlyOwner
            {
              _setBillingAccessController(_billingAccessController);
            }
            /**
             * @notice gets billingAccessController
             * @return address of billingAccessController contract
             */
            function getBillingAccessController()
              external
              view
              returns (AccessControllerInterface)
            {
              return s_billingAccessController;
            }
            /***************************************************************************
             * Section: 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
            );
            /**
             * @notice sets 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 incurred by accounting logic
             * @dev access control provided by billingAccessController
             */
            function setBilling(
              uint32 maximumGasPriceGwei,
              uint32 reasonableGasPriceGwei,
              uint32 observationPaymentGjuels,
              uint32 transmissionPaymentGjuels,
              uint24 accountingGas
            )
              external
            {
              AccessControllerInterface access = s_billingAccessController;
              require(msg.sender == owner() || access.hasAccess(msg.sender, msg.data),
                "Only owner&billingAdmin can call");
              _payOracles();
              s_hotVars.maximumGasPriceGwei = maximumGasPriceGwei;
              s_hotVars.reasonableGasPriceGwei = reasonableGasPriceGwei;
              s_hotVars.observationPaymentGjuels = observationPaymentGjuels;
              s_hotVars.transmissionPaymentGjuels = transmissionPaymentGjuels;
              s_hotVars.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_hotVars.accountingGas
              );
            }
            /***************************************************************************
             * Section: Payments and Withdrawals
             **************************************************************************/
            /**
             * @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
            {
              require(msg.sender == s_payees[transmitter], "Only payee can withdraw");
              _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 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
            );
            // _payOracle pays out transmitter's balance to the corresponding payee, and zeros it out
            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.
                require(s_linkToken.transfer(payee, juelsAmount), "insufficient funds");
                s_rewardFromAggregatorRoundId[transmitter.index] = s_hotVars.latestAggregatorRoundId;
                s_transmitters[transmitterAddress].paymentJuels = 0;
                emit OraclePaid(transmitterAddress, payee, juelsAmount, s_linkToken);
              }
            }
            // _payOracles pays out all transmitters, 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[maxNumOracles] memory rewardFromAggregatorRoundId = s_rewardFromAggregatorRoundId;
                address[] memory transmitters = s_transmittersList;
                for (uint 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.
                      require(linkToken.transfer(payee, juelsAmount), "insufficient funds");
                      rewardFromAggregatorRoundId[transmitteridx] = latestAggregatorRoundId;
                      emit OraclePaid(transmitters[transmitteridx], payee, juelsAmount, linkToken);
                    }
                }
                // "Zero" the accounting storage variables
                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
             * @param recipient address to send funds to
             * @param amount maximum amount to withdraw, denominated in LINK-wei.
             * @dev access control provided by billingAccessController
             */
            function withdrawFunds(
              address recipient,
              uint256 amount
            )
              external
            {
              require(msg.sender == owner() || s_billingAccessController.hasAccess(msg.sender, msg.data),
                "Only owner&billingAdmin can call");
              uint256 linkDue = _totalLinkDue();
              uint256 linkBalance = s_linkToken.balanceOf(address(this));
              require(linkBalance >= linkDue, "insufficient balance");
              require(s_linkToken.transfer(recipient, _min(linkBalance - linkDue, amount)), "insufficient funds");
            }
            // Total LINK due to participants in past reports (denominated in Juels).
            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[maxNumOracles] memory rewardFromAggregatorRoundId = s_rewardFromAggregatorRoundId;
              for (uint i = 0; i < n; i++) {
                linkDue += latestAggregatorRoundId - rewardFromAggregatorRoundId[i];
              }
              // Convert observationPaymentGjuels to uint256, or this overflows!
              linkDue *= uint256(s_hotVars.observationPaymentGjuels) * (1 gwei);
              for (uint i = 0; i < n; i++) {
                linkDue += uint256(s_transmitters[transmitters[i]].paymentJuels);
              }
            }
            /**
             * @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
             */
            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];
            }
            /***************************************************************************
             * Section: Transmitter Payment
             **************************************************************************/
            // Gas price at which the transmitter should be reimbursed, in gwei/gas
            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);
              }
            }
            // gas reimbursement due the transmitter, 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 {
                require(initialGas >= leftGas, "leftGas cannot exceed initialGas");
                uint256 usedGas =
                  initialGas - leftGas + // observed gas usage
                  callDataGas + accountingGas; // estimated gas usage
                uint256 fullGasCostWei = usedGas * gasPriceGwei * (1 gwei);
                return fullGasCostWei;
              }
            }
            function _payTransmitter(
              HotVars memory hotVars,
              int192 juelsPerFeeCoin,
              uint32 initialGas,
              address transmitter
            )
              internal
              virtual
            {
              // 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 {
                // 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,
                  hotVars.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[transmitter].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[transmitter].paymentJuels = newTransmitterPaymentJuels;
              }
            }
            /***************************************************************************
             * Section: Payee Management
             **************************************************************************/
            // Addresses at which oracles want to receive payments, by transmitter address
            mapping (address /* transmitter */ => address /* payment address */)
              internal
              s_payees;
            // Payee addresses which must be approved by the owner
            mapping (address /* transmitter */ => address /* payment address */)
              internal
              s_proposedPayees;
            /**
             * @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 current the payee address for the oracle, prior to this setting
             */
            event PayeeshipTransferred(
              address indexed transmitter,
              address indexed previous,
              address indexed current
            );
            /**
             * @notice sets the payees for transmitting addresses
             * @param transmitters addresses oracles use to transmit the reports
             * @param payees addresses of payees corresponding to list of transmitters
             * @dev must be called by owner
             * @dev cannot be used to change payee addresses, only to initially populate them
             */
            function setPayees(
              address[] calldata transmitters,
              address[] calldata payees
            )
              external
              onlyOwner()
            {
              require(transmitters.length == payees.length, "transmitters.size != payees.size");
              for (uint i = 0; i < transmitters.length; i++) {
                address transmitter = transmitters[i];
                address payee = payees[i];
                address currentPayee = s_payees[transmitter];
                bool zeroedOut = currentPayee == address(0);
                require(zeroedOut || currentPayee == payee, "payee already set");
                s_payees[transmitter] = payee;
                if (currentPayee != payee) {
                  emit PayeeshipTransferred(transmitter, currentPayee, payee);
                }
              }
            }
            /**
             * @notice first step of payeeship transfer (safe transfer pattern)
             * @param transmitter transmitter address of oracle whose payee is changing
             * @param proposed new payee address
             * @dev can only be called by payee address
             */
            function transferPayeeship(
              address transmitter,
              address proposed
            )
              external
            {
              require(msg.sender == s_payees[transmitter], "only current payee can update");
              require(msg.sender != proposed, "cannot transfer to self");
              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)
             * @param transmitter transmitter address of oracle whose payee is changing
             * @dev can only be called by proposed new payee address
             */
            function acceptPayeeship(
              address transmitter
            )
              external
            {
              require(msg.sender == s_proposedPayees[transmitter], "only proposed payees can accept");
              address currentPayee = s_payees[transmitter];
              s_payees[transmitter] = msg.sender;
              s_proposedPayees[transmitter] = address(0);
              emit PayeeshipTransferred(transmitter, currentPayee, msg.sender);
            }
            /***************************************************************************
             * Section: TypeAndVersionInterface
             **************************************************************************/
            function typeAndVersion()
              external
              override
              pure
              virtual
              returns (string memory)
            {
              return "OCR2Aggregator 1.0.0";
            }
            /***************************************************************************
             * Section: Helper Functions
             **************************************************************************/
            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.19;
          import "./interfaces/TypeAndVersionInterface.sol";
          import "./lib/ConfigDigestUtilEVMSimple.sol";
          import "./OwnerIsCreator.sol";
          import "./OCR2Abstract.sol";
          /// @title OCRConfigurationStoreEVMSimple
          /// @notice This contract stores configurations for protocol versions OCR2 and
          /// above in contract storage. It uses the "EVMSimple" config digester.
          contract OCRConfigurationStoreEVMSimple is TypeAndVersionInterface {
              struct ConfigurationEVMSimple {
                  address[] signers;
                  address[] transmitters;
                  bytes onchainConfig;
                  bytes offchainConfig;
                  address contractAddress;
                  uint64 offchainConfigVersion;
                  uint32 configCount;
                  uint8 f;
              }
              /// @notice a list of configurations keyed by their digest
              mapping(bytes32 => ConfigurationEVMSimple) internal s_configurations;
              /// @notice emitted when a new configuration is added
              event NewConfiguration(bytes32 indexed configDigest);
              /// @notice adds a new configuration to the store
              function addConfig(ConfigurationEVMSimple calldata configuration) external returns (bytes32) {
                  bytes32 configDigest = ConfigDigestUtilEVMSimple.configDigestFromConfigData(
                      block.chainid,
                      configuration.contractAddress,
                      configuration.configCount,
                      configuration.signers,
                      configuration.transmitters,
                      configuration.f,
                      configuration.onchainConfig,
                      configuration.offchainConfigVersion,
                      configuration.offchainConfig
                  );
                  s_configurations[configDigest] = configuration;
                  emit NewConfiguration(configDigest);
                  return configDigest;
              }
              /// @notice reads a configuration from the store
              function readConfig(bytes32 configDigest) external view returns (ConfigurationEVMSimple memory) {
                  return s_configurations[configDigest];
              }
              /// @inheritdoc TypeAndVersionInterface
              function typeAndVersion() external override pure virtual returns (string memory)
              {
                  return "OCRConfigurationStoreEVMSimple 1.0.0";
              }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./ConfirmedOwner.sol";
          /**
           * @title The OwnerIsCreator contract
           * @notice A contract with helpers for basic contract ownership.
           */
          contract OwnerIsCreator is ConfirmedOwner {
            constructor(
            )
              ConfirmedOwner(
                msg.sender
              )
            {
            }
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./SimpleWriteAccessController.sol";
          /**
           * @title SimpleReadAccessController
           * @notice Gives access to:
           * - any externally owned account (note that offchain actors can always read
           * any contract storage regardless of onchain 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)
            {
              return super.hasAccess(_user, _calldata) || _user == tx.origin;
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./OwnerIsCreator.sol";
          import "./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, OwnerIsCreator {
            bool public checkEnabled;
            mapping(address => bool) internal accessList;
            event AddedAccess(address user);
            event RemovedAccess(address user);
            event CheckAccessEnabled();
            event CheckAccessDisabled();
            constructor()
            // TODO
            // this is modified from the version in the Chainlink monorepo
            //  OwnerIsCreator()
            {
              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 accessList[_user] || !checkEnabled;
            }
            /**
             * @notice Adds an address to the access list
             * @param _user The address to add
             */
            function addAccess(address _user)
              external
              onlyOwner()
            {
              if (!accessList[_user]) {
                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 (accessList[_user]) {
                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() {
              require(hasAccess(msg.sender, msg.data), "No access");
              _;
            }
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface AccessControllerInterface {
            function hasAccess(address user, bytes calldata data) external view returns (bool);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface AggregatorInterface {
            function latestAnswer() external view returns (int256);
            function latestTimestamp() external view returns (uint256);
            function latestRound() external view returns (uint256);
            function getAnswer(uint256 roundId) external view returns (int256);
            function getTimestamp(uint256 roundId) external view returns (uint256);
            event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
            event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          import "./AggregatorInterface.sol";
          import "./AggregatorV3Interface.sol";
          interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
          {
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface AggregatorV3Interface {
            function decimals() external view returns (uint8);
            function description() external view returns (string memory);
            function version() external view returns (uint256);
            function getRoundData(uint80 _roundId)
              external
              view
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              );
            function latestRoundData()
              external
              view
              returns (
                uint80 roundId,
                int256 answer,
                uint256 startedAt,
                uint256 updatedAt,
                uint80 answeredInRound
              );
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface AggregatorValidatorInterface {
            function validate(
              uint256 previousRoundId,
              int256 previousAnswer,
              uint256 currentRoundId,
              int256 currentAnswer
            ) external returns (bool);
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface 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;
          interface OwnableInterface {
            function owner()
              external
              returns (
                address
              );
            function transferOwnership(
              address recipient
            )
              external;
            function acceptOwnership()
              external;
          }
          // SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          interface TypeAndVersionInterface{
            function typeAndVersion()
              external
              pure
              returns (string memory);
          }// SPDX-License-Identifier: MIT
          pragma solidity ^0.8.0;
          /// @title ConfigDigestUtilEVMSimple
          /// @notice ConfigDigest related utility functions for "EVMSimple" config
          /// digester
          library ConfigDigestUtilEVMSimple {
              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 hash = uint256(
                      keccak256(
                          abi.encode(
                              chainId,
                              contractAddress,
                              configCount,
                              signers,
                              transmitters,
                              f,
                              onchainConfig,
                              offchainConfigVersion,
                              offchainConfig
                  )));
                  uint256 prefixMask = type(uint256).max << (256-16); // 0xFFFF00..00
                  uint256 prefix = 0x0001 << (256-16); // 0x000100..00
                  return bytes32((prefix & prefixMask) | (hash & ~prefixMask));
              }
          }