diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsClient.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsClient.sol deleted file mode 100644 index 4aabef01f28..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsClient.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import {IFunctionsRouter} from "./interfaces/IFunctionsRouter.sol"; -import {IFunctionsClient} from "./interfaces/IFunctionsClient.sol"; - -import {FunctionsRequest} from "./libraries/FunctionsRequest.sol"; - -/// @title The Chainlink Functions client contract -/// @notice Contract developers can inherit this contract in order to make Chainlink Functions requests -abstract contract FunctionsClient is IFunctionsClient { - using FunctionsRequest for FunctionsRequest.Request; - - IFunctionsRouter internal immutable i_functionsRouter; - - event RequestSent(bytes32 indexed id); - event RequestFulfilled(bytes32 indexed id); - - error OnlyRouterCanFulfill(); - - constructor(address router) { - i_functionsRouter = IFunctionsRouter(router); - } - - /// @notice Sends a Chainlink Functions request - /// @param data The CBOR encoded bytes data for a Functions request - /// @param subscriptionId The subscription ID that will be charged to service the request - /// @param callbackGasLimit the amount of gas that will be available for the fulfillment callback - /// @return requestId The generated request ID for this request - function _sendRequest( - bytes memory data, - uint64 subscriptionId, - uint32 callbackGasLimit, - bytes32 donId - ) internal returns (bytes32) { - bytes32 requestId = i_functionsRouter.sendRequest( - subscriptionId, - data, - FunctionsRequest.REQUEST_DATA_VERSION, - callbackGasLimit, - donId - ); - emit RequestSent(requestId); - return requestId; - } - - /// @notice User defined function to handle a response from the DON - /// @param requestId The request ID, returned by sendRequest() - /// @param response Aggregated response from the execution of the user's source code - /// @param err Aggregated error from the execution of the user code or from the execution pipeline - /// @dev Either response or error parameter will be set, but never both - function _fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal virtual; - - /// @inheritdoc IFunctionsClient - function handleOracleFulfillment(bytes32 requestId, bytes memory response, bytes memory err) external override { - if (msg.sender != address(i_functionsRouter)) { - revert OnlyRouterCanFulfill(); - } - _fulfillRequest(requestId, response, err); - emit RequestFulfilled(requestId); - } -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsCoordinator.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsCoordinator.sol index 9c7f3598711..39b298d4857 100644 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsCoordinator.sol +++ b/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsCoordinator.sol @@ -5,7 +5,7 @@ import {IFunctionsCoordinator} from "../v1_0_0/interfaces/IFunctionsCoordinator. import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; import {FunctionsBilling, FunctionsBillingConfig} from "./FunctionsBilling.sol"; -import {OCR2Base} from "./ocr/OCR2Base.sol"; +import {OCR2Base} from "../v1_0_0/ocr/OCR2Base.sol"; import {FunctionsResponse} from "../v1_0_0/libraries/FunctionsResponse.sol"; /// @title Functions Coordinator contract diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsRouter.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsRouter.sol index cc925ecf4c9..862817476be 100644 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsRouter.sol +++ b/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsRouter.sol @@ -2,13 +2,13 @@ pragma solidity ^0.8.19; import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; -import {IFunctionsRouter} from "./interfaces/IFunctionsRouter.sol"; -import {IFunctionsCoordinator} from "./interfaces/IFunctionsCoordinator.sol"; +import {IFunctionsRouter} from "../v1_0_0/interfaces/IFunctionsRouter.sol"; +import {IFunctionsCoordinator} from "../v1_0_0/interfaces/IFunctionsCoordinator.sol"; import {IAccessController} from "../../shared/interfaces/IAccessController.sol"; import {GAS_BOUND_CALLER, IGasBoundCaller} from "./interfaces/zksync/IGasBoundCaller.sol"; -import {FunctionsSubscriptions} from "./FunctionsSubscriptions.sol"; -import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; +import {FunctionsSubscriptions} from "../v1_0_0/FunctionsSubscriptions.sol"; +import {FunctionsResponse} from "../v1_0_0/libraries/FunctionsResponse.sol"; import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsSubscriptions.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsSubscriptions.sol deleted file mode 100644 index b93d2174734..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/FunctionsSubscriptions.sol +++ /dev/null @@ -1,552 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import {IFunctionsSubscriptions} from "./interfaces/IFunctionsSubscriptions.sol"; -import {IERC677Receiver} from "../../shared/interfaces/IERC677Receiver.sol"; -import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol"; - -import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; - -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; - -/// @title Functions Subscriptions contract -/// @notice Contract that coordinates payment from users to the nodes of the Decentralized Oracle Network (DON). -abstract contract FunctionsSubscriptions is IFunctionsSubscriptions, IERC677Receiver { - using SafeERC20 for IERC20; - using FunctionsResponse for FunctionsResponse.Commitment; - - // ================================================================ - // | Balance state | - // ================================================================ - // link token address - IERC20 internal immutable i_linkToken; - - // s_totalLinkBalance tracks the total LINK sent to/from - // this contract through onTokenTransfer, cancelSubscription and oracleWithdraw. - // A discrepancy with this contract's LINK balance indicates that someone - // sent tokens using transfer and so we may need to use recoverFunds. - uint96 private s_totalLinkBalance; - - /// @dev NOP balances are held as a single amount. The breakdown is held by the Coordinator. - mapping(address coordinator => uint96 balanceJuelsLink) private s_withdrawableTokens; - - // ================================================================ - // | Subscription state | - // ================================================================ - // Keep a count of the number of subscriptions so that its possible to - // loop through all the current subscriptions via .getSubscription(). - uint64 private s_currentSubscriptionId; - - mapping(uint64 subscriptionId => Subscription) private s_subscriptions; - - // Maintains the list of keys in s_consumers. - // We do this for 2 reasons: - // 1. To be able to clean up all keys from s_consumers when canceling a subscription. - // 2. To be able to return the list of all consumers in getSubscription. - // Note that we need the s_consumers map to be able to directly check if a - // consumer is valid without reading all the consumers from storage. - mapping(address consumer => mapping(uint64 subscriptionId => Consumer)) private s_consumers; - - event SubscriptionCreated(uint64 indexed subscriptionId, address owner); - event SubscriptionFunded(uint64 indexed subscriptionId, uint256 oldBalance, uint256 newBalance); - event SubscriptionConsumerAdded(uint64 indexed subscriptionId, address consumer); - event SubscriptionConsumerRemoved(uint64 indexed subscriptionId, address consumer); - event SubscriptionCanceled(uint64 indexed subscriptionId, address fundsRecipient, uint256 fundsAmount); - event SubscriptionOwnerTransferRequested(uint64 indexed subscriptionId, address from, address to); - event SubscriptionOwnerTransferred(uint64 indexed subscriptionId, address from, address to); - - error TooManyConsumers(uint16 maximumConsumers); - error InsufficientBalance(uint96 currentBalanceJuels); - error InvalidConsumer(); - error CannotRemoveWithPendingRequests(); - error InvalidSubscription(); - error OnlyCallableFromLink(); - error InvalidCalldata(); - error MustBeSubscriptionOwner(); - error TimeoutNotExceeded(); - error MustBeProposedOwner(address proposedOwner); - event FundsRecovered(address to, uint256 amount); - - // ================================================================ - // | Request state | - // ================================================================ - - mapping(bytes32 requestId => bytes32 commitmentHash) internal s_requestCommitments; - - struct Receipt { - uint96 callbackGasCostJuels; - uint96 totalCostJuels; - } - - event RequestTimedOut(bytes32 indexed requestId); - - // ================================================================ - // | Initialization | - // ================================================================ - constructor(address link) { - i_linkToken = IERC20(link); - } - - // ================================================================ - // | Request/Response | - // ================================================================ - - /// @notice Sets a request as in-flight - /// @dev Only callable within the Router - function _markRequestInFlight(address client, uint64 subscriptionId, uint96 estimatedTotalCostJuels) internal { - // Earmark subscription funds - s_subscriptions[subscriptionId].blockedBalance += estimatedTotalCostJuels; - - // Increment sent requests - s_consumers[client][subscriptionId].initiatedRequests += 1; - } - - /// @notice Moves funds from one subscription account to another. - /// @dev Only callable by the Coordinator contract that is saved in the request commitment - function _pay( - uint64 subscriptionId, - uint96 estimatedTotalCostJuels, - address client, - uint96 adminFee, - uint96 juelsPerGas, - uint96 gasUsed, - uint96 costWithoutCallbackJuels - ) internal returns (Receipt memory) { - uint96 callbackGasCostJuels = juelsPerGas * gasUsed; - uint96 totalCostJuels = costWithoutCallbackJuels + adminFee + callbackGasCostJuels; - - if ( - s_subscriptions[subscriptionId].balance < totalCostJuels || - s_subscriptions[subscriptionId].blockedBalance < estimatedTotalCostJuels - ) { - revert InsufficientBalance(s_subscriptions[subscriptionId].balance); - } - - // Charge the subscription - s_subscriptions[subscriptionId].balance -= totalCostJuels; - - // Unblock earmarked funds - s_subscriptions[subscriptionId].blockedBalance -= estimatedTotalCostJuels; - - // Pay the DON's fees and gas reimbursement - s_withdrawableTokens[msg.sender] += costWithoutCallbackJuels + callbackGasCostJuels; - - // Pay out the administration fee - s_withdrawableTokens[address(this)] += adminFee; - - // Increment finished requests - s_consumers[client][subscriptionId].completedRequests += 1; - - return Receipt({callbackGasCostJuels: callbackGasCostJuels, totalCostJuels: totalCostJuels}); - } - - // ================================================================ - // | Owner methods | - // ================================================================ - - /// @inheritdoc IFunctionsSubscriptions - function ownerCancelSubscription(uint64 subscriptionId) external override { - _onlyRouterOwner(); - _isExistingSubscription(subscriptionId); - _cancelSubscriptionHelper(subscriptionId, s_subscriptions[subscriptionId].owner, false); - } - - /// @inheritdoc IFunctionsSubscriptions - function recoverFunds(address to) external override { - _onlyRouterOwner(); - uint256 externalBalance = i_linkToken.balanceOf(address(this)); - uint256 internalBalance = uint256(s_totalLinkBalance); - if (internalBalance < externalBalance) { - uint256 amount = externalBalance - internalBalance; - i_linkToken.safeTransfer(to, amount); - emit FundsRecovered(to, amount); - } - // If the balances are equal, nothing to be done. - } - - // ================================================================ - // | Fund withdrawal | - // ================================================================ - - /// @inheritdoc IFunctionsSubscriptions - function oracleWithdraw(address recipient, uint96 amount) external override { - _whenNotPaused(); - - if (amount == 0) { - revert InvalidCalldata(); - } - uint96 currentBalance = s_withdrawableTokens[msg.sender]; - if (currentBalance < amount) { - revert InsufficientBalance(currentBalance); - } - s_withdrawableTokens[msg.sender] -= amount; - s_totalLinkBalance -= amount; - i_linkToken.safeTransfer(recipient, amount); - } - - /// @notice Owner withdraw LINK earned through admin fees - /// @notice If amount is 0 the full balance will be withdrawn - /// @param recipient where to send the funds - /// @param amount amount to withdraw - function ownerWithdraw(address recipient, uint96 amount) external { - _onlyRouterOwner(); - if (amount == 0) { - amount = s_withdrawableTokens[address(this)]; - } - uint96 currentBalance = s_withdrawableTokens[address(this)]; - if (currentBalance < amount) { - revert InsufficientBalance(currentBalance); - } - s_withdrawableTokens[address(this)] -= amount; - s_totalLinkBalance -= amount; - - i_linkToken.safeTransfer(recipient, amount); - } - - // ================================================================ - // | TransferAndCall Deposit helper | - // ================================================================ - - // This function is to be invoked when using LINK.transferAndCall - /// @dev Note to fund the subscription, use transferAndCall. For example - /// @dev LINKTOKEN.transferAndCall( - /// @dev address(ROUTER), - /// @dev amount, - /// @dev abi.encode(subscriptionId)); - function onTokenTransfer(address /* sender */, uint256 amount, bytes calldata data) external override { - _whenNotPaused(); - if (msg.sender != address(i_linkToken)) { - revert OnlyCallableFromLink(); - } - if (data.length != 32) { - revert InvalidCalldata(); - } - uint64 subscriptionId = abi.decode(data, (uint64)); - if (s_subscriptions[subscriptionId].owner == address(0)) { - revert InvalidSubscription(); - } - // We do not check that the msg.sender is the subscription owner, - // anyone can fund a subscription. - uint256 oldBalance = s_subscriptions[subscriptionId].balance; - s_subscriptions[subscriptionId].balance += uint96(amount); - s_totalLinkBalance += uint96(amount); - emit SubscriptionFunded(subscriptionId, oldBalance, oldBalance + amount); - } - - // ================================================================ - // | Subscription management | - // ================================================================ - - /// @inheritdoc IFunctionsSubscriptions - function getTotalBalance() external view override returns (uint96) { - return s_totalLinkBalance; - } - - /// @inheritdoc IFunctionsSubscriptions - function getSubscriptionCount() external view override returns (uint64) { - return s_currentSubscriptionId; - } - - /// @inheritdoc IFunctionsSubscriptions - function getSubscription(uint64 subscriptionId) public view override returns (Subscription memory) { - _isExistingSubscription(subscriptionId); - return s_subscriptions[subscriptionId]; - } - - /// @inheritdoc IFunctionsSubscriptions - function getSubscriptionsInRange( - uint64 subscriptionIdStart, - uint64 subscriptionIdEnd - ) external view override returns (Subscription[] memory subscriptions) { - if ( - subscriptionIdStart > subscriptionIdEnd || - subscriptionIdEnd > s_currentSubscriptionId || - s_currentSubscriptionId == 0 - ) { - revert InvalidCalldata(); - } - - subscriptions = new Subscription[]((subscriptionIdEnd - subscriptionIdStart) + 1); - for (uint256 i = 0; i <= subscriptionIdEnd - subscriptionIdStart; ++i) { - subscriptions[i] = s_subscriptions[uint64(subscriptionIdStart + i)]; - } - - return subscriptions; - } - - /// @inheritdoc IFunctionsSubscriptions - function getConsumer(address client, uint64 subscriptionId) public view override returns (Consumer memory) { - return s_consumers[client][subscriptionId]; - } - - /// @dev Used within this file & FunctionsRouter.sol - function _isExistingSubscription(uint64 subscriptionId) internal view { - if (s_subscriptions[subscriptionId].owner == address(0)) { - revert InvalidSubscription(); - } - } - - /// @dev Used within FunctionsRouter.sol - function _isAllowedConsumer(address client, uint64 subscriptionId) internal view { - if (!s_consumers[client][subscriptionId].allowed) { - revert InvalidConsumer(); - } - } - - /// @inheritdoc IFunctionsSubscriptions - function createSubscription() external override returns (uint64 subscriptionId) { - _whenNotPaused(); - _onlySenderThatAcceptedToS(); - - subscriptionId = ++s_currentSubscriptionId; - s_subscriptions[subscriptionId] = Subscription({ - balance: 0, - blockedBalance: 0, - owner: msg.sender, - proposedOwner: address(0), - consumers: new address[](0), - flags: bytes32(0) - }); - - emit SubscriptionCreated(subscriptionId, msg.sender); - - return subscriptionId; - } - - /// @inheritdoc IFunctionsSubscriptions - function createSubscriptionWithConsumer(address consumer) external override returns (uint64 subscriptionId) { - _whenNotPaused(); - _onlySenderThatAcceptedToS(); - - subscriptionId = ++s_currentSubscriptionId; - s_subscriptions[subscriptionId] = Subscription({ - balance: 0, - blockedBalance: 0, - owner: msg.sender, - proposedOwner: address(0), - consumers: new address[](0), - flags: bytes32(0) - }); - - s_subscriptions[subscriptionId].consumers.push(consumer); - s_consumers[consumer][subscriptionId].allowed = true; - - emit SubscriptionCreated(subscriptionId, msg.sender); - emit SubscriptionConsumerAdded(subscriptionId, consumer); - - return subscriptionId; - } - - /// @inheritdoc IFunctionsSubscriptions - function proposeSubscriptionOwnerTransfer(uint64 subscriptionId, address newOwner) external override { - _whenNotPaused(); - _onlySubscriptionOwner(subscriptionId); - _onlySenderThatAcceptedToS(); - - if (newOwner == address(0) || s_subscriptions[subscriptionId].proposedOwner == newOwner) { - revert InvalidCalldata(); - } - - s_subscriptions[subscriptionId].proposedOwner = newOwner; - emit SubscriptionOwnerTransferRequested(subscriptionId, msg.sender, newOwner); - } - - /// @inheritdoc IFunctionsSubscriptions - function acceptSubscriptionOwnerTransfer(uint64 subscriptionId) external override { - _whenNotPaused(); - _onlySenderThatAcceptedToS(); - - address previousOwner = s_subscriptions[subscriptionId].owner; - address proposedOwner = s_subscriptions[subscriptionId].proposedOwner; - if (proposedOwner != msg.sender) { - revert MustBeProposedOwner(proposedOwner); - } - s_subscriptions[subscriptionId].owner = msg.sender; - s_subscriptions[subscriptionId].proposedOwner = address(0); - emit SubscriptionOwnerTransferred(subscriptionId, previousOwner, msg.sender); - } - - /// @inheritdoc IFunctionsSubscriptions - function removeConsumer(uint64 subscriptionId, address consumer) external override { - _whenNotPaused(); - _onlySubscriptionOwner(subscriptionId); - _onlySenderThatAcceptedToS(); - - Consumer memory consumerData = s_consumers[consumer][subscriptionId]; - _isAllowedConsumer(consumer, subscriptionId); - if (consumerData.initiatedRequests != consumerData.completedRequests) { - revert CannotRemoveWithPendingRequests(); - } - // Note bounded by config.maxConsumers - address[] memory consumers = s_subscriptions[subscriptionId].consumers; - for (uint256 i = 0; i < consumers.length; ++i) { - if (consumers[i] == consumer) { - // Storage write to preserve last element - s_subscriptions[subscriptionId].consumers[i] = consumers[consumers.length - 1]; - // Storage remove last element - s_subscriptions[subscriptionId].consumers.pop(); - break; - } - } - delete s_consumers[consumer][subscriptionId]; - emit SubscriptionConsumerRemoved(subscriptionId, consumer); - } - - /// @dev Overriden in FunctionsRouter.sol - function _getMaxConsumers() internal view virtual returns (uint16); - - /// @inheritdoc IFunctionsSubscriptions - function addConsumer(uint64 subscriptionId, address consumer) external override { - _whenNotPaused(); - _onlySubscriptionOwner(subscriptionId); - _onlySenderThatAcceptedToS(); - - // Already maxed, cannot add any more consumers. - uint16 maximumConsumers = _getMaxConsumers(); - if (s_subscriptions[subscriptionId].consumers.length >= maximumConsumers) { - revert TooManyConsumers(maximumConsumers); - } - if (s_consumers[consumer][subscriptionId].allowed) { - // Idempotence - do nothing if already added. - // Ensures uniqueness in s_subscriptions[subscriptionId].consumers. - return; - } - - s_consumers[consumer][subscriptionId].allowed = true; - s_subscriptions[subscriptionId].consumers.push(consumer); - - emit SubscriptionConsumerAdded(subscriptionId, consumer); - } - - /// @dev Overriden in FunctionsRouter.sol - function _getSubscriptionDepositDetails() internal virtual returns (uint16, uint72); - - function _cancelSubscriptionHelper(uint64 subscriptionId, address toAddress, bool checkDepositRefundability) private { - Subscription memory subscription = s_subscriptions[subscriptionId]; - uint96 balance = subscription.balance; - uint64 completedRequests = 0; - - // NOTE: loop iterations are bounded by config.maxConsumers - // If no consumers, does nothing. - for (uint256 i = 0; i < subscription.consumers.length; ++i) { - address consumer = subscription.consumers[i]; - completedRequests += s_consumers[consumer][subscriptionId].completedRequests; - delete s_consumers[consumer][subscriptionId]; - } - delete s_subscriptions[subscriptionId]; - - (uint16 subscriptionDepositMinimumRequests, uint72 subscriptionDepositJuels) = _getSubscriptionDepositDetails(); - - // If subscription has not made enough requests, deposit will be forfeited - if (checkDepositRefundability && completedRequests < subscriptionDepositMinimumRequests) { - uint96 deposit = subscriptionDepositJuels > balance ? balance : subscriptionDepositJuels; - if (deposit > 0) { - s_withdrawableTokens[address(this)] += deposit; - balance -= deposit; - } - } - - if (balance > 0) { - s_totalLinkBalance -= balance; - i_linkToken.safeTransfer(toAddress, uint256(balance)); - } - emit SubscriptionCanceled(subscriptionId, toAddress, balance); - } - - /// @inheritdoc IFunctionsSubscriptions - function cancelSubscription(uint64 subscriptionId, address to) external override { - _whenNotPaused(); - _onlySubscriptionOwner(subscriptionId); - _onlySenderThatAcceptedToS(); - - if (pendingRequestExists(subscriptionId)) { - revert CannotRemoveWithPendingRequests(); - } - - _cancelSubscriptionHelper(subscriptionId, to, true); - } - - /// @inheritdoc IFunctionsSubscriptions - function pendingRequestExists(uint64 subscriptionId) public view override returns (bool) { - address[] memory consumers = s_subscriptions[subscriptionId].consumers; - // NOTE: loop iterations are bounded by config.maxConsumers - for (uint256 i = 0; i < consumers.length; ++i) { - Consumer memory consumer = s_consumers[consumers[i]][subscriptionId]; - if (consumer.initiatedRequests != consumer.completedRequests) { - return true; - } - } - return false; - } - - /// @inheritdoc IFunctionsSubscriptions - function setFlags(uint64 subscriptionId, bytes32 flags) external override { - _onlyRouterOwner(); - _isExistingSubscription(subscriptionId); - s_subscriptions[subscriptionId].flags = flags; - } - - /// @inheritdoc IFunctionsSubscriptions - function getFlags(uint64 subscriptionId) public view returns (bytes32) { - return s_subscriptions[subscriptionId].flags; - } - - // ================================================================ - // | Request Timeout | - // ================================================================ - - /// @inheritdoc IFunctionsSubscriptions - function timeoutRequests(FunctionsResponse.Commitment[] calldata requestsToTimeoutByCommitment) external override { - _whenNotPaused(); - - for (uint256 i = 0; i < requestsToTimeoutByCommitment.length; ++i) { - FunctionsResponse.Commitment memory request = requestsToTimeoutByCommitment[i]; - bytes32 requestId = request.requestId; - uint64 subscriptionId = request.subscriptionId; - - // Check that request ID is valid - if (keccak256(abi.encode(request)) != s_requestCommitments[requestId]) { - revert InvalidCalldata(); - } - - // Check that request has exceeded allowed request time - if (block.timestamp < request.timeoutTimestamp) { - revert TimeoutNotExceeded(); - } - - // Notify the Coordinator that the request should no longer be fulfilled - IFunctionsBilling(request.coordinator).deleteCommitment(requestId); - // Release the subscription's balance that had been earmarked for the request - s_subscriptions[subscriptionId].blockedBalance -= request.estimatedTotalCostJuels; - s_consumers[request.client][subscriptionId].completedRequests += 1; - // Delete commitment within Router state - delete s_requestCommitments[requestId]; - - emit RequestTimedOut(requestId); - } - } - - // ================================================================ - // | Modifiers | - // ================================================================ - - function _onlySubscriptionOwner(uint64 subscriptionId) internal view { - address owner = s_subscriptions[subscriptionId].owner; - if (owner == address(0)) { - revert InvalidSubscription(); - } - if (msg.sender != owner) { - revert MustBeSubscriptionOwner(); - } - } - - /// @dev Overriden in FunctionsRouter.sol - function _onlySenderThatAcceptedToS() internal virtual; - - /// @dev Overriden in FunctionsRouter.sol - function _onlyRouterOwner() internal virtual; - - /// @dev Overriden in FunctionsRouter.sol - function _whenNotPaused() internal virtual; -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/accessControl/TermsOfServiceAllowList.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/accessControl/TermsOfServiceAllowList.sol deleted file mode 100644 index 1d9a3b915b1..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/accessControl/TermsOfServiceAllowList.sol +++ /dev/null @@ -1,205 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import {ITermsOfServiceAllowList, TermsOfServiceAllowListConfig} from "./interfaces/ITermsOfServiceAllowList.sol"; -import {IAccessController} from "../../../shared/interfaces/IAccessController.sol"; -import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; - -import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol"; - -import {Address} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol"; -import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; - -/// @notice A contract to handle access control of subscription management dependent on signing a Terms of Service -contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, ITypeAndVersion, ConfirmedOwner { - using Address for address; - using EnumerableSet for EnumerableSet.AddressSet; - - /// @inheritdoc ITypeAndVersion - // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables - string public constant override typeAndVersion = "Functions Terms of Service Allow List v1.1.0"; - - EnumerableSet.AddressSet private s_allowedSenders; - EnumerableSet.AddressSet private s_blockedSenders; - - event AddedAccess(address user); - event BlockedAccess(address user); - event UnblockedAccess(address user); - - error InvalidSignature(); - error InvalidUsage(); - error RecipientIsBlocked(); - error InvalidCalldata(); - - TermsOfServiceAllowListConfig private s_config; - - event ConfigUpdated(TermsOfServiceAllowListConfig config); - - // ================================================================ - // | Initialization | - // ================================================================ - - constructor( - TermsOfServiceAllowListConfig memory config, - address[] memory initialAllowedSenders, - address[] memory initialBlockedSenders - ) ConfirmedOwner(msg.sender) { - updateConfig(config); - - for (uint256 i = 0; i < initialAllowedSenders.length; ++i) { - s_allowedSenders.add(initialAllowedSenders[i]); - } - - for (uint256 j = 0; j < initialBlockedSenders.length; ++j) { - if (s_allowedSenders.contains(initialBlockedSenders[j])) { - // Allowed senders cannot also be blocked - revert InvalidCalldata(); - } - s_blockedSenders.add(initialBlockedSenders[j]); - } - } - - // ================================================================ - // | Configuration | - // ================================================================ - - /// @notice Gets the contracts's configuration - /// @return config - function getConfig() external view returns (TermsOfServiceAllowListConfig memory) { - return s_config; - } - - /// @notice Sets the contracts's configuration - /// @param config - See the contents of the TermsOfServiceAllowListConfig struct in ITermsOfServiceAllowList.sol for more information - function updateConfig(TermsOfServiceAllowListConfig memory config) public onlyOwner { - s_config = config; - emit ConfigUpdated(config); - } - - // ================================================================ - // | Allow methods | - // ================================================================ - - /// @inheritdoc ITermsOfServiceAllowList - function getMessage(address acceptor, address recipient) public pure override returns (bytes32) { - return keccak256(abi.encodePacked(acceptor, recipient)); - } - - /// @inheritdoc ITermsOfServiceAllowList - function acceptTermsOfService(address acceptor, address recipient, bytes32 r, bytes32 s, uint8 v) external override { - if (s_blockedSenders.contains(recipient)) { - revert RecipientIsBlocked(); - } - - // Validate that the signature is correct and the correct data has been signed - bytes32 prefixedMessage = keccak256( - abi.encodePacked("\x19Ethereum Signed Message:\n32", getMessage(acceptor, recipient)) - ); - if (ecrecover(prefixedMessage, v, r, s) != s_config.signerPublicKey) { - revert InvalidSignature(); - } - - // If contract, validate that msg.sender == recipient - // This is to prevent EoAs from claiming contracts that they are not in control of - // If EoA, validate that msg.sender == acceptor == recipient - // This is to prevent EoAs from accepting for other EoAs - if (msg.sender != recipient || (msg.sender != acceptor && !msg.sender.isContract())) { - revert InvalidUsage(); - } - - // Add recipient to the allow list - if (s_allowedSenders.add(recipient)) { - emit AddedAccess(recipient); - } - } - - /// @inheritdoc ITermsOfServiceAllowList - function getAllAllowedSenders() external view override returns (address[] memory) { - return s_allowedSenders.values(); - } - - /// @inheritdoc ITermsOfServiceAllowList - function getAllowedSendersCount() external view override returns (uint64) { - return uint64(s_allowedSenders.length()); - } - - /// @inheritdoc ITermsOfServiceAllowList - function getAllowedSendersInRange( - uint64 allowedSenderIdxStart, - uint64 allowedSenderIdxEnd - ) external view override returns (address[] memory allowedSenders) { - if ( - allowedSenderIdxStart > allowedSenderIdxEnd || - allowedSenderIdxEnd >= s_allowedSenders.length() || - s_allowedSenders.length() == 0 - ) { - revert InvalidCalldata(); - } - - allowedSenders = new address[]((allowedSenderIdxEnd - allowedSenderIdxStart) + 1); - for (uint256 i = 0; i <= allowedSenderIdxEnd - allowedSenderIdxStart; ++i) { - allowedSenders[i] = s_allowedSenders.at(uint256(allowedSenderIdxStart + i)); - } - - return allowedSenders; - } - - /// @inheritdoc IAccessController - function hasAccess(address user, bytes calldata /* data */) external view override returns (bool) { - if (!s_config.enabled) { - return true; - } - return s_allowedSenders.contains(user); - } - - // ================================================================ - // | Block methods | - // ================================================================ - - /// @inheritdoc ITermsOfServiceAllowList - function isBlockedSender(address sender) external view override returns (bool) { - if (!s_config.enabled) { - return false; - } - return s_blockedSenders.contains(sender); - } - - /// @inheritdoc ITermsOfServiceAllowList - function blockSender(address sender) external override onlyOwner { - s_allowedSenders.remove(sender); - s_blockedSenders.add(sender); - emit BlockedAccess(sender); - } - - /// @inheritdoc ITermsOfServiceAllowList - function unblockSender(address sender) external override onlyOwner { - s_blockedSenders.remove(sender); - emit UnblockedAccess(sender); - } - - /// @inheritdoc ITermsOfServiceAllowList - function getBlockedSendersCount() external view override returns (uint64) { - return uint64(s_blockedSenders.length()); - } - - /// @inheritdoc ITermsOfServiceAllowList - function getBlockedSendersInRange( - uint64 blockedSenderIdxStart, - uint64 blockedSenderIdxEnd - ) external view override returns (address[] memory blockedSenders) { - if ( - blockedSenderIdxStart > blockedSenderIdxEnd || - blockedSenderIdxEnd >= s_blockedSenders.length() || - s_blockedSenders.length() == 0 - ) { - revert InvalidCalldata(); - } - - blockedSenders = new address[]((blockedSenderIdxEnd - blockedSenderIdxStart) + 1); - for (uint256 i = 0; i <= blockedSenderIdxEnd - blockedSenderIdxStart; ++i) { - blockedSenders[i] = s_blockedSenders.at(uint256(blockedSenderIdxStart + i)); - } - - return blockedSenders; - } -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/accessControl/interfaces/ITermsOfServiceAllowList.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/accessControl/interfaces/ITermsOfServiceAllowList.sol deleted file mode 100644 index 65db9c42b69..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/accessControl/interfaces/ITermsOfServiceAllowList.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -/// @notice A contract to handle access control of subscription management dependent on signing a Terms of Service -interface ITermsOfServiceAllowList { - /// @notice Return the message data for the proof given to accept the Terms of Service - /// @param acceptor - The wallet address that has accepted the Terms of Service on the UI - /// @param recipient - The recipient address that the acceptor is taking responsibility for - /// @return Hash of the message data - function getMessage(address acceptor, address recipient) external pure returns (bytes32); - - /// @notice Check if the address is blocked for usage - /// @param sender The transaction sender's address - /// @return True or false - function isBlockedSender(address sender) external returns (bool); - - /// @notice Get a list of all allowed senders - /// @dev WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - /// to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - /// this function has an unbounded cost, and using it as part of a state-changing function may render the function - /// uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - /// @return addresses - all allowed addresses - function getAllAllowedSenders() external view returns (address[] memory); - - /// @notice Get details about the total number of allowed senders - /// @return count - total number of allowed senders in the system - function getAllowedSendersCount() external view returns (uint64); - - /// @notice Retrieve a list of allowed senders using an inclusive range - /// @dev WARNING: getAllowedSendersInRange uses EnumerableSet .length() and .at() methods to iterate over the list - /// without the need for an extra mapping. These method can not guarantee the ordering when new elements are added. - /// Evaluate if eventual consistency will satisfy your usecase before using it. - /// @param allowedSenderIdxStart - index of the allowed sender to start the range at - /// @param allowedSenderIdxEnd - index of the allowed sender to end the range at - /// @return allowedSenders - allowed addresses in the range provided - function getAllowedSendersInRange( - uint64 allowedSenderIdxStart, - uint64 allowedSenderIdxEnd - ) external view returns (address[] memory allowedSenders); - - /// @notice Allows access to the sender based on acceptance of the Terms of Service - /// @param acceptor - The wallet address that has accepted the Terms of Service on the UI - /// @param recipient - The recipient address that the acceptor is taking responsibility for - /// @param r - ECDSA signature r data produced by the Chainlink Functions Subscription UI - /// @param s - ECDSA signature s produced by the Chainlink Functions Subscription UI - /// @param v - ECDSA signature v produced by the Chainlink Functions Subscription UI - function acceptTermsOfService(address acceptor, address recipient, bytes32 r, bytes32 s, uint8 v) external; - - /// @notice Removes a sender's access if already authorized, and disallows re-accepting the Terms of Service - /// @param sender - Address of the sender to block - function blockSender(address sender) external; - - /// @notice Re-allows a previously blocked sender to accept the Terms of Service - /// @param sender - Address of the sender to unblock - function unblockSender(address sender) external; - - /// @notice Get details about the total number of blocked senders - /// @return count - total number of blocked senders in the system - function getBlockedSendersCount() external view returns (uint64); - - /// @notice Retrieve a list of blocked senders using an inclusive range - /// @dev WARNING: getBlockedSendersInRange uses EnumerableSet .length() and .at() methods to iterate over the list - /// without the need for an extra mapping. These method can not guarantee the ordering when new elements are added. - /// Evaluate if eventual consistency will satisfy your usecase before using it. - /// @param blockedSenderIdxStart - index of the blocked sender to start the range at - /// @param blockedSenderIdxEnd - index of the blocked sender to end the range at - /// @return blockedSenders - blocked addresses in the range provided - function getBlockedSendersInRange( - uint64 blockedSenderIdxStart, - uint64 blockedSenderIdxEnd - ) external view returns (address[] memory blockedSenders); -} - -// ================================================================ -// | Configuration state | -// ================================================================ -struct TermsOfServiceAllowListConfig { - bool enabled; // ═════════════╗ When enabled, access will be checked against s_allowedSenders. When disabled, all access will be allowed. - address signerPublicKey; // ══╝ The key pair that needs to sign the acceptance data -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsBilling.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsBilling.sol deleted file mode 100644 index 79806f1eb18..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsBilling.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -/// @title Chainlink Functions DON billing interface. -interface IFunctionsBilling { - /// @notice Return the current conversion from WEI of ETH to LINK from the configured Chainlink data feed - /// @return weiPerUnitLink - The amount of WEI in one LINK - function getWeiPerUnitLink() external view returns (uint256); - - /// @notice Return the current conversion from LINK to USD from the configured Chainlink data feed - /// @return weiPerUnitLink - The amount of USD that one LINK is worth - /// @return decimals - The number of decimals that should be represented in the price feed's response - function getUsdPerUnitLink() external view returns (uint256, uint8); - - /// @notice Determine the fee that will be split between Node Operators for servicing a request - /// @param requestCBOR - CBOR encoded Chainlink Functions request data, use FunctionsRequest library to encode a request - /// @return fee - Cost in Juels (1e18) of LINK - function getDONFeeJuels(bytes memory requestCBOR) external view returns (uint72); - - /// @notice Determine the fee that will be paid to the Coordinator owner for operating the network - /// @return fee - Cost in Juels (1e18) of LINK - function getOperationFeeJuels() external view returns (uint72); - - /// @notice Determine the fee that will be paid to the Router owner for operating the network - /// @return fee - Cost in Juels (1e18) of LINK - function getAdminFeeJuels() external view returns (uint72); - - /// @notice Estimate the total cost that will be charged to a subscription to make a request: transmitter gas re-reimbursement, plus DON fee, plus Registry fee - /// @param - subscriptionId An identifier of the billing account - /// @param - data Encoded Chainlink Functions request data, use FunctionsClient API to encode a request - /// @param - callbackGasLimit Gas limit for the fulfillment callback - /// @param - gasPriceWei The blockchain's gas price to estimate with - /// @return - billedCost Cost in Juels (1e18) of LINK - function estimateCost( - uint64 subscriptionId, - bytes calldata data, - uint32 callbackGasLimit, - uint256 gasPriceWei - ) external view returns (uint96); - - /// @notice Remove a request commitment that the Router has determined to be stale - /// @param requestId - The request ID to remove - function deleteCommitment(bytes32 requestId) external; - - /// @notice Oracle withdraw LINK earned through fulfilling requests - /// @notice If amount is 0 the full balance will be withdrawn - /// @param recipient where to send the funds - /// @param amount amount to withdraw - function oracleWithdraw(address recipient, uint96 amount) external; - - /// @notice Withdraw all LINK earned by Oracles through fulfilling requests - /// @dev transmitter addresses must support LINK tokens to avoid tokens from getting stuck as oracleWithdrawAll() calls will forward tokens directly to transmitters - function oracleWithdrawAll() external; -} - -// ================================================================ -// | Configuration state | -// ================================================================ - -struct FunctionsBillingConfig { - uint32 fulfillmentGasPriceOverEstimationBP; // ══╗ Percentage of gas price overestimation to account for changes in gas price between request and response. Held as basis points (one hundredth of 1 percentage point) - uint32 feedStalenessSeconds; // ║ How long before we consider the feed price to be stale and fallback to fallbackNativePerUnitLink. - uint32 gasOverheadBeforeCallback; // ║ Represents the average gas execution cost before the fulfillment callback. This amount is always billed for every request. - uint32 gasOverheadAfterCallback; // ║ Represents the average gas execution cost after the fulfillment callback. This amount is always billed for every request. - uint40 minimumEstimateGasPriceWei; // ║ The lowest amount of wei that will be used as the tx.gasprice when estimating the cost to fulfill the request - uint16 maxSupportedRequestDataVersion; // ║ The highest support request data version supported by the node. All lower versions should also be supported. - uint64 fallbackUsdPerUnitLink; // ║ Fallback LINK / USD conversion rate if the data feed is stale - uint8 fallbackUsdPerUnitLinkDecimals; // ════════╝ Fallback LINK / USD conversion rate decimal places if the data feed is stale - uint224 fallbackNativePerUnitLink; // ═══════════╗ Fallback NATIVE CURRENCY / LINK conversion rate if the data feed is stale - uint32 requestTimeoutSeconds; // ════════════════╝ How many seconds it takes before we consider a request to be timed out - uint16 donFeeCentsUsd; // ═══════════════════════════════╗ Additional flat fee (denominated in cents of USD, paid as LINK) that will be split between Node Operators. - uint16 operationFeeCentsUsd; // ═════════════════════════╝ Additional flat fee (denominated in cents of USD, paid as LINK) that will be paid to the owner of the Coordinator contract. -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsClient.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsClient.sol deleted file mode 100644 index f28a41666b5..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsClient.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -/// @title Chainlink Functions client interface. -interface IFunctionsClient { - /// @notice Chainlink Functions response handler called by the Functions Router - /// during fullilment from the designated transmitter node in an OCR round. - /// @param requestId The requestId returned by FunctionsClient.sendRequest(). - /// @param response Aggregated response from the request's source code. - /// @param err Aggregated error either from the request's source code or from the execution pipeline. - /// @dev Either response or error parameter will be set, but never both. - function handleOracleFulfillment(bytes32 requestId, bytes memory response, bytes memory err) external; -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsCoordinator.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsCoordinator.sol deleted file mode 100644 index 4e2bd703dc4..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsCoordinator.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import {FunctionsResponse} from "../libraries/FunctionsResponse.sol"; - -/// @title Chainlink Functions DON Coordinator interface. -interface IFunctionsCoordinator { - /// @notice Returns the DON's threshold encryption public key used to encrypt secrets - /// @dev All nodes on the DON have separate key shares of the threshold decryption key - /// and nodes must participate in a threshold decryption OCR round to decrypt secrets - /// @return thresholdPublicKey the DON's threshold encryption public key - function getThresholdPublicKey() external view returns (bytes memory); - - /// @notice Sets the DON's threshold encryption public key used to encrypt secrets - /// @dev Used to rotate the key - /// @param thresholdPublicKey The new public key - function setThresholdPublicKey(bytes calldata thresholdPublicKey) external; - - /// @notice Returns the DON's secp256k1 public key that is used to encrypt secrets - /// @dev All nodes on the DON have the corresponding private key - /// needed to decrypt the secrets encrypted with the public key - /// @return publicKey the DON's public key - function getDONPublicKey() external view returns (bytes memory); - - /// @notice Sets DON's secp256k1 public key used to encrypt secrets - /// @dev Used to rotate the key - /// @param donPublicKey The new public key - function setDONPublicKey(bytes calldata donPublicKey) external; - - /// @notice Receives a request to be emitted to the DON for processing - /// @param request The request metadata - /// @dev see the struct for field descriptions - /// @return commitment - The parameters of the request that must be held consistent at response time - function startRequest( - FunctionsResponse.RequestMeta calldata request - ) external returns (FunctionsResponse.Commitment memory commitment); -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsRouter.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsRouter.sol deleted file mode 100644 index 5f93aac873e..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsRouter.sol +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import {FunctionsResponse} from "../libraries/FunctionsResponse.sol"; - -/// @title Chainlink Functions Router interface. -interface IFunctionsRouter { - /// @notice The identifier of the route to retrieve the address of the access control contract - /// The access control contract controls which accounts can manage subscriptions - /// @return id - bytes32 id that can be passed to the "getContractById" of the Router - function getAllowListId() external view returns (bytes32); - - /// @notice Set the identifier of the route to retrieve the address of the access control contract - /// The access control contract controls which accounts can manage subscriptions - function setAllowListId(bytes32 allowListId) external; - - /// @notice Get the flat fee (in Juels of LINK) that will be paid to the Router owner for operation of the network - /// @return adminFee - function getAdminFee() external view returns (uint72 adminFee); - - /// @notice Sends a request using the provided subscriptionId - /// @param subscriptionId - A unique subscription ID allocated by billing system, - /// a client can make requests from different contracts referencing the same subscription - /// @param data - CBOR encoded Chainlink Functions request data, use FunctionsClient API to encode a request - /// @param dataVersion - Gas limit for the fulfillment callback - /// @param callbackGasLimit - Gas limit for the fulfillment callback - /// @param donId - An identifier used to determine which route to send the request along - /// @return requestId - A unique request identifier - function sendRequest( - uint64 subscriptionId, - bytes calldata data, - uint16 dataVersion, - uint32 callbackGasLimit, - bytes32 donId - ) external returns (bytes32); - - /// @notice Sends a request to the proposed contracts - /// @param subscriptionId - A unique subscription ID allocated by billing system, - /// a client can make requests from different contracts referencing the same subscription - /// @param data - CBOR encoded Chainlink Functions request data, use FunctionsClient API to encode a request - /// @param dataVersion - Gas limit for the fulfillment callback - /// @param callbackGasLimit - Gas limit for the fulfillment callback - /// @param donId - An identifier used to determine which route to send the request along - /// @return requestId - A unique request identifier - function sendRequestToProposed( - uint64 subscriptionId, - bytes calldata data, - uint16 dataVersion, - uint32 callbackGasLimit, - bytes32 donId - ) external returns (bytes32); - - /// @notice Fulfill the request by: - /// - calling back the data that the Oracle returned to the client contract - /// - pay the DON for processing the request - /// @dev Only callable by the Coordinator contract that is saved in the commitment - /// @param response response data from DON consensus - /// @param err error from DON consensus - /// @param juelsPerGas - current rate of juels/gas - /// @param costWithoutFulfillment - The cost of processing the request (in Juels of LINK ), without fulfillment - /// @param transmitter - The Node that transmitted the OCR report - /// @param commitment - The parameters of the request that must be held consistent between request and response time - /// @return fulfillResult - - /// @return callbackGasCostJuels - - function fulfill( - bytes memory response, - bytes memory err, - uint96 juelsPerGas, - uint96 costWithoutFulfillment, - address transmitter, - FunctionsResponse.Commitment memory commitment - ) external returns (FunctionsResponse.FulfillResult, uint96); - - /// @notice Validate requested gas limit is below the subscription max. - /// @param subscriptionId subscription ID - /// @param callbackGasLimit desired callback gas limit - function isValidCallbackGasLimit(uint64 subscriptionId, uint32 callbackGasLimit) external view; - - /// @notice Get the current contract given an ID - /// @param id A bytes32 identifier for the route - /// @return contract The current contract address - function getContractById(bytes32 id) external view returns (address); - - /// @notice Get the proposed next contract given an ID - /// @param id A bytes32 identifier for the route - /// @return contract The current or proposed contract address - function getProposedContractById(bytes32 id) external view returns (address); - - /// @notice Return the latest proprosal set - /// @return ids The identifiers of the contracts to update - /// @return to The addresses of the contracts that will be updated to - function getProposedContractSet() external view returns (bytes32[] memory, address[] memory); - - /// @notice Proposes one or more updates to the contract routes - /// @dev Only callable by owner - function proposeContractsUpdate(bytes32[] memory proposalSetIds, address[] memory proposalSetAddresses) external; - - /// @notice Updates the current contract routes to the proposed contracts - /// @dev Only callable by owner - function updateContracts() external; - - /// @dev Puts the system into an emergency stopped state. - /// @dev Only callable by owner - function pause() external; - - /// @dev Takes the system out of an emergency stopped state. - /// @dev Only callable by owner - function unpause() external; -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsSubscriptions.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsSubscriptions.sol deleted file mode 100644 index eafd6f4fe99..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IFunctionsSubscriptions.sol +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import {FunctionsResponse} from "../libraries/FunctionsResponse.sol"; - -/// @title Chainlink Functions Subscription interface. -interface IFunctionsSubscriptions { - struct Subscription { - uint96 balance; // ═════════╗ Common LINK balance that is controlled by the Router to be used for all consumer requests. - address owner; // ══════════╝ The owner can fund/withdraw/cancel the subscription. - uint96 blockedBalance; // ══╗ LINK balance that is reserved to pay for pending consumer requests. - address proposedOwner; // ══╝ For safely transferring sub ownership. - address[] consumers; // ════╸ Client contracts that can use the subscription - bytes32 flags; // ══════════╸ Per-subscription flags - } - - struct Consumer { - bool allowed; // ══════════════╗ Owner can fund/withdraw/cancel the sub. - uint64 initiatedRequests; // ║ The number of requests that have been started - uint64 completedRequests; // ══╝ The number of requests that have successfully completed or timed out - } - - /// @notice Get details about a subscription. - /// @param subscriptionId - the ID of the subscription - /// @return subscription - see IFunctionsSubscriptions.Subscription for more information on the structure - function getSubscription(uint64 subscriptionId) external view returns (Subscription memory); - - /// @notice Retrieve details about multiple subscriptions using an inclusive range - /// @param subscriptionIdStart - the ID of the subscription to start the range at - /// @param subscriptionIdEnd - the ID of the subscription to end the range at - /// @return subscriptions - see IFunctionsSubscriptions.Subscription for more information on the structure - function getSubscriptionsInRange( - uint64 subscriptionIdStart, - uint64 subscriptionIdEnd - ) external view returns (Subscription[] memory); - - /// @notice Get details about a consumer of a subscription. - /// @param client - the consumer contract address - /// @param subscriptionId - the ID of the subscription - /// @return consumer - see IFunctionsSubscriptions.Consumer for more information on the structure - function getConsumer(address client, uint64 subscriptionId) external view returns (Consumer memory); - - /// @notice Get details about the total amount of LINK within the system - /// @return totalBalance - total Juels of LINK held by the contract - function getTotalBalance() external view returns (uint96); - - /// @notice Get details about the total number of subscription accounts - /// @return count - total number of subscriptions in the system - function getSubscriptionCount() external view returns (uint64); - - /// @notice Time out all expired requests: unlocks funds and removes the ability for the request to be fulfilled - /// @param requestsToTimeoutByCommitment - A list of request commitments to time out - /// @dev The commitment can be found on the "OracleRequest" event created when sending the request. - function timeoutRequests(FunctionsResponse.Commitment[] calldata requestsToTimeoutByCommitment) external; - - /// @notice Oracle withdraw LINK earned through fulfilling requests - /// @notice If amount is 0 the full balance will be withdrawn - /// @notice Both signing and transmitting wallets will have a balance to withdraw - /// @param recipient where to send the funds - /// @param amount amount to withdraw - function oracleWithdraw(address recipient, uint96 amount) external; - - /// @notice Owner cancel subscription, sends remaining link directly to the subscription owner. - /// @dev Only callable by the Router Owner - /// @param subscriptionId subscription id - /// @dev notably can be called even if there are pending requests, outstanding ones may fail onchain - function ownerCancelSubscription(uint64 subscriptionId) external; - - /// @notice Recover link sent with transfer instead of transferAndCall. - /// @dev Only callable by the Router Owner - /// @param to address to send link to - function recoverFunds(address to) external; - - /// @notice Create a new subscription. - /// @return subscriptionId - A unique subscription id. - /// @dev You can manage the consumer set dynamically with addConsumer/removeConsumer. - /// @dev Note to fund the subscription, use transferAndCall. For example - /// @dev LINKTOKEN.transferAndCall( - /// @dev address(ROUTER), - /// @dev amount, - /// @dev abi.encode(subscriptionId)); - function createSubscription() external returns (uint64); - - /// @notice Create a new subscription and add a consumer. - /// @return subscriptionId - A unique subscription id. - /// @dev You can manage the consumer set dynamically with addConsumer/removeConsumer. - /// @dev Note to fund the subscription, use transferAndCall. For example - /// @dev LINKTOKEN.transferAndCall( - /// @dev address(ROUTER), - /// @dev amount, - /// @dev abi.encode(subscriptionId)); - function createSubscriptionWithConsumer(address consumer) external returns (uint64 subscriptionId); - - /// @notice Propose a new owner for a subscription. - /// @dev Only callable by the Subscription's owner - /// @param subscriptionId - ID of the subscription - /// @param newOwner - proposed new owner of the subscription - function proposeSubscriptionOwnerTransfer(uint64 subscriptionId, address newOwner) external; - - /// @notice Accept an ownership transfer. - /// @param subscriptionId - ID of the subscription - /// @dev will revert if original owner of subscriptionId has not requested that msg.sender become the new owner. - function acceptSubscriptionOwnerTransfer(uint64 subscriptionId) external; - - /// @notice Remove a consumer from a Chainlink Functions subscription. - /// @dev Only callable by the Subscription's owner - /// @param subscriptionId - ID of the subscription - /// @param consumer - Consumer to remove from the subscription - function removeConsumer(uint64 subscriptionId, address consumer) external; - - /// @notice Add a consumer to a Chainlink Functions subscription. - /// @dev Only callable by the Subscription's owner - /// @param subscriptionId - ID of the subscription - /// @param consumer - New consumer which can use the subscription - function addConsumer(uint64 subscriptionId, address consumer) external; - - /// @notice Cancel a subscription - /// @dev Only callable by the Subscription's owner - /// @param subscriptionId - ID of the subscription - /// @param to - Where to send the remaining LINK to - function cancelSubscription(uint64 subscriptionId, address to) external; - - /// @notice Check to see if there exists a request commitment for all consumers for a given sub. - /// @param subscriptionId - ID of the subscription - /// @return true if there exists at least one unfulfilled request for the subscription, false otherwise. - /// @dev Looping is bounded to MAX_CONSUMERS*(number of DONs). - /// @dev Used to disable subscription canceling while outstanding request are present. - function pendingRequestExists(uint64 subscriptionId) external view returns (bool); - - /// @notice Set subscription specific flags for a subscription. - /// Each byte of the flag is used to represent a resource tier that the subscription can utilize. - /// @param subscriptionId - ID of the subscription - /// @param flags - desired flag values - function setFlags(uint64 subscriptionId, bytes32 flags) external; - - /// @notice Get flags for a given subscription. - /// @param subscriptionId - ID of the subscription - /// @return flags - current flag values - function getFlags(uint64 subscriptionId) external view returns (bytes32); -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IOwnableFunctionsRouter.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IOwnableFunctionsRouter.sol deleted file mode 100644 index c5f3d82677e..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/interfaces/IOwnableFunctionsRouter.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import {IFunctionsRouter} from "./IFunctionsRouter.sol"; -import {IOwnable} from "../../../shared/interfaces/IOwnable.sol"; - -/// @title Chainlink Functions Router interface with Ownability. -interface IOwnableFunctionsRouter is IOwnable, IFunctionsRouter {} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/libraries/FunctionsRequest.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/libraries/FunctionsRequest.sol deleted file mode 100644 index a44c1d491fe..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/libraries/FunctionsRequest.sol +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import {CBOR} from "../../../vendor/solidity-cborutils/v2.0.0/CBOR.sol"; - -/// @title Library for encoding the input data of a Functions request into CBOR -library FunctionsRequest { - using CBOR for CBOR.CBORBuffer; - - uint16 public constant REQUEST_DATA_VERSION = 1; - uint256 internal constant DEFAULT_BUFFER_SIZE = 256; - - enum Location { - Inline, // Provided within the Request - Remote, // Hosted through remote location that can be accessed through a provided URL - DONHosted // Hosted on the DON's storage - } - - enum CodeLanguage { - JavaScript - // In future version we may add other languages - } - - struct Request { - Location codeLocation; // ════════════╸ The location of the source code that will be executed on each node in the DON - Location secretsLocation; // ═════════╸ The location of secrets that will be passed into the source code. *Only Remote secrets are supported - CodeLanguage language; // ════════════╸ The coding language that the source code is written in - string source; // ════════════════════╸ Raw source code for Request.codeLocation of Location.Inline, URL for Request.codeLocation of Location.Remote, or slot decimal number for Request.codeLocation of Location.DONHosted - bytes encryptedSecretsReference; // ══╸ Encrypted URLs for Request.secretsLocation of Location.Remote (use addSecretsReference()), or CBOR encoded slotid+version for Request.secretsLocation of Location.DONHosted (use addDONHostedSecrets()) - string[] args; // ════════════════════╸ String arguments that will be passed into the source code - bytes[] bytesArgs; // ════════════════╸ Bytes arguments that will be passed into the source code - } - - error EmptySource(); - error EmptySecrets(); - error EmptyArgs(); - error NoInlineSecrets(); - - /// @notice Encodes a Request to CBOR encoded bytes - /// @param self The request to encode - /// @return CBOR encoded bytes - function _encodeCBOR(Request memory self) internal pure returns (bytes memory) { - CBOR.CBORBuffer memory buffer = CBOR.create(DEFAULT_BUFFER_SIZE); - - buffer.writeString("codeLocation"); - buffer.writeUInt256(uint256(self.codeLocation)); - - buffer.writeString("language"); - buffer.writeUInt256(uint256(self.language)); - - buffer.writeString("source"); - buffer.writeString(self.source); - - if (self.args.length > 0) { - buffer.writeString("args"); - buffer.startArray(); - for (uint256 i = 0; i < self.args.length; ++i) { - buffer.writeString(self.args[i]); - } - buffer.endSequence(); - } - - if (self.encryptedSecretsReference.length > 0) { - if (self.secretsLocation == Location.Inline) { - revert NoInlineSecrets(); - } - buffer.writeString("secretsLocation"); - buffer.writeUInt256(uint256(self.secretsLocation)); - buffer.writeString("secrets"); - buffer.writeBytes(self.encryptedSecretsReference); - } - - if (self.bytesArgs.length > 0) { - buffer.writeString("bytesArgs"); - buffer.startArray(); - for (uint256 i = 0; i < self.bytesArgs.length; ++i) { - buffer.writeBytes(self.bytesArgs[i]); - } - buffer.endSequence(); - } - - return buffer.buf.buf; - } - - /// @notice Initializes a Chainlink Functions Request - /// @dev Sets the codeLocation and code on the request - /// @param self The uninitialized request - /// @param codeLocation The user provided source code location - /// @param language The programming language of the user code - /// @param source The user provided source code or a url - function _initializeRequest( - Request memory self, - Location codeLocation, - CodeLanguage language, - string memory source - ) internal pure { - if (bytes(source).length == 0) revert EmptySource(); - - self.codeLocation = codeLocation; - self.language = language; - self.source = source; - } - - /// @notice Initializes a Chainlink Functions Request - /// @dev Simplified version of initializeRequest for PoC - /// @param self The uninitialized request - /// @param javaScriptSource The user provided JS code (must not be empty) - function _initializeRequestForInlineJavaScript(Request memory self, string memory javaScriptSource) internal pure { - _initializeRequest(self, Location.Inline, CodeLanguage.JavaScript, javaScriptSource); - } - - /// @notice Adds Remote user encrypted secrets to a Request - /// @param self The initialized request - /// @param encryptedSecretsReference Encrypted comma-separated string of URLs pointing to off-chain secrets - function _addSecretsReference(Request memory self, bytes memory encryptedSecretsReference) internal pure { - if (encryptedSecretsReference.length == 0) revert EmptySecrets(); - - self.secretsLocation = Location.Remote; - self.encryptedSecretsReference = encryptedSecretsReference; - } - - /// @notice Adds DON-hosted secrets reference to a Request - /// @param self The initialized request - /// @param slotID Slot ID of the user's secrets hosted on DON - /// @param version User data version (for the slotID) - function _addDONHostedSecrets(Request memory self, uint8 slotID, uint64 version) internal pure { - CBOR.CBORBuffer memory buffer = CBOR.create(DEFAULT_BUFFER_SIZE); - - buffer.writeString("slotID"); - buffer.writeUInt64(slotID); - buffer.writeString("version"); - buffer.writeUInt64(version); - - self.secretsLocation = Location.DONHosted; - self.encryptedSecretsReference = buffer.buf.buf; - } - - /// @notice Sets args for the user run function - /// @param self The initialized request - /// @param args The array of string args (must not be empty) - function _setArgs(Request memory self, string[] memory args) internal pure { - if (args.length == 0) revert EmptyArgs(); - - self.args = args; - } - - /// @notice Sets bytes args for the user run function - /// @param self The initialized request - /// @param args The array of bytes args (must not be empty) - function _setBytesArgs(Request memory self, bytes[] memory args) internal pure { - if (args.length == 0) revert EmptyArgs(); - - self.bytesArgs = args; - } -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/libraries/FunctionsResponse.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/libraries/FunctionsResponse.sol deleted file mode 100644 index 65fad665d69..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/libraries/FunctionsResponse.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -/// @title Library of types that are used for fulfillment of a Functions request -library FunctionsResponse { - // Used to send request information from the Router to the Coordinator - struct RequestMeta { - bytes data; // ══════════════════╸ CBOR encoded Chainlink Functions request data, use FunctionsRequest library to encode a request - bytes32 flags; // ═══════════════╸ Per-subscription flags - address requestingContract; // ══╗ The client contract that is sending the request - uint96 availableBalance; // ═════╝ Common LINK balance of the subscription that is controlled by the Router to be used for all consumer requests. - uint72 adminFee; // ═════════════╗ Flat fee (in Juels of LINK) that will be paid to the Router Owner for operation of the network - uint64 subscriptionId; // ║ Identifier of the billing subscription that will be charged for the request - uint64 initiatedRequests; // ║ The number of requests that have been started - uint32 callbackGasLimit; // ║ The amount of gas that the callback to the consuming contract will be given - uint16 dataVersion; // ══════════╝ The version of the structure of the CBOR encoded request data - uint64 completedRequests; // ════╗ The number of requests that have successfully completed or timed out - address subscriptionOwner; // ═══╝ The owner of the billing subscription - } - - enum FulfillResult { - FULFILLED, // 0 - USER_CALLBACK_ERROR, // 1 - INVALID_REQUEST_ID, // 2 - COST_EXCEEDS_COMMITMENT, // 3 - INSUFFICIENT_GAS_PROVIDED, // 4 - SUBSCRIPTION_BALANCE_INVARIANT_VIOLATION, // 5 - INVALID_COMMITMENT // 6 - } - - struct Commitment { - bytes32 requestId; // ═════════════════╸ A unique identifier for a Chainlink Functions request - address coordinator; // ═══════════════╗ The Coordinator contract that manages the DON that is servicing a request - uint96 estimatedTotalCostJuels; // ════╝ The maximum cost in Juels (1e18) of LINK that will be charged to fulfill a request - address client; // ════════════════════╗ The client contract that sent the request - uint64 subscriptionId; // ║ Identifier of the billing subscription that will be charged for the request - uint32 callbackGasLimit; // ═══════════╝ The amount of gas that the callback to the consuming contract will be given - uint72 adminFee; // ═══════════════════╗ Flat fee (in Juels of LINK) that will be paid to the Router Owner for operation of the network - uint72 donFee; // ║ Fee (in Juels of LINK) that will be split between Node Operators for servicing a request - uint40 gasOverheadBeforeCallback; // ║ Represents the average gas execution cost before the fulfillment callback. - uint40 gasOverheadAfterCallback; // ║ Represents the average gas execution cost after the fulfillment callback. - uint32 timeoutTimestamp; // ═══════════╝ The timestamp at which a request will be eligible to be timed out - } -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/ocr/OCR2Abstract.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/ocr/OCR2Abstract.sol deleted file mode 100644 index 4182227d645..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/ocr/OCR2Abstract.sol +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; - -abstract contract OCR2Abstract is ITypeAndVersion { - // Maximum number of oracles the offchain reporting protocol is designed for - uint256 internal constant MAX_NUM_ORACLES = 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); - - /** - * @notice optionally emited to indicate the latest configDigest and epoch for - which a report was successfully transmited. 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 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; -} diff --git a/contracts/src/v0.8/functions/v1_3_0_zksync/ocr/OCR2Base.sol b/contracts/src/v0.8/functions/v1_3_0_zksync/ocr/OCR2Base.sol deleted file mode 100644 index 565e7d800dd..00000000000 --- a/contracts/src/v0.8/functions/v1_3_0_zksync/ocr/OCR2Base.sol +++ /dev/null @@ -1,353 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol"; -import {OCR2Abstract} from "./OCR2Abstract.sol"; - -/** - * @notice Onchain verification of reports from the offchain reporting protocol - * @dev For details on its operation, see the offchain reporting protocol design - * doc, which refers to this contract as simply the "contract". - */ -abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { - error ReportInvalid(string message); - error InvalidConfig(string message); - - constructor() ConfirmedOwner(msg.sender) {} - - // incremented each time a new config is posted. This count is incorporated - // into the config digest, to prevent replay attacks. - uint32 internal s_configCount; - uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems - // to extract config from logs. - - // Storing these fields used on the hot path in a ConfigInfo variable reduces the - // retrieval of all of them to a single SLOAD. If any further fields are - // added, make sure that storage of the struct still takes at most 32 bytes. - struct ConfigInfo { - bytes32 latestConfigDigest; - uint8 f; // TODO: could be optimized by squeezing into one slot - uint8 n; - } - ConfigInfo internal s_configInfo; - - // Used for s_oracles[a].role, where a is an address, to track the purpose - // of the address, or to indicate that the address is unset. - enum Role { - // No oracle role has been set for address a - Unset, - // Signing address for the s_oracles[a].index'th oracle. I.e., report - // signatures from this oracle should ecrecover back to address a. - Signer, - // Transmission address for the s_oracles[a].index'th oracle. I.e., if a - // report is received by OCR2Aggregator.transmit in which msg.sender is - // a, it is attributed to the s_oracles[a].index'th oracle. - Transmitter - } - - struct Oracle { - uint8 index; // Index of oracle in s_signers/s_transmitters - Role role; // Role of the address which mapped to this struct - } - - mapping(address signerOrTransmitter => Oracle) internal s_oracles; - - // s_signers contains the signing address of each oracle - address[] internal s_signers; - - // s_transmitters contains the transmission address of each oracle, - // i.e. the address the oracle actually sends transactions to the contract from - address[] internal s_transmitters; - - struct DecodedReport { - bytes32[] requestIds; - bytes[] results; - bytes[] errors; - bytes[] onchainMetadata; - bytes[] offchainMetadata; - } - - /* - * Config logic - */ - - // Reverts transaction if config args are invalid - modifier checkConfigValid(uint256 numSigners, uint256 numTransmitters, uint256 f) { - if (numSigners > MAX_NUM_ORACLES) revert InvalidConfig("too many signers"); - if (f == 0) revert InvalidConfig("f must be positive"); - if (numSigners != numTransmitters) revert InvalidConfig("oracle addresses out of registration"); - if (numSigners <= 3 * f) revert InvalidConfig("faulty-oracle f too high"); - _; - } - - // solhint-disable-next-line gas-struct-packing - struct SetConfigArgs { - address[] signers; - address[] transmitters; - uint8 f; - bytes onchainConfig; - uint64 offchainConfigVersion; - bytes offchainConfig; - } - - /// @inheritdoc OCR2Abstract - function latestConfigDigestAndEpoch() - external - view - virtual - override - returns (bool scanLogs, bytes32 configDigest, uint32 epoch) - { - return (true, bytes32(0), uint32(0)); - } - - /** - * @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 encoded on-chain contract configuration - * @param _offchainConfigVersion version number for offchainEncoding schema - * @param _offchainConfig encoded off-chain oracle configuration - */ - function setConfig( - address[] memory _signers, - address[] memory _transmitters, - uint8 _f, - bytes memory _onchainConfig, - uint64 _offchainConfigVersion, - bytes memory _offchainConfig - ) external override checkConfigValid(_signers.length, _transmitters.length, _f) onlyOwner { - SetConfigArgs memory args = SetConfigArgs({ - signers: _signers, - transmitters: _transmitters, - f: _f, - onchainConfig: _onchainConfig, - offchainConfigVersion: _offchainConfigVersion, - offchainConfig: _offchainConfig - }); - - _beforeSetConfig(args.f, args.onchainConfig); - - while (s_signers.length != 0) { - // remove any old signer/transmitter addresses - uint256 lastIdx = s_signers.length - 1; - address signer = s_signers[lastIdx]; - address transmitter = s_transmitters[lastIdx]; - delete s_oracles[signer]; - delete s_oracles[transmitter]; - s_signers.pop(); - s_transmitters.pop(); - } - - // Bounded by MAX_NUM_ORACLES in OCR2Abstract.sol - for (uint256 i = 0; i < args.signers.length; i++) { - if (args.signers[i] == address(0)) revert InvalidConfig("signer must not be empty"); - if (args.transmitters[i] == address(0)) revert InvalidConfig("transmitter must not be empty"); - // add new signer/transmitter addresses - if (s_oracles[args.signers[i]].role != Role.Unset) revert InvalidConfig("repeated signer address"); - s_oracles[args.signers[i]] = Oracle(uint8(i), Role.Signer); - if (s_oracles[args.transmitters[i]].role != Role.Unset) revert InvalidConfig("repeated transmitter address"); - s_oracles[args.transmitters[i]] = Oracle(uint8(i), Role.Transmitter); - s_signers.push(args.signers[i]); - s_transmitters.push(args.transmitters[i]); - } - s_configInfo.f = args.f; - uint32 previousConfigBlockNumber = s_latestConfigBlockNumber; - s_latestConfigBlockNumber = uint32(block.number); - s_configCount += 1; - { - s_configInfo.latestConfigDigest = _configDigestFromConfigData( - block.chainid, - address(this), - s_configCount, - args.signers, - args.transmitters, - args.f, - args.onchainConfig, - args.offchainConfigVersion, - args.offchainConfig - ); - } - s_configInfo.n = uint8(args.signers.length); - - emit ConfigSet( - previousConfigBlockNumber, - s_configInfo.latestConfigDigest, - s_configCount, - args.signers, - args.transmitters, - args.f, - args.onchainConfig, - args.offchainConfigVersion, - args.offchainConfig - ); - } - - function _configDigestFromConfigData( - uint256 _chainId, - address _contractAddress, - uint64 _configCount, - address[] memory _signers, - address[] memory _transmitters, - uint8 _f, - bytes memory _onchainConfig, - uint64 _encodedConfigVersion, - bytes memory _encodedConfig - ) internal pure returns (bytes32) { - uint256 h = uint256( - keccak256( - abi.encode( - _chainId, - _contractAddress, - _configCount, - _signers, - _transmitters, - _f, - _onchainConfig, - _encodedConfigVersion, - _encodedConfig - ) - ) - ); - uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 - uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00 - return bytes32((prefix & prefixMask) | (h & ~prefixMask)); - } - - /** - * @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 - override - returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) - { - return (s_configCount, s_latestConfigBlockNumber, s_configInfo.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 transmitters() external view returns (address[] memory) { - return s_transmitters; - } - - function _beforeSetConfig(uint8 _f, bytes memory _onchainConfig) internal virtual; - - /** - * @dev hook called after the report has been fully validated - * for the extending contract to handle additional logic, such as oracle payment - * @param decodedReport decodedReport - */ - function _report(DecodedReport memory decodedReport) internal virtual; - - // 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 - uint16 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 location start 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 - - 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 = uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) + - report.length + // one byte pure entry in _report - rs.length * - 32 + // 32 bytes per entry in _rs - ss.length * - 32 + // 32 bytes per entry in _ss - 0; // placeholder - if (msg.data.length != expected) revert ReportInvalid("calldata length mismatch"); - } - - function _beforeTransmit( - bytes calldata report - ) internal virtual returns (bool shouldStop, DecodedReport memory decodedReport); - - /** - * @notice transmit is called to post a new report to the contract - * @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 override { - (bool shouldStop, DecodedReport memory decodedReport) = _beforeTransmit(report); - - if (shouldStop) { - return; - } - - { - // reportContext consists of: - // reportContext[0]: ConfigDigest - // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round - // reportContext[2]: ExtraHash - bytes32 configDigest = reportContext[0]; - uint32 epochAndRound = uint32(uint256(reportContext[1])); - - emit Transmitted(configDigest, uint32(epochAndRound >> 8)); - - // The following check is disabled to allow both current and proposed routes to submit reports using the same OCR config digest - // Chainlink Functions uses globally unique request IDs. Metadata about the request is stored and checked in the Coordinator and Router - // require(configInfo.latestConfigDigest == configDigest, "configDigest mismatch"); - - _requireExpectedMsgDataLength(report, rs, ss); - - uint256 expectedNumSignatures = (s_configInfo.n + s_configInfo.f) / 2 + 1; - - if (rs.length != expectedNumSignatures) revert ReportInvalid("wrong number of signatures"); - if (rs.length != ss.length) revert ReportInvalid("report rs and ss must be of equal length"); - - Oracle memory transmitter = s_oracles[msg.sender]; - if (transmitter.role != Role.Transmitter && msg.sender != s_transmitters[transmitter.index]) - revert ReportInvalid("unauthorized transmitter"); - } - - address[MAX_NUM_ORACLES] memory signed; - - { - // Verify signatures attached to report - bytes32 h = keccak256(abi.encodePacked(keccak256(report), reportContext)); - - Oracle memory o; - // Bounded by MAX_NUM_ORACLES in OCR2Abstract.sol - for (uint256 i = 0; i < rs.length; ++i) { - address signer = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); - o = s_oracles[signer]; - if (o.role != Role.Signer) revert ReportInvalid("address not authorized to sign"); - if (signed[o.index] != address(0)) revert ReportInvalid("non-unique signature"); - signed[o.index] = signer; - } - } - - _report(decodedReport); - } -}