Transaction Hash:
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 | ||
---|---|---|---|---|---|
0x164F005B...A8895323C | 0.895155107744758412 Eth | 0.897591501352925953 Eth | 0.002436393608167541 | ||
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 20.765876202908893435 Eth | 20.765963729306893435 Eth | 0.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( )
-
relayToCyber[CyberRelayGate (ln:44)]
processRelay[CyberRelayGate (ln:59)]
Relay[CyberRelayGate (ln:66)]
File 1 of 5: ERC1967Proxy
File 2 of 5: CyberIDPermissionedRelayHook
File 3 of 5: CyberRelayGate
File 4 of 5: EACAggregatorProxy
File 5 of 5: AccessControlledOCR2Aggregator
// 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)); } }