More Info
Private Name Tags
ContractCreator
Sponsored
Latest 9 from a total of 9 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Accept Terms Of ... | 126112703 | 7 days ago | IN | 0 ETH | 0.000000858461 | ||||
Accept Terms Of ... | 123813199 | 60 days ago | IN | 0 ETH | 0.000000247079 | ||||
Accept Terms Of ... | 122513991 | 90 days ago | IN | 0 ETH | 0.000005099134 | ||||
Accept Terms Of ... | 122478704 | 91 days ago | IN | 0 ETH | 0.000005451149 | ||||
Accept Terms Of ... | 117772445 | 200 days ago | IN | 0 ETH | 0.000000819852 | ||||
Accept Terms Of ... | 117676129 | 202 days ago | IN | 0 ETH | 0.000001040984 | ||||
Transfer Ownersh... | 117329350 | 210 days ago | IN | 0 ETH | 0.000142341506 | ||||
Accept Terms Of ... | 117297224 | 211 days ago | IN | 0 ETH | 0.000180907405 | ||||
0x60806040 | 117167161 | 214 days ago | IN | 0 ETH | 0.005147467974 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
TermsOfServiceAllowList
Compiler Version
v0.8.19+commit.7dd6d404
Contract Source Code (Solidity Standard Json-Input format)
// 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; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {IFunctionsSubscriptions} from "./interfaces/IFunctionsSubscriptions.sol"; import {AggregatorV3Interface} from "../../../shared/interfaces/AggregatorV3Interface.sol"; import {IFunctionsBilling, FunctionsBillingConfig} from "./interfaces/IFunctionsBilling.sol"; import {Routable} from "./Routable.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; import {ChainSpecificUtil} from "./libraries/ChainSpecificUtil.sol"; /// @title Functions Billing contract /// @notice Contract that calculates payment from users to the nodes of the Decentralized Oracle Network (DON). abstract contract FunctionsBilling is Routable, IFunctionsBilling { using FunctionsResponse for FunctionsResponse.RequestMeta; using FunctionsResponse for FunctionsResponse.Commitment; using FunctionsResponse for FunctionsResponse.FulfillResult; uint256 private constant REASONABLE_GAS_PRICE_CEILING = 1_000_000_000_000_000; // 1 million gwei event RequestBilled( bytes32 indexed requestId, uint96 juelsPerGas, uint256 l1FeeShareWei, uint96 callbackCostJuels, uint72 donFeeJuels, uint72 adminFeeJuels, uint72 operationFeeJuels ); // ================================================================ // | Request Commitment state | // ================================================================ mapping(bytes32 requestId => bytes32 commitmentHash) private s_requestCommitments; event CommitmentDeleted(bytes32 requestId); FunctionsBillingConfig private s_config; event ConfigUpdated(FunctionsBillingConfig config); error UnsupportedRequestDataVersion(); error InsufficientBalance(); error InvalidSubscription(); error UnauthorizedSender(); error MustBeSubOwner(address owner); error InvalidLinkWeiPrice(int256 linkWei); error InvalidUsdLinkPrice(int256 usdLink); error PaymentTooLarge(); error NoTransmittersSet(); error InvalidCalldata(); // ================================================================ // | Balance state | // ================================================================ mapping(address transmitter => uint96 balanceJuelsLink) private s_withdrawableTokens; // Pool together collected DON fees // Disperse them on withdrawal or change in OCR configuration uint96 internal s_feePool; AggregatorV3Interface private s_linkToNativeFeed; AggregatorV3Interface private s_linkToUsdFeed; // ================================================================ // | Initialization | // ================================================================ constructor( address router, FunctionsBillingConfig memory config, address linkToNativeFeed, address linkToUsdFeed ) Routable(router) { s_linkToNativeFeed = AggregatorV3Interface(linkToNativeFeed); s_linkToUsdFeed = AggregatorV3Interface(linkToUsdFeed); updateConfig(config); } // ================================================================ // | Configuration | // ================================================================ /// @notice Gets the Chainlink Coordinator's billing configuration /// @return config function getConfig() external view returns (FunctionsBillingConfig memory) { return s_config; } /// @notice Sets the Chainlink Coordinator's billing configuration /// @param config - See the contents of the FunctionsBillingConfig struct in IFunctionsBilling.sol for more information function updateConfig(FunctionsBillingConfig memory config) public { _onlyOwner(); s_config = config; emit ConfigUpdated(config); } // ================================================================ // | Fee Calculation | // ================================================================ /// @inheritdoc IFunctionsBilling function getDONFeeJuels(bytes memory /* requestData */) public view override returns (uint72) { // s_config.donFee is in cents of USD. Get Juel amount then convert to dollars. return SafeCast.toUint72(_getJuelsFromUsd(s_config.donFeeCentsUsd) / 100); } /// @inheritdoc IFunctionsBilling function getOperationFeeJuels() public view override returns (uint72) { // s_config.donFee is in cents of USD. Get Juel amount then convert to dollars. return SafeCast.toUint72(_getJuelsFromUsd(s_config.operationFeeCentsUsd) / 100); } /// @inheritdoc IFunctionsBilling function getAdminFeeJuels() public view override returns (uint72) { return _getRouter().getAdminFee(); } /// @inheritdoc IFunctionsBilling function getWeiPerUnitLink() public view returns (uint256) { (, int256 weiPerUnitLink, , uint256 timestamp, ) = s_linkToNativeFeed.latestRoundData(); // solhint-disable-next-line not-rely-on-time if (s_config.feedStalenessSeconds < block.timestamp - timestamp && s_config.feedStalenessSeconds > 0) { return s_config.fallbackNativePerUnitLink; } if (weiPerUnitLink <= 0) { revert InvalidLinkWeiPrice(weiPerUnitLink); } return uint256(weiPerUnitLink); } function _getJuelsFromWei(uint256 amountWei) private view returns (uint96) { // (1e18 juels/link) * wei / (wei/link) = juels // There are only 1e9*1e18 = 1e27 juels in existence, should not exceed uint96 (2^96 ~ 7e28) return SafeCast.toUint96((1e18 * amountWei) / getWeiPerUnitLink()); } /// @inheritdoc IFunctionsBilling function getUsdPerUnitLink() public view returns (uint256, uint8) { (, int256 usdPerUnitLink, , uint256 timestamp, ) = s_linkToUsdFeed.latestRoundData(); // solhint-disable-next-line not-rely-on-time if (s_config.feedStalenessSeconds < block.timestamp - timestamp && s_config.feedStalenessSeconds > 0) { return (s_config.fallbackUsdPerUnitLink, s_config.fallbackUsdPerUnitLinkDecimals); } if (usdPerUnitLink <= 0) { revert InvalidUsdLinkPrice(usdPerUnitLink); } return (uint256(usdPerUnitLink), s_linkToUsdFeed.decimals()); } function _getJuelsFromUsd(uint256 amountUsd) private view returns (uint96) { (uint256 usdPerLink, uint8 decimals) = getUsdPerUnitLink(); // (usd) * (10**18 juels/link) * (10**decimals) / (link / usd) = juels // There are only 1e9*1e18 = 1e27 juels in existence, should not exceed uint96 (2^96 ~ 7e28) return SafeCast.toUint96((amountUsd * 10 ** (18 + decimals)) / usdPerLink); } // ================================================================ // | Cost Estimation | // ================================================================ /// @inheritdoc IFunctionsBilling function estimateCost( uint64 subscriptionId, bytes calldata data, uint32 callbackGasLimit, uint256 gasPriceWei ) external view override returns (uint96) { _getRouter().isValidCallbackGasLimit(subscriptionId, callbackGasLimit); // Reasonable ceilings to prevent integer overflows if (gasPriceWei > REASONABLE_GAS_PRICE_CEILING) { revert InvalidCalldata(); } uint72 adminFee = getAdminFeeJuels(); uint72 donFee = getDONFeeJuels(data); uint72 operationFee = getOperationFeeJuels(); return _calculateCostEstimate(callbackGasLimit, gasPriceWei, donFee, adminFee, operationFee); } /// @notice Estimate the cost in Juels of LINK // that will be charged to a subscription to fulfill a Functions request // Gas Price can be overestimated to account for flucuations between request and response time function _calculateCostEstimate( uint32 callbackGasLimit, uint256 gasPriceWei, uint72 donFeeJuels, uint72 adminFeeJuels, uint72 operationFeeJuels ) internal view returns (uint96) { // If gas price is less than the minimum fulfillment gas price, override to using the minimum if (gasPriceWei < s_config.minimumEstimateGasPriceWei) { gasPriceWei = s_config.minimumEstimateGasPriceWei; } uint256 gasPriceWithOverestimation = gasPriceWei + ((gasPriceWei * s_config.fulfillmentGasPriceOverEstimationBP) / 10_000); /// @NOTE: Basis Points are 1/100th of 1%, divide by 10_000 to bring back to original units uint256 executionGas = s_config.gasOverheadBeforeCallback + s_config.gasOverheadAfterCallback + callbackGasLimit; uint256 l1FeeWei = ChainSpecificUtil._getCurrentTxL1GasFees(msg.data); uint96 estimatedGasReimbursementJuels = _getJuelsFromWei((gasPriceWithOverestimation * executionGas) + l1FeeWei); uint96 feesJuels = uint96(donFeeJuels) + uint96(adminFeeJuels) + uint96(operationFeeJuels); return estimatedGasReimbursementJuels + feesJuels; } // ================================================================ // | Billing | // ================================================================ /// @notice Initiate the billing process for an Functions request /// @dev Only callable by the Functions Router /// @param request - Chainlink Functions request data, see FunctionsResponse.RequestMeta for the structure /// @return commitment - The parameters of the request that must be held consistent at response time function _startBilling( FunctionsResponse.RequestMeta memory request ) internal returns (FunctionsResponse.Commitment memory commitment, uint72 operationFee) { // Nodes should support all past versions of the structure if (request.dataVersion > s_config.maxSupportedRequestDataVersion) { revert UnsupportedRequestDataVersion(); } uint72 donFee = getDONFeeJuels(request.data); operationFee = getOperationFeeJuels(); uint96 estimatedTotalCostJuels = _calculateCostEstimate( request.callbackGasLimit, tx.gasprice, donFee, request.adminFee, operationFee ); // Check that subscription can afford the estimated cost if ((request.availableBalance) < estimatedTotalCostJuels) { revert InsufficientBalance(); } uint32 timeoutTimestamp = uint32(block.timestamp + s_config.requestTimeoutSeconds); bytes32 requestId = keccak256( abi.encode( address(this), request.requestingContract, request.subscriptionId, request.initiatedRequests + 1, keccak256(request.data), request.dataVersion, request.callbackGasLimit, estimatedTotalCostJuels, timeoutTimestamp, // solhint-disable-next-line avoid-tx-origin tx.origin ) ); commitment = FunctionsResponse.Commitment({ adminFee: request.adminFee, coordinator: address(this), client: request.requestingContract, subscriptionId: request.subscriptionId, callbackGasLimit: request.callbackGasLimit, estimatedTotalCostJuels: estimatedTotalCostJuels, timeoutTimestamp: timeoutTimestamp, requestId: requestId, donFee: donFee, gasOverheadBeforeCallback: s_config.gasOverheadBeforeCallback, gasOverheadAfterCallback: s_config.gasOverheadAfterCallback }); s_requestCommitments[requestId] = keccak256(abi.encode(commitment)); return (commitment, operationFee); } /// @notice Finalize billing process for an Functions request by sending a callback to the Client contract and then charging the subscription /// @param requestId identifier for the request that was generated by the Registry in the beginBilling commitment /// @param response response data from DON consensus /// @param err error from DON consensus /// @param reportBatchSize the number of fulfillments in the transmitter's report /// @return result fulfillment result /// @dev Only callable by a node that has been approved on the Coordinator /// @dev simulated offchain to determine if sufficient balance is present to fulfill the request function _fulfillAndBill( bytes32 requestId, bytes memory response, bytes memory err, bytes memory onchainMetadata, bytes memory /* offchainMetadata TODO: use in getDonFee() for dynamic billing */, uint8 reportBatchSize ) internal returns (FunctionsResponse.FulfillResult) { FunctionsResponse.Commitment memory commitment = abi.decode(onchainMetadata, (FunctionsResponse.Commitment)); uint256 gasOverheadWei = (commitment.gasOverheadBeforeCallback + commitment.gasOverheadAfterCallback) * tx.gasprice; uint256 l1FeeShareWei = ChainSpecificUtil._getCurrentTxL1GasFees(msg.data) / reportBatchSize; // Gas overhead without callback uint96 gasOverheadJuels = _getJuelsFromWei(gasOverheadWei + l1FeeShareWei); uint96 juelsPerGas = _getJuelsFromWei(tx.gasprice); // The Functions Router will perform the callback to the client contract (FunctionsResponse.FulfillResult resultCode, uint96 callbackCostJuels) = _getRouter().fulfill( response, err, juelsPerGas, // The following line represents: "cost without callback or admin fee, those will be added by the Router" // But because the _offchain_ Commitment is using operation fee in the place of the admin fee, this now adds admin fee (actually operation fee) // Admin fee is configured to 0 in the Router gasOverheadJuels + commitment.donFee + commitment.adminFee, msg.sender, FunctionsResponse.Commitment({ adminFee: 0, // The Router should have adminFee set to 0. If it does not this will cause fulfillments to fail with INVALID_COMMITMENT instead of carrying out incorrect bookkeeping. coordinator: commitment.coordinator, client: commitment.client, subscriptionId: commitment.subscriptionId, callbackGasLimit: commitment.callbackGasLimit, estimatedTotalCostJuels: commitment.estimatedTotalCostJuels, timeoutTimestamp: commitment.timeoutTimestamp, requestId: commitment.requestId, donFee: commitment.donFee, gasOverheadBeforeCallback: commitment.gasOverheadBeforeCallback, gasOverheadAfterCallback: commitment.gasOverheadAfterCallback }) ); // The router will only pay the DON on successfully processing the fulfillment // In these two fulfillment results the user has been charged // Otherwise, the Coordinator should hold on to the request commitment if ( resultCode == FunctionsResponse.FulfillResult.FULFILLED || resultCode == FunctionsResponse.FulfillResult.USER_CALLBACK_ERROR ) { delete s_requestCommitments[requestId]; // Reimburse the transmitter for the fulfillment gas cost s_withdrawableTokens[msg.sender] += gasOverheadJuels + callbackCostJuels; // Put donFee into the pool of fees, to be split later // Saves on storage writes that would otherwise be charged to the user s_feePool += commitment.donFee; // Pay the operation fee to the Coordinator owner s_withdrawableTokens[_owner()] += commitment.adminFee; // OperationFee is used in the slot for Admin Fee in the Offchain Commitment. Admin Fee is set to 0 in the Router (enforced by line 316 in FunctionsBilling.sol). emit RequestBilled({ requestId: requestId, juelsPerGas: juelsPerGas, l1FeeShareWei: l1FeeShareWei, callbackCostJuels: callbackCostJuels, donFeeJuels: commitment.donFee, // The following two lines are because of OperationFee being used in the Offchain Commitment adminFeeJuels: 0, operationFeeJuels: commitment.adminFee }); } return resultCode; } // ================================================================ // | Request Timeout | // ================================================================ /// @inheritdoc IFunctionsBilling /// @dev Only callable by the Router /// @dev Used by FunctionsRouter.sol during timeout of a request function deleteCommitment(bytes32 requestId) external override onlyRouter { // Delete commitment delete s_requestCommitments[requestId]; emit CommitmentDeleted(requestId); } // ================================================================ // | Fund withdrawal | // ================================================================ /// @inheritdoc IFunctionsBilling function oracleWithdraw(address recipient, uint96 amount) external { _disperseFeePool(); if (amount == 0) { amount = s_withdrawableTokens[msg.sender]; } else if (s_withdrawableTokens[msg.sender] < amount) { revert InsufficientBalance(); } s_withdrawableTokens[msg.sender] -= amount; IFunctionsSubscriptions(address(_getRouter())).oracleWithdraw(recipient, amount); } /// @inheritdoc IFunctionsBilling /// @dev Only callable by the Coordinator owner function oracleWithdrawAll() external { _onlyOwner(); _disperseFeePool(); address[] memory transmitters = _getTransmitters(); // Bounded by "maxNumOracles" on OCR2Abstract.sol for (uint256 i = 0; i < transmitters.length; ++i) { uint96 balance = s_withdrawableTokens[transmitters[i]]; if (balance > 0) { s_withdrawableTokens[transmitters[i]] = 0; IFunctionsSubscriptions(address(_getRouter())).oracleWithdraw(transmitters[i], balance); } } } // Overriden in FunctionsCoordinator, which has visibility into transmitters function _getTransmitters() internal view virtual returns (address[] memory); // DON fees are collected into a pool s_feePool // When OCR configuration changes, or any oracle withdraws, this must be dispersed function _disperseFeePool() internal { if (s_feePool == 0) { return; } // All transmitters are assumed to also be observers // Pay out the DON fee to all transmitters address[] memory transmitters = _getTransmitters(); uint256 numberOfTransmitters = transmitters.length; if (numberOfTransmitters == 0) { revert NoTransmittersSet(); } uint96 feePoolShare = s_feePool / uint96(numberOfTransmitters); // Bounded by "maxNumOracles" on OCR2Abstract.sol for (uint256 i = 0; i < numberOfTransmitters; ++i) { s_withdrawableTokens[transmitters[i]] += feePoolShare; } s_feePool -= feePoolShare * uint96(numberOfTransmitters); } // Overriden in FunctionsCoordinator.sol function _onlyOwner() internal view virtual; // Used in FunctionsCoordinator.sol function _isExistingRequest(bytes32 requestId) internal view returns (bool) { return s_requestCommitments[requestId] != bytes32(0); } // Overriden in FunctionsCoordinator.sol function _owner() internal view virtual returns (address owner); }
// 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); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {IFunctionsCoordinator} from "./interfaces/IFunctionsCoordinator.sol"; import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; import {FunctionsBilling, FunctionsBillingConfig} from "./FunctionsBilling.sol"; import {OCR2Base} from "./ocr/OCR2Base.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; /// @title Functions Coordinator contract /// @notice Contract that nodes of a Decentralized Oracle Network (DON) interact with contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilling { using FunctionsResponse for FunctionsResponse.RequestMeta; using FunctionsResponse for FunctionsResponse.Commitment; using FunctionsResponse for FunctionsResponse.FulfillResult; /// @inheritdoc ITypeAndVersion // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables string public constant override typeAndVersion = "Functions Coordinator v1.3.0"; event OracleRequest( bytes32 indexed requestId, address indexed requestingContract, address requestInitiator, uint64 subscriptionId, address subscriptionOwner, bytes data, uint16 dataVersion, bytes32 flags, uint64 callbackGasLimit, FunctionsResponse.Commitment commitment ); event OracleResponse(bytes32 indexed requestId, address transmitter); error InconsistentReportData(); error EmptyPublicKey(); error UnauthorizedPublicKeyChange(); bytes private s_donPublicKey; bytes private s_thresholdPublicKey; constructor( address router, FunctionsBillingConfig memory config, address linkToNativeFeed, address linkToUsdFeed ) OCR2Base() FunctionsBilling(router, config, linkToNativeFeed, linkToUsdFeed) {} /// @inheritdoc IFunctionsCoordinator function getThresholdPublicKey() external view override returns (bytes memory) { if (s_thresholdPublicKey.length == 0) { revert EmptyPublicKey(); } return s_thresholdPublicKey; } /// @inheritdoc IFunctionsCoordinator function setThresholdPublicKey(bytes calldata thresholdPublicKey) external override onlyOwner { if (thresholdPublicKey.length == 0) { revert EmptyPublicKey(); } s_thresholdPublicKey = thresholdPublicKey; } /// @inheritdoc IFunctionsCoordinator function getDONPublicKey() external view override returns (bytes memory) { if (s_donPublicKey.length == 0) { revert EmptyPublicKey(); } return s_donPublicKey; } /// @inheritdoc IFunctionsCoordinator function setDONPublicKey(bytes calldata donPublicKey) external override onlyOwner { if (donPublicKey.length == 0) { revert EmptyPublicKey(); } s_donPublicKey = donPublicKey; } /// @dev check if node is in current transmitter list function _isTransmitter(address node) internal view returns (bool) { // Bounded by "maxNumOracles" on OCR2Abstract.sol for (uint256 i = 0; i < s_transmitters.length; ++i) { if (s_transmitters[i] == node) { return true; } } return false; } /// @inheritdoc IFunctionsCoordinator function startRequest( FunctionsResponse.RequestMeta calldata request ) external override onlyRouter returns (FunctionsResponse.Commitment memory commitment) { uint72 operationFee; (commitment, operationFee) = _startBilling(request); emit OracleRequest( commitment.requestId, request.requestingContract, // solhint-disable-next-line avoid-tx-origin tx.origin, request.subscriptionId, request.subscriptionOwner, request.data, request.dataVersion, request.flags, request.callbackGasLimit, FunctionsResponse.Commitment({ coordinator: commitment.coordinator, client: commitment.client, subscriptionId: commitment.subscriptionId, callbackGasLimit: commitment.callbackGasLimit, estimatedTotalCostJuels: commitment.estimatedTotalCostJuels, timeoutTimestamp: commitment.timeoutTimestamp, requestId: commitment.requestId, donFee: commitment.donFee, gasOverheadBeforeCallback: commitment.gasOverheadBeforeCallback, gasOverheadAfterCallback: commitment.gasOverheadAfterCallback, // The following line is done to use the Coordinator's operationFee in place of the Router's operation fee // With this in place the Router.adminFee must be set to 0 in the Router. adminFee: operationFee }) ); return commitment; } /// @dev DON fees are pooled together. If the OCR configuration is going to change, these need to be distributed. function _beforeSetConfig(uint8 /* _f */, bytes memory /* _onchainConfig */) internal override { if (_getTransmitters().length > 0) { _disperseFeePool(); } } /// @dev Used by FunctionsBilling.sol function _getTransmitters() internal view override returns (address[] memory) { return s_transmitters; } function _beforeTransmit( bytes calldata report ) internal view override returns (bool shouldStop, DecodedReport memory decodedReport) { ( bytes32[] memory requestIds, bytes[] memory results, bytes[] memory errors, bytes[] memory onchainMetadata, bytes[] memory offchainMetadata ) = abi.decode(report, (bytes32[], bytes[], bytes[], bytes[], bytes[])); uint256 numberOfFulfillments = uint8(requestIds.length); if ( numberOfFulfillments == 0 || numberOfFulfillments != results.length || numberOfFulfillments != errors.length || numberOfFulfillments != onchainMetadata.length || numberOfFulfillments != offchainMetadata.length ) { revert ReportInvalid("Fields must be equal length"); } for (uint256 i = 0; i < numberOfFulfillments; ++i) { if (_isExistingRequest(requestIds[i])) { // If there is an existing request, validate report // Leave shouldStop to default, false break; } if (i == numberOfFulfillments - 1) { // If the last fulfillment on the report does not exist, then all are duplicates // Indicate that it's safe to stop to save on the gas of validating the report shouldStop = true; } } return ( shouldStop, DecodedReport({ requestIds: requestIds, results: results, errors: errors, onchainMetadata: onchainMetadata, offchainMetadata: offchainMetadata }) ); } /// @dev Report hook called within OCR2Base.sol function _report(DecodedReport memory decodedReport) internal override { uint256 numberOfFulfillments = uint8(decodedReport.requestIds.length); // Bounded by "MaxRequestBatchSize" on the Job's ReportingPluginConfig for (uint256 i = 0; i < numberOfFulfillments; ++i) { FunctionsResponse.FulfillResult result = FunctionsResponse.FulfillResult( _fulfillAndBill( decodedReport.requestIds[i], decodedReport.results[i], decodedReport.errors[i], decodedReport.onchainMetadata[i], decodedReport.offchainMetadata[i], uint8(numberOfFulfillments) // will not exceed "MaxRequestBatchSize" on the Job's ReportingPluginConfig ) ); // Emit on successfully processing the fulfillment // In these two fulfillment results the user has been charged // Otherwise, the DON will re-try if ( result == FunctionsResponse.FulfillResult.FULFILLED || result == FunctionsResponse.FulfillResult.USER_CALLBACK_ERROR ) { emit OracleResponse(decodedReport.requestIds[i], msg.sender); } } } /// @dev Used in FunctionsBilling.sol function _onlyOwner() internal view override { _validateOwnership(); } /// @dev Used in FunctionsBilling.sol function _owner() internal view override returns (address owner) { return this.owner(); } }
// SPDX-License-Identifier: MIT 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 {IAccessController} from "../../../shared/interfaces/IAccessController.sol"; import {FunctionsSubscriptions} from "./FunctionsSubscriptions.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol"; import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; import {Pausable} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol"; contract FunctionsRouter is IFunctionsRouter, FunctionsSubscriptions, Pausable, ITypeAndVersion, ConfirmedOwner { using FunctionsResponse for FunctionsResponse.RequestMeta; using FunctionsResponse for FunctionsResponse.Commitment; using FunctionsResponse for FunctionsResponse.FulfillResult; // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables string public constant override typeAndVersion = "Functions Router v1.0.0"; // We limit return data to a selector plus 4 words. This is to avoid // malicious contracts from returning large amounts of data and causing // repeated out-of-gas scenarios. uint16 public constant MAX_CALLBACK_RETURN_BYTES = 4 + 4 * 32; uint8 private constant MAX_CALLBACK_GAS_LIMIT_FLAGS_INDEX = 0; event RequestStart( bytes32 indexed requestId, bytes32 indexed donId, uint64 indexed subscriptionId, address subscriptionOwner, address requestingContract, address requestInitiator, bytes data, uint16 dataVersion, uint32 callbackGasLimit, uint96 estimatedTotalCostJuels ); event RequestProcessed( bytes32 indexed requestId, uint64 indexed subscriptionId, uint96 totalCostJuels, address transmitter, FunctionsResponse.FulfillResult resultCode, bytes response, bytes err, bytes callbackReturnData ); event RequestNotProcessed( bytes32 indexed requestId, address coordinator, address transmitter, FunctionsResponse.FulfillResult resultCode ); error EmptyRequestData(); error OnlyCallableFromCoordinator(); error SenderMustAcceptTermsOfService(address sender); error InvalidGasFlagValue(uint8 value); error GasLimitTooBig(uint32 limit); error DuplicateRequestId(bytes32 requestId); struct CallbackResult { bool success; // ══════╸ Whether the callback succeeded or not uint256 gasUsed; // ═══╸ The amount of gas consumed during the callback bytes returnData; // ══╸ The return of the callback function } // ================================================================ // | Route state | // ================================================================ mapping(bytes32 id => address routableContract) private s_route; error RouteNotFound(bytes32 id); // Identifier for the route to the Terms of Service Allow List bytes32 private s_allowListId; // ================================================================ // | Configuration state | // ================================================================ struct Config { uint16 maxConsumersPerSubscription; // ═════════╗ Maximum number of consumers which can be added to a single subscription. This bound ensures we are able to loop over all subscription consumers as needed, without exceeding gas limits. Should a user require more consumers, they can use multiple subscriptions. uint72 adminFee; // ║ Flat fee (in Juels of LINK) that will be paid to the Router owner for operation of the network bytes4 handleOracleFulfillmentSelector; // ║ The function selector that is used when calling back to the Client contract uint16 gasForCallExactCheck; // ════════════════╝ Used during calling back to the client. Ensures we have at least enough gas to be able to revert if gasAmount > 63//64*gas available. uint32[] maxCallbackGasLimits; // ══════════════╸ List of max callback gas limits used by flag with MAX_CALLBACK_GAS_LIMIT_FLAGS_INDEX uint16 subscriptionDepositMinimumRequests; //═══╗ Amount of requests that must be completed before the full subscription balance will be released when closing a subscription account. uint72 subscriptionDepositJuels; // ════════════╝ Amount of subscription funds that are held as a deposit until Config.subscriptionDepositMinimumRequests are made using the subscription. } Config private s_config; event ConfigUpdated(Config); // ================================================================ // | Proposal state | // ================================================================ uint8 private constant MAX_PROPOSAL_SET_LENGTH = 8; struct ContractProposalSet { bytes32[] ids; // ══╸ The IDs that key into the routes that will be modified if the update is applied address[] to; // ═══╸ The address of the contracts that the route will point to if the updated is applied } ContractProposalSet private s_proposedContractSet; event ContractProposed( bytes32 proposedContractSetId, address proposedContractSetFromAddress, address proposedContractSetToAddress ); event ContractUpdated(bytes32 id, address from, address to); error InvalidProposal(); error IdentifierIsReserved(bytes32 id); // ================================================================ // | Initialization | // ================================================================ constructor( address linkToken, Config memory config ) FunctionsSubscriptions(linkToken) ConfirmedOwner(msg.sender) Pausable() { // Set the intial configuration updateConfig(config); } // ================================================================ // | Configuration | // ================================================================ /// @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 getConfig() external view returns (Config memory) { return s_config; } /// @notice The router configuration function updateConfig(Config memory config) public onlyOwner { s_config = config; emit ConfigUpdated(config); } /// @inheritdoc IFunctionsRouter function isValidCallbackGasLimit(uint64 subscriptionId, uint32 callbackGasLimit) public view { uint8 callbackGasLimitsIndexSelector = uint8(getFlags(subscriptionId)[MAX_CALLBACK_GAS_LIMIT_FLAGS_INDEX]); if (callbackGasLimitsIndexSelector >= s_config.maxCallbackGasLimits.length) { revert InvalidGasFlagValue(callbackGasLimitsIndexSelector); } uint32 maxCallbackGasLimit = s_config.maxCallbackGasLimits[callbackGasLimitsIndexSelector]; if (callbackGasLimit > maxCallbackGasLimit) { revert GasLimitTooBig(maxCallbackGasLimit); } } /// @inheritdoc IFunctionsRouter function getAdminFee() external view override returns (uint72) { return s_config.adminFee; } /// @inheritdoc IFunctionsRouter function getAllowListId() external view override returns (bytes32) { return s_allowListId; } /// @inheritdoc IFunctionsRouter function setAllowListId(bytes32 allowListId) external override onlyOwner { s_allowListId = allowListId; } /// @dev Used within FunctionsSubscriptions.sol function _getMaxConsumers() internal view override returns (uint16) { return s_config.maxConsumersPerSubscription; } /// @dev Used within FunctionsSubscriptions.sol function _getSubscriptionDepositDetails() internal view override returns (uint16, uint72) { return (s_config.subscriptionDepositMinimumRequests, s_config.subscriptionDepositJuels); } // ================================================================ // | Requests | // ================================================================ /// @inheritdoc IFunctionsRouter function sendRequest( uint64 subscriptionId, bytes calldata data, uint16 dataVersion, uint32 callbackGasLimit, bytes32 donId ) external override returns (bytes32) { IFunctionsCoordinator coordinator = IFunctionsCoordinator(getContractById(donId)); return _sendRequest(donId, coordinator, subscriptionId, data, dataVersion, callbackGasLimit); } /// @inheritdoc IFunctionsRouter function sendRequestToProposed( uint64 subscriptionId, bytes calldata data, uint16 dataVersion, uint32 callbackGasLimit, bytes32 donId ) external override returns (bytes32) { IFunctionsCoordinator coordinator = IFunctionsCoordinator(getProposedContractById(donId)); return _sendRequest(donId, coordinator, subscriptionId, data, dataVersion, callbackGasLimit); } function _sendRequest( bytes32 donId, IFunctionsCoordinator coordinator, uint64 subscriptionId, bytes memory data, uint16 dataVersion, uint32 callbackGasLimit ) private returns (bytes32) { _whenNotPaused(); _isExistingSubscription(subscriptionId); _isAllowedConsumer(msg.sender, subscriptionId); isValidCallbackGasLimit(subscriptionId, callbackGasLimit); if (data.length == 0) { revert EmptyRequestData(); } Subscription memory subscription = getSubscription(subscriptionId); Consumer memory consumer = getConsumer(msg.sender, subscriptionId); uint72 adminFee = s_config.adminFee; // Forward request to DON FunctionsResponse.Commitment memory commitment = coordinator.startRequest( FunctionsResponse.RequestMeta({ requestingContract: msg.sender, data: data, subscriptionId: subscriptionId, dataVersion: dataVersion, flags: getFlags(subscriptionId), callbackGasLimit: callbackGasLimit, adminFee: adminFee, initiatedRequests: consumer.initiatedRequests, completedRequests: consumer.completedRequests, availableBalance: subscription.balance - subscription.blockedBalance, subscriptionOwner: subscription.owner }) ); // Do not allow setting a comittment for a requestId that already exists if (s_requestCommitments[commitment.requestId] != bytes32(0)) { revert DuplicateRequestId(commitment.requestId); } // Store a commitment about the request s_requestCommitments[commitment.requestId] = keccak256( abi.encode( FunctionsResponse.Commitment({ adminFee: adminFee, coordinator: address(coordinator), client: msg.sender, subscriptionId: subscriptionId, callbackGasLimit: callbackGasLimit, estimatedTotalCostJuels: commitment.estimatedTotalCostJuels, timeoutTimestamp: commitment.timeoutTimestamp, requestId: commitment.requestId, donFee: commitment.donFee, gasOverheadBeforeCallback: commitment.gasOverheadBeforeCallback, gasOverheadAfterCallback: commitment.gasOverheadAfterCallback }) ) ); _markRequestInFlight(msg.sender, subscriptionId, commitment.estimatedTotalCostJuels); emit RequestStart({ requestId: commitment.requestId, donId: donId, subscriptionId: subscriptionId, subscriptionOwner: subscription.owner, requestingContract: msg.sender, // solhint-disable-next-line avoid-tx-origin requestInitiator: tx.origin, data: data, dataVersion: dataVersion, callbackGasLimit: callbackGasLimit, estimatedTotalCostJuels: commitment.estimatedTotalCostJuels }); return commitment.requestId; } // ================================================================ // | Responses | // ================================================================ /// @inheritdoc IFunctionsRouter function fulfill( bytes memory response, bytes memory err, uint96 juelsPerGas, uint96 costWithoutFulfillment, address transmitter, FunctionsResponse.Commitment memory commitment ) external override returns (FunctionsResponse.FulfillResult resultCode, uint96) { _whenNotPaused(); if (msg.sender != commitment.coordinator) { revert OnlyCallableFromCoordinator(); } { bytes32 commitmentHash = s_requestCommitments[commitment.requestId]; if (commitmentHash == bytes32(0)) { resultCode = FunctionsResponse.FulfillResult.INVALID_REQUEST_ID; emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode); return (resultCode, 0); } if (keccak256(abi.encode(commitment)) != commitmentHash) { resultCode = FunctionsResponse.FulfillResult.INVALID_COMMITMENT; emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode); return (resultCode, 0); } // Check that the transmitter has supplied enough gas for the callback to succeed if (gasleft() < commitment.callbackGasLimit + commitment.gasOverheadAfterCallback) { resultCode = FunctionsResponse.FulfillResult.INSUFFICIENT_GAS_PROVIDED; emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode); return (resultCode, 0); } } { uint96 callbackCost = juelsPerGas * SafeCast.toUint96(commitment.callbackGasLimit); uint96 totalCostJuels = commitment.adminFee + costWithoutFulfillment + callbackCost; // Check that the subscription can still afford to fulfill the request if (totalCostJuels > getSubscription(commitment.subscriptionId).balance) { resultCode = FunctionsResponse.FulfillResult.SUBSCRIPTION_BALANCE_INVARIANT_VIOLATION; emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode); return (resultCode, 0); } // Check that the cost has not exceeded the quoted cost if (totalCostJuels > commitment.estimatedTotalCostJuels) { resultCode = FunctionsResponse.FulfillResult.COST_EXCEEDS_COMMITMENT; emit RequestNotProcessed(commitment.requestId, commitment.coordinator, transmitter, resultCode); return (resultCode, 0); } } delete s_requestCommitments[commitment.requestId]; CallbackResult memory result = _callback( commitment.requestId, response, err, commitment.callbackGasLimit, commitment.client ); resultCode = result.success ? FunctionsResponse.FulfillResult.FULFILLED : FunctionsResponse.FulfillResult.USER_CALLBACK_ERROR; Receipt memory receipt = _pay( commitment.subscriptionId, commitment.estimatedTotalCostJuels, commitment.client, commitment.adminFee, juelsPerGas, SafeCast.toUint96(result.gasUsed), costWithoutFulfillment ); emit RequestProcessed({ requestId: commitment.requestId, subscriptionId: commitment.subscriptionId, totalCostJuels: receipt.totalCostJuels, transmitter: transmitter, resultCode: resultCode, response: response, err: err, callbackReturnData: result.returnData }); return (resultCode, receipt.callbackGasCostJuels); } function _callback( bytes32 requestId, bytes memory response, bytes memory err, uint32 callbackGasLimit, address client ) private returns (CallbackResult memory) { bool destinationNoLongerExists; assembly { // solidity calls check that a contract actually exists at the destination, so we do the same destinationNoLongerExists := iszero(extcodesize(client)) } if (destinationNoLongerExists) { // Return without attempting callback // The subscription will still be charged to reimburse transmitter's gas overhead return CallbackResult({success: false, gasUsed: 0, returnData: new bytes(0)}); } bytes memory encodedCallback = abi.encodeWithSelector( s_config.handleOracleFulfillmentSelector, requestId, response, err ); uint16 gasForCallExactCheck = s_config.gasForCallExactCheck; // Call with explicitly the amount of callback gas requested // Important to not let them exhaust the gas budget and avoid payment. // NOTE: that callWithExactGas will revert if we do not have sufficient gas // to give the callee their requested amount. bool success; uint256 gasUsed; // allocate return data memory ahead of time bytes memory returnData = new bytes(MAX_CALLBACK_RETURN_BYTES); assembly { let g := gas() // Compute g -= gasForCallExactCheck and check for underflow // The gas actually passed to the callee is _min(gasAmount, 63//64*gas available). // We want to ensure that we revert if gasAmount > 63//64*gas available // as we do not want to provide them with less, however that check itself costs // gas. gasForCallExactCheck ensures we have at least enough gas to be able // to revert if gasAmount > 63//64*gas available. if lt(g, gasForCallExactCheck) { revert(0, 0) } g := sub(g, gasForCallExactCheck) // if g - g//64 <= gasAmount, revert // (we subtract g//64 because of EIP-150) if iszero(gt(sub(g, div(g, 64)), callbackGasLimit)) { revert(0, 0) } // call and report whether we succeeded // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) let gasBeforeCall := gas() success := call(callbackGasLimit, client, 0, add(encodedCallback, 0x20), mload(encodedCallback), 0, 0) gasUsed := sub(gasBeforeCall, gas()) // limit our copy to MAX_CALLBACK_RETURN_BYTES bytes let toCopy := returndatasize() if gt(toCopy, MAX_CALLBACK_RETURN_BYTES) { toCopy := MAX_CALLBACK_RETURN_BYTES } // Store the length of the copied bytes mstore(returnData, toCopy) // copy the bytes from returnData[0:_toCopy] returndatacopy(add(returnData, 0x20), 0, toCopy) } return CallbackResult({success: success, gasUsed: gasUsed, returnData: returnData}); } // ================================================================ // | Route methods | // ================================================================ /// @inheritdoc IFunctionsRouter function getContractById(bytes32 id) public view override returns (address) { address currentImplementation = s_route[id]; if (currentImplementation == address(0)) { revert RouteNotFound(id); } return currentImplementation; } /// @inheritdoc IFunctionsRouter function getProposedContractById(bytes32 id) public view override returns (address) { // Iterations will not exceed MAX_PROPOSAL_SET_LENGTH for (uint8 i = 0; i < s_proposedContractSet.ids.length; ++i) { if (id == s_proposedContractSet.ids[i]) { return s_proposedContractSet.to[i]; } } revert RouteNotFound(id); } // ================================================================ // | Contract Proposal methods | // ================================================================ /// @inheritdoc IFunctionsRouter function getProposedContractSet() external view override returns (bytes32[] memory, address[] memory) { return (s_proposedContractSet.ids, s_proposedContractSet.to); } /// @inheritdoc IFunctionsRouter function proposeContractsUpdate( bytes32[] memory proposedContractSetIds, address[] memory proposedContractSetAddresses ) external override onlyOwner { // IDs and addresses arrays must be of equal length and must not exceed the max proposal length uint256 idsArrayLength = proposedContractSetIds.length; if (idsArrayLength != proposedContractSetAddresses.length || idsArrayLength > MAX_PROPOSAL_SET_LENGTH) { revert InvalidProposal(); } // NOTE: iterations of this loop will not exceed MAX_PROPOSAL_SET_LENGTH for (uint256 i = 0; i < idsArrayLength; ++i) { bytes32 id = proposedContractSetIds[i]; address proposedContract = proposedContractSetAddresses[i]; if ( proposedContract == address(0) || // The Proposed address must be a valid address s_route[id] == proposedContract // The Proposed address must point to a different address than what is currently set ) { revert InvalidProposal(); } emit ContractProposed({ proposedContractSetId: id, proposedContractSetFromAddress: s_route[id], proposedContractSetToAddress: proposedContract }); } s_proposedContractSet = ContractProposalSet({ids: proposedContractSetIds, to: proposedContractSetAddresses}); } /// @inheritdoc IFunctionsRouter function updateContracts() external override onlyOwner { // Iterations will not exceed MAX_PROPOSAL_SET_LENGTH for (uint256 i = 0; i < s_proposedContractSet.ids.length; ++i) { bytes32 id = s_proposedContractSet.ids[i]; address to = s_proposedContractSet.to[i]; emit ContractUpdated({id: id, from: s_route[id], to: to}); s_route[id] = to; } delete s_proposedContractSet; } // ================================================================ // | Modifiers | // ================================================================ // Favoring internal functions over actual modifiers to reduce contract size /// @dev Used within FunctionsSubscriptions.sol function _whenNotPaused() internal view override { _requireNotPaused(); } /// @dev Used within FunctionsSubscriptions.sol function _onlyRouterOwner() internal view override { _validateOwnership(); } /// @dev Used within FunctionsSubscriptions.sol function _onlySenderThatAcceptedToS() internal view override { address currentImplementation = s_route[s_allowListId]; if (currentImplementation == address(0)) { // If not set, ignore this check, allow all access return; } if (!IAccessController(currentImplementation).hasAccess(msg.sender, new bytes(0))) { revert SenderMustAcceptTermsOfService(msg.sender); } } /// @inheritdoc IFunctionsRouter function pause() external override onlyOwner { _pause(); } /// @inheritdoc IFunctionsRouter function unpause() external override onlyOwner { _unpause(); } }
// 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; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; import {IOwnableFunctionsRouter} from "./interfaces/IOwnableFunctionsRouter.sol"; /// @title This abstract should be inherited by contracts that will be used /// as the destinations to a route (id=>contract) on the Router. /// It provides a Router getter and modifiers. abstract contract Routable is ITypeAndVersion { IOwnableFunctionsRouter private immutable i_functionsRouter; error RouterMustBeSet(); error OnlyCallableByRouter(); error OnlyCallableByRouterOwner(); /// @dev Initializes the contract. constructor(address router) { if (router == address(0)) { revert RouterMustBeSet(); } i_functionsRouter = IOwnableFunctionsRouter(router); } /// @notice Return the Router function _getRouter() internal view returns (IOwnableFunctionsRouter router) { return i_functionsRouter; } /// @notice Reverts if called by anyone other than the router. modifier onlyRouter() { if (msg.sender != address(i_functionsRouter)) { revert OnlyCallableByRouter(); } _; } /// @notice Reverts if called by anyone other than the router owner. modifier onlyRouterOwner() { if (msg.sender != i_functionsRouter.owner()) { revert OnlyCallableByRouterOwner(); } _; } }
// 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 }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {FunctionsClient} from "../FunctionsClient.sol"; import {ConfirmedOwner} from "../../../../shared/access/ConfirmedOwner.sol"; import {FunctionsRequest} from "../libraries/FunctionsRequest.sol"; /// @title Chainlink Functions example Client contract implementation contract FunctionsClientExample is FunctionsClient, ConfirmedOwner { using FunctionsRequest for FunctionsRequest.Request; uint32 public constant MAX_CALLBACK_GAS = 70_000; bytes32 public s_lastRequestId; bytes32 public s_lastResponse; bytes32 public s_lastError; uint32 public s_lastResponseLength; uint32 public s_lastErrorLength; error UnexpectedRequestID(bytes32 requestId); constructor(address router) FunctionsClient(router) ConfirmedOwner(msg.sender) {} /// @notice Send a simple request /// @param source JavaScript source code /// @param encryptedSecretsReferences Encrypted secrets payload /// @param args List of arguments accessible from within the source code /// @param subscriptionId Billing ID function sendRequest( string calldata source, bytes calldata encryptedSecretsReferences, string[] calldata args, uint64 subscriptionId, bytes32 jobId ) external onlyOwner { FunctionsRequest.Request memory req; req._initializeRequestForInlineJavaScript(source); if (encryptedSecretsReferences.length > 0) req._addSecretsReference(encryptedSecretsReferences); if (args.length > 0) req._setArgs(args); s_lastRequestId = _sendRequest(req._encodeCBOR(), subscriptionId, MAX_CALLBACK_GAS, jobId); } /// @notice Store latest result/error /// @param requestId The request ID, returned by sendRequest() /// @param response Aggregated response from the user code /// @param err Aggregated error from 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 override { if (s_lastRequestId != requestId) { revert UnexpectedRequestID(requestId); } // Save only the first 32 bytes of response/error to always fit within MAX_CALLBACK_GAS s_lastResponse = _bytesToBytes32(response); s_lastResponseLength = uint32(response.length); s_lastError = _bytesToBytes32(err); s_lastErrorLength = uint32(err.length); } function _bytesToBytes32(bytes memory b) private pure returns (bytes32 out) { uint256 maxLen = 32; if (b.length < 32) { maxLen = b.length; } for (uint256 i = 0; i < maxLen; ++i) { out |= bytes32(b[i]) >> (i * 8); } return out; } }
// 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. }
// 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; }
// 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); }
// 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; }
// 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); }
// 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 {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {ArbGasInfo} from "../../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; import {GasPriceOracle} from "../../../../vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/GasPriceOracle.sol"; /// @dev A library that abstracts out opcodes that behave differently across chains. /// @dev The methods below return values that are pertinent to the given chain. library ChainSpecificUtil { // ------------ Start Arbitrum Constants ------------ /// @dev ARBGAS_ADDR is the address of the ArbGasInfo precompile on Arbitrum. /// @dev reference: https://github.com/OffchainLabs/nitro/blob/v2.0.14/contracts/src/precompiles/ArbGasInfo.sol#L10 address private constant ARBGAS_ADDR = address(0x000000000000000000000000000000000000006C); ArbGasInfo private constant ARBGAS = ArbGasInfo(ARBGAS_ADDR); uint256 private constant ARB_MAINNET_CHAIN_ID = 42161; uint256 private constant ARB_GOERLI_TESTNET_CHAIN_ID = 421613; uint256 private constant ARB_SEPOLIA_TESTNET_CHAIN_ID = 421614; // ------------ End Arbitrum Constants ------------ // ------------ Start Optimism Constants ------------ /// @dev L1_FEE_DATA_PADDING includes 35 bytes for L1 data padding for Optimism bytes internal constant L1_FEE_DATA_PADDING = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; /// @dev OVM_GASPRICEORACLE_ADDR is the address of the GasPriceOracle precompile on Optimism. /// @dev reference: https://community.optimism.io/docs/developers/build/transaction-fees/#estimating-the-l1-data-fee address private constant OVM_GASPRICEORACLE_ADDR = address(0x420000000000000000000000000000000000000F); GasPriceOracle private constant OVM_GASPRICEORACLE = GasPriceOracle(OVM_GASPRICEORACLE_ADDR); uint256 private constant OP_MAINNET_CHAIN_ID = 10; uint256 private constant OP_GOERLI_CHAIN_ID = 420; uint256 private constant OP_SEPOLIA_CHAIN_ID = 11155420; /// @dev Base is a OP stack based rollup and follows the same L1 pricing logic as Optimism. uint256 private constant BASE_MAINNET_CHAIN_ID = 8453; uint256 private constant BASE_GOERLI_CHAIN_ID = 84531; uint256 private constant BASE_SEPOLIA_CHAIN_ID = 84532; // ------------ End Optimism Constants ------------ /// @notice Returns the L1 fees in wei that will be paid for the current transaction, given any calldata /// @notice for the current transaction. /// @notice When on a known Arbitrum chain, it uses ArbGas.getCurrentTxL1GasFees to get the fees. /// @notice On Arbitrum, the provided calldata is not used to calculate the fees. /// @notice On Optimism, the provided calldata is passed to the GasPriceOracle predeploy /// @notice and getL1Fee is called to get the fees. function _getCurrentTxL1GasFees(bytes memory txCallData) internal view returns (uint256 l1FeeWei) { uint256 chainid = block.chainid; if (_isArbitrumChainId(chainid)) { return ARBGAS.getCurrentTxL1GasFees(); } else if (_isOptimismChainId(chainid)) { return OVM_GASPRICEORACLE.getL1Fee(bytes.concat(txCallData, L1_FEE_DATA_PADDING)); } return 0; } /// @notice Return true if and only if the provided chain ID is an Arbitrum chain ID. function _isArbitrumChainId(uint256 chainId) internal pure returns (bool) { return chainId == ARB_MAINNET_CHAIN_ID || chainId == ARB_GOERLI_TESTNET_CHAIN_ID || chainId == ARB_SEPOLIA_TESTNET_CHAIN_ID; } /// @notice Return true if and only if the provided chain ID is an Optimism (or Base) chain ID. /// @notice Note that optimism chain id's are also OP stack chain id's. function _isOptimismChainId(uint256 chainId) internal pure returns (bool) { return chainId == OP_MAINNET_CHAIN_ID || chainId == OP_GOERLI_CHAIN_ID || chainId == OP_SEPOLIA_CHAIN_ID || chainId == BASE_MAINNET_CHAIN_ID || chainId == BASE_GOERLI_CHAIN_ID || chainId == BASE_SEPOLIA_CHAIN_ID; } }
// 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; } }
// 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 } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; contract FunctionsV1EventsMock { struct Config { uint16 maxConsumersPerSubscription; uint72 adminFee; bytes4 handleOracleFulfillmentSelector; uint16 gasForCallExactCheck; uint32[] maxCallbackGasLimits; } event ConfigUpdated(Config param1); event ContractProposed( bytes32 proposedContractSetId, address proposedContractSetFromAddress, address proposedContractSetToAddress ); event ContractUpdated(bytes32 id, address from, address to); event FundsRecovered(address to, uint256 amount); event OwnershipTransferRequested(address indexed from, address indexed to); event OwnershipTransferred(address indexed from, address indexed to); event Paused(address account); event RequestNotProcessed(bytes32 indexed requestId, address coordinator, address transmitter, uint8 resultCode); event RequestProcessed( bytes32 indexed requestId, uint64 indexed subscriptionId, uint96 totalCostJuels, address transmitter, uint8 resultCode, bytes response, bytes err, bytes callbackReturnData ); event RequestStart( bytes32 indexed requestId, bytes32 indexed donId, uint64 indexed subscriptionId, address subscriptionOwner, address requestingContract, address requestInitiator, bytes data, uint16 dataVersion, uint32 callbackGasLimit, uint96 estimatedTotalCostJuels ); event RequestTimedOut(bytes32 indexed requestId); event SubscriptionCanceled(uint64 indexed subscriptionId, address fundsRecipient, uint256 fundsAmount); event SubscriptionConsumerAdded(uint64 indexed subscriptionId, address consumer); event SubscriptionConsumerRemoved(uint64 indexed subscriptionId, address consumer); event SubscriptionCreated(uint64 indexed subscriptionId, address owner); event SubscriptionFunded(uint64 indexed subscriptionId, uint256 oldBalance, uint256 newBalance); event SubscriptionOwnerTransferRequested(uint64 indexed subscriptionId, address from, address to); event SubscriptionOwnerTransferred(uint64 indexed subscriptionId, address from, address to); event Unpaused(address account); function emitConfigUpdated(Config memory param1) public { emit ConfigUpdated(param1); } function emitContractProposed( bytes32 proposedContractSetId, address proposedContractSetFromAddress, address proposedContractSetToAddress ) public { emit ContractProposed(proposedContractSetId, proposedContractSetFromAddress, proposedContractSetToAddress); } function emitContractUpdated(bytes32 id, address from, address to) public { emit ContractUpdated(id, from, to); } function emitFundsRecovered(address to, uint256 amount) public { emit FundsRecovered(to, amount); } function emitOwnershipTransferRequested(address from, address to) public { emit OwnershipTransferRequested(from, to); } function emitOwnershipTransferred(address from, address to) public { emit OwnershipTransferred(from, to); } function emitPaused(address account) public { emit Paused(account); } function emitRequestNotProcessed( bytes32 requestId, address coordinator, address transmitter, uint8 resultCode ) public { emit RequestNotProcessed(requestId, coordinator, transmitter, resultCode); } function emitRequestProcessed( bytes32 requestId, uint64 subscriptionId, uint96 totalCostJuels, address transmitter, uint8 resultCode, bytes memory response, bytes memory err, bytes memory callbackReturnData ) public { emit RequestProcessed( requestId, subscriptionId, totalCostJuels, transmitter, resultCode, response, err, callbackReturnData ); } function emitRequestStart( bytes32 requestId, bytes32 donId, uint64 subscriptionId, address subscriptionOwner, address requestingContract, address requestInitiator, bytes memory data, uint16 dataVersion, uint32 callbackGasLimit, uint96 estimatedTotalCostJuels ) public { emit RequestStart( requestId, donId, subscriptionId, subscriptionOwner, requestingContract, requestInitiator, data, dataVersion, callbackGasLimit, estimatedTotalCostJuels ); } function emitRequestTimedOut(bytes32 requestId) public { emit RequestTimedOut(requestId); } function emitSubscriptionCanceled(uint64 subscriptionId, address fundsRecipient, uint256 fundsAmount) public { emit SubscriptionCanceled(subscriptionId, fundsRecipient, fundsAmount); } function emitSubscriptionConsumerAdded(uint64 subscriptionId, address consumer) public { emit SubscriptionConsumerAdded(subscriptionId, consumer); } function emitSubscriptionConsumerRemoved(uint64 subscriptionId, address consumer) public { emit SubscriptionConsumerRemoved(subscriptionId, consumer); } function emitSubscriptionCreated(uint64 subscriptionId, address owner) public { emit SubscriptionCreated(subscriptionId, owner); } function emitSubscriptionFunded(uint64 subscriptionId, uint256 oldBalance, uint256 newBalance) public { emit SubscriptionFunded(subscriptionId, oldBalance, newBalance); } function emitSubscriptionOwnerTransferRequested(uint64 subscriptionId, address from, address to) public { emit SubscriptionOwnerTransferRequested(subscriptionId, from, to); } function emitSubscriptionOwnerTransferred(uint64 subscriptionId, address from, address to) public { emit SubscriptionOwnerTransferred(subscriptionId, from, to); } function emitUnpaused(address account) public { emit Unpaused(account); } }
// 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; }
// 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"); _; } 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); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {FunctionsRequest} from "../../../dev/v1_X/libraries/FunctionsRequest.sol"; import {FunctionsClient} from "../../../dev/v1_X/FunctionsClient.sol"; import {ConfirmedOwner} from "../../../../shared/access/ConfirmedOwner.sol"; contract FunctionsClientUpgradeHelper is FunctionsClient, ConfirmedOwner { using FunctionsRequest for FunctionsRequest.Request; constructor(address router) FunctionsClient(router) ConfirmedOwner(msg.sender) {} event ResponseReceived(bytes32 indexed requestId, bytes result, bytes err); /** * @notice Send a simple request * * @param donId DON ID * @param source JavaScript source code * @param secrets Encrypted secrets payload * @param args List of arguments accessible from within the source code * @param subscriptionId Funtions billing subscription ID * @param callbackGasLimit Maximum amount of gas used to call the client contract's `handleOracleFulfillment` function * @return Functions request ID */ function sendRequest( bytes32 donId, string calldata source, bytes calldata secrets, string[] calldata args, bytes[] memory bytesArgs, uint64 subscriptionId, uint32 callbackGasLimit ) public onlyOwner returns (bytes32) { FunctionsRequest.Request memory req; req._initializeRequestForInlineJavaScript(source); if (secrets.length > 0) req._addSecretsReference(secrets); if (args.length > 0) req._setArgs(args); if (bytesArgs.length > 0) req._setBytesArgs(bytesArgs); return _sendRequest(FunctionsRequest._encodeCBOR(req), subscriptionId, callbackGasLimit, donId); } function sendRequestBytes( bytes memory data, uint64 subscriptionId, uint32 callbackGasLimit, bytes32 donId ) public returns (bytes32 requestId) { return _sendRequest(data, subscriptionId, callbackGasLimit, donId); } /** * @notice Same as sendRequest but for DONHosted secrets */ function sendRequestWithDONHostedSecrets( bytes32 donId, string calldata source, uint8 slotId, uint64 slotVersion, string[] calldata args, uint64 subscriptionId, uint32 callbackGasLimit ) public onlyOwner returns (bytes32) { FunctionsRequest.Request memory req; req._initializeRequestForInlineJavaScript(source); req._addDONHostedSecrets(slotId, slotVersion); if (args.length > 0) req._setArgs(args); return _sendRequest(FunctionsRequest._encodeCBOR(req), subscriptionId, callbackGasLimit, donId); } // @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 _sendRequestToProposed( bytes memory data, uint64 subscriptionId, uint32 callbackGasLimit, bytes32 donId ) internal returns (bytes32) { bytes32 requestId = i_functionsRouter.sendRequestToProposed( subscriptionId, data, FunctionsRequest.REQUEST_DATA_VERSION, callbackGasLimit, donId ); emit RequestSent(requestId); return requestId; } /** * @notice Send a simple request to the proposed contract * * @param donId DON ID * @param source JavaScript source code * @param secrets Encrypted secrets payload * @param args List of arguments accessible from within the source code * @param subscriptionId Funtions billing subscription ID * @param callbackGasLimit Maximum amount of gas used to call the client contract's `handleOracleFulfillment` function * @return Functions request ID */ function sendRequestToProposed( bytes32 donId, string calldata source, bytes calldata secrets, string[] calldata args, bytes[] memory bytesArgs, uint64 subscriptionId, uint32 callbackGasLimit ) public onlyOwner returns (bytes32) { FunctionsRequest.Request memory req; req._initializeRequestForInlineJavaScript(source); if (secrets.length > 0) req._addSecretsReference(secrets); if (args.length > 0) req._setArgs(args); if (bytesArgs.length > 0) req._setBytesArgs(bytesArgs); return _sendRequestToProposed(FunctionsRequest._encodeCBOR(req), subscriptionId, callbackGasLimit, donId); } /** * @notice Same as sendRequestToProposed but for DONHosted secrets */ function sendRequestToProposedWithDONHostedSecrets( bytes32 donId, string calldata source, uint8 slotId, uint64 slotVersion, string[] calldata args, uint64 subscriptionId, uint32 callbackGasLimit ) public onlyOwner returns (bytes32) { FunctionsRequest.Request memory req; req._initializeRequestForInlineJavaScript(source); req._addDONHostedSecrets(slotId, slotVersion); if (args.length > 0) req._setArgs(args); return _sendRequestToProposed(FunctionsRequest._encodeCBOR(req), subscriptionId, callbackGasLimit, donId); } /** * @notice Callback that is invoked once the DON has resolved the request or hit an error * * @param requestId The request ID, returned by sendRequest() * @param response Aggregated response from the user code * @param err Aggregated error from the user code or from the execution pipeline * Either response or error parameter will be set, but never both */ function _fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override { emit ResponseReceived(requestId, response, err); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol"; /// @title The ConfirmedOwner contract /// @notice A contract with helpers for basic contract ownership. contract ConfirmedOwner is ConfirmedOwnerWithProposal { constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IOwnable} from "../interfaces/IOwnable.sol"; /// @title The ConfirmedOwner contract /// @notice A contract with helpers for basic contract ownership. contract ConfirmedOwnerWithProposal is IOwnable { address private s_owner; address private s_pendingOwner; event OwnershipTransferRequested(address indexed from, address indexed to); event OwnershipTransferred(address indexed from, address indexed to); constructor(address newOwner, address pendingOwner) { // solhint-disable-next-line custom-errors require(newOwner != address(0), "Cannot set owner to zero"); s_owner = newOwner; if (pendingOwner != address(0)) { _transferOwnership(pendingOwner); } } /// @notice Allows an owner to begin transferring ownership to a new address. function transferOwnership(address to) public override onlyOwner { _transferOwnership(to); } /// @notice Allows an ownership transfer to be completed by the recipient. function acceptOwnership() external override { // solhint-disable-next-line custom-errors require(msg.sender == s_pendingOwner, "Must be proposed owner"); address oldOwner = s_owner; s_owner = msg.sender; s_pendingOwner = address(0); emit OwnershipTransferred(oldOwner, msg.sender); } /// @notice Get the current owner function owner() public view override returns (address) { return s_owner; } /// @notice validate, transfer ownership, and emit relevant events function _transferOwnership(address to) private { // solhint-disable-next-line custom-errors require(to != msg.sender, "Cannot transfer to self"); s_pendingOwner = to; emit OwnershipTransferRequested(s_owner, to); } /// @notice validate access function _validateOwnership() internal view { // solhint-disable-next-line custom-errors require(msg.sender == s_owner, "Only callable by owner"); } /// @notice Reverts if called by anyone other than the contract owner. modifier onlyOwner() { _validateOwnership(); _; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface 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 IAccessController { function hasAccess(address user, bytes calldata data) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.6; interface IERC677Receiver { function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IOwnable { function owner() external returns (address); function transferOwnership(address recipient) external; function acceptOwnership() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ITypeAndVersion { function typeAndVersion() external pure returns (string memory); }
// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE // SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.4.21 <0.9.0; interface ArbGasInfo { // return gas prices in wei, assuming the specified aggregator is used // ( // per L2 tx, // per L1 calldata unit, (zero byte = 4 units, nonzero byte = 16 units) // per storage allocation, // per ArbGas base, // per ArbGas congestion, // per ArbGas total // ) function getPricesInWeiWithAggregator(address aggregator) external view returns (uint, uint, uint, uint, uint, uint); // return gas prices in wei, as described above, assuming the caller's preferred aggregator is used // if the caller hasn't specified a preferred aggregator, the default aggregator is assumed function getPricesInWei() external view returns (uint, uint, uint, uint, uint, uint); // return prices in ArbGas (per L2 tx, per L1 calldata unit, per storage allocation), // assuming the specified aggregator is used function getPricesInArbGasWithAggregator(address aggregator) external view returns (uint, uint, uint); // return gas prices in ArbGas, as described above, assuming the caller's preferred aggregator is used // if the caller hasn't specified a preferred aggregator, the default aggregator is assumed function getPricesInArbGas() external view returns (uint, uint, uint); // return gas accounting parameters (speedLimitPerSecond, gasPoolMax, maxTxGasLimit) function getGasAccountingParams() external view returns (uint, uint, uint); // get ArbOS's estimate of the L1 gas price in wei function getL1GasPriceEstimate() external view returns(uint); // set ArbOS's estimate of the L1 gas price in wei // reverts unless called by chain owner or designated gas oracle (if any) function setL1GasPriceEstimate(uint priceInWei) external; // get L1 gas fees paid by the current transaction (txBaseFeeWei, calldataFeeWei) function getCurrentTxL1GasFees() external view returns(uint); }
// SPDX-License-Identifier: BSD-2-Clause pragma solidity ^0.8.4; /** * @dev A library for working with mutable byte buffers in Solidity. * * Byte buffers are mutable and expandable, and provide a variety of primitives * for appending to them. At any time you can fetch a bytes object containing the * current contents of the buffer. The bytes object should not be stored between * operations, as it may change due to resizing of the buffer. */ library Buffer { /** * @dev Represents a mutable buffer. Buffers have a current value (buf) and * a capacity. The capacity may be longer than the current value, in * which case it can be extended without the need to allocate more memory. */ struct buffer { bytes buf; uint capacity; } /** * @dev Initializes a buffer with an initial capacity. * @param buf The buffer to initialize. * @param capacity The number of bytes of space to allocate the buffer. * @return The buffer, for chaining. */ function init(buffer memory buf, uint capacity) internal pure returns(buffer memory) { if (capacity % 32 != 0) { capacity += 32 - (capacity % 32); } // Allocate space for the buffer data buf.capacity = capacity; assembly { let ptr := mload(0x40) mstore(buf, ptr) mstore(ptr, 0) let fpm := add(32, add(ptr, capacity)) if lt(fpm, ptr) { revert(0, 0) } mstore(0x40, fpm) } return buf; } /** * @dev Initializes a new buffer from an existing bytes object. * Changes to the buffer may mutate the original value. * @param b The bytes object to initialize the buffer with. * @return A new buffer. */ function fromBytes(bytes memory b) internal pure returns(buffer memory) { buffer memory buf; buf.buf = b; buf.capacity = b.length; return buf; } function resize(buffer memory buf, uint capacity) private pure { bytes memory oldbuf = buf.buf; init(buf, capacity); append(buf, oldbuf); } /** * @dev Sets buffer length to 0. * @param buf The buffer to truncate. * @return The original buffer, for chaining.. */ function truncate(buffer memory buf) internal pure returns (buffer memory) { assembly { let bufptr := mload(buf) mstore(bufptr, 0) } return buf; } /** * @dev Appends len bytes of a byte string to a buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @param len The number of bytes to copy. * @return The original buffer, for chaining. */ function append(buffer memory buf, bytes memory data, uint len) internal pure returns(buffer memory) { require(len <= data.length); uint off = buf.buf.length; uint newCapacity = off + len; if (newCapacity > buf.capacity) { resize(buf, newCapacity * 2); } uint dest; uint src; assembly { // Memory address of the buffer data let bufptr := mload(buf) // Length of existing buffer data let buflen := mload(bufptr) // Start address = buffer address + offset + sizeof(buffer length) dest := add(add(bufptr, 32), off) // Update buffer length if we're extending it if gt(newCapacity, buflen) { mstore(bufptr, newCapacity) } src := add(data, 32) } // Copy word-length chunks while possible for (; len >= 32; len -= 32) { assembly { mstore(dest, mload(src)) } dest += 32; src += 32; } // Copy remaining bytes unchecked { uint mask = (256 ** (32 - len)) - 1; assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) mstore(dest, or(destpart, srcpart)) } } return buf; } /** * @dev Appends a byte string to a buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chaining. */ function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) { return append(buf, data, data.length); } /** * @dev Appends a byte to the buffer. Resizes if doing so would exceed the * capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chaining. */ function appendUint8(buffer memory buf, uint8 data) internal pure returns(buffer memory) { uint off = buf.buf.length; uint offPlusOne = off + 1; if (off >= buf.capacity) { resize(buf, offPlusOne * 2); } assembly { // Memory address of the buffer data let bufptr := mload(buf) // Address = buffer address + sizeof(buffer length) + off let dest := add(add(bufptr, off), 32) mstore8(dest, data) // Update buffer length if we extended it if gt(offPlusOne, mload(bufptr)) { mstore(bufptr, offPlusOne) } } return buf; } /** * @dev Appends len bytes of bytes32 to a buffer. Resizes if doing so would * exceed the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @param len The number of bytes to write (left-aligned). * @return The original buffer, for chaining. */ function append(buffer memory buf, bytes32 data, uint len) private pure returns(buffer memory) { uint off = buf.buf.length; uint newCapacity = len + off; if (newCapacity > buf.capacity) { resize(buf, newCapacity * 2); } unchecked { uint mask = (256 ** len) - 1; // Right-align data data = data >> (8 * (32 - len)); assembly { // Memory address of the buffer data let bufptr := mload(buf) // Address = buffer address + sizeof(buffer length) + newCapacity let dest := add(bufptr, newCapacity) mstore(dest, or(and(mload(dest), not(mask)), data)) // Update buffer length if we extended it if gt(newCapacity, mload(bufptr)) { mstore(bufptr, newCapacity) } } } return buf; } /** * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chhaining. */ function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) { return append(buf, bytes32(data), 20); } /** * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed * the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @return The original buffer, for chaining. */ function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) { return append(buf, data, 32); } /** * @dev Appends a byte to the end of the buffer. Resizes if doing so would * exceed the capacity of the buffer. * @param buf The buffer to append to. * @param data The data to append. * @param len The number of bytes to write (right-aligned). * @return The original buffer. */ function appendInt(buffer memory buf, uint data, uint len) internal pure returns(buffer memory) { uint off = buf.buf.length; uint newCapacity = len + off; if (newCapacity > buf.capacity) { resize(buf, newCapacity * 2); } uint mask = (256 ** len) - 1; assembly { // Memory address of the buffer data let bufptr := mload(buf) // Address = buffer address + sizeof(buffer length) + newCapacity let dest := add(bufptr, newCapacity) mstore(dest, or(and(mload(dest), not(mask)), data)) // Update buffer length if we extended it if gt(newCapacity, mload(bufptr)) { mstore(bufptr, newCapacity) } } return buf; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import { ISemver } from "../universal/ISemver.sol"; import { Predeploys } from "../libraries/Predeploys.sol"; import { L1Block } from "./L1Block.sol"; /// @custom:proxied /// @custom:predeploy 0x420000000000000000000000000000000000000F /// @title GasPriceOracle /// @notice This contract maintains the variables responsible for computing the L1 portion of the /// total fee charged on L2. Before Bedrock, this contract held variables in state that were /// read during the state transition function to compute the L1 portion of the transaction /// fee. After Bedrock, this contract now simply proxies the L1Block contract, which has /// the values used to compute the L1 portion of the fee in its state. /// /// The contract exposes an API that is useful for knowing how large the L1 portion of the /// transaction fee will be. The following events were deprecated with Bedrock: /// - event OverheadUpdated(uint256 overhead); /// - event ScalarUpdated(uint256 scalar); /// - event DecimalsUpdated(uint256 decimals); contract GasPriceOracle is ISemver { /// @notice Number of decimals used in the scalar. uint256 public constant DECIMALS = 6; /// @notice Semantic version. /// @custom:semver 1.1.0 string public constant version = "1.1.0"; /// @notice Computes the L1 portion of the fee based on the size of the rlp encoded input /// transaction, the current L1 base fee, and the various dynamic parameters. /// @param _data Unsigned fully RLP-encoded transaction to get the L1 fee for. /// @return L1 fee that should be paid for the tx function getL1Fee(bytes memory _data) external view returns (uint256) { uint256 l1GasUsed = getL1GasUsed(_data); uint256 l1Fee = l1GasUsed * l1BaseFee(); uint256 divisor = 10 ** DECIMALS; uint256 unscaled = l1Fee * scalar(); uint256 scaled = unscaled / divisor; return scaled; } /// @notice Retrieves the current gas price (base fee). /// @return Current L2 gas price (base fee). function gasPrice() public view returns (uint256) { return block.basefee; } /// @notice Retrieves the current base fee. /// @return Current L2 base fee. function baseFee() public view returns (uint256) { return block.basefee; } /// @notice Retrieves the current fee overhead. /// @return Current fee overhead. function overhead() public view returns (uint256) { return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeOverhead(); } /// @notice Retrieves the current fee scalar. /// @return Current fee scalar. function scalar() public view returns (uint256) { return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeScalar(); } /// @notice Retrieves the latest known L1 base fee. /// @return Latest known L1 base fee. function l1BaseFee() public view returns (uint256) { return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).basefee(); } /// @custom:legacy /// @notice Retrieves the number of decimals used in the scalar. /// @return Number of decimals used in the scalar. function decimals() public pure returns (uint256) { return DECIMALS; } /// @notice Computes the amount of L1 gas used for a transaction. Adds the overhead which /// represents the per-transaction gas overhead of posting the transaction and state /// roots to L1. Adds 68 bytes of padding to account for the fact that the input does /// not have a signature. /// @param _data Unsigned fully RLP-encoded transaction to get the L1 gas for. /// @return Amount of L1 gas used to publish the transaction. function getL1GasUsed(bytes memory _data) public view returns (uint256) { uint256 total = 0; uint256 length = _data.length; for (uint256 i = 0; i < length; i++) { if (_data[i] == 0) { total += 4; } else { total += 16; } } uint256 unsigned = total + overhead(); return unsigned + (68 * 16); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.15; import { ISemver } from "../universal/ISemver.sol"; /// @custom:proxied /// @custom:predeploy 0x4200000000000000000000000000000000000015 /// @title L1Block /// @notice The L1Block predeploy gives users access to information about the last known L1 block. /// Values within this contract are updated once per epoch (every L1 block) and can only be /// set by the "depositor" account, a special system address. Depositor account transactions /// are created by the protocol whenever we move to a new epoch. contract L1Block is ISemver { /// @notice Address of the special depositor account. address public constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001; /// @notice The latest L1 block number known by the L2 system. uint64 public number; /// @notice The latest L1 timestamp known by the L2 system. uint64 public timestamp; /// @notice The latest L1 basefee. uint256 public basefee; /// @notice The latest L1 blockhash. bytes32 public hash; /// @notice The number of L2 blocks in the same epoch. uint64 public sequenceNumber; /// @notice The versioned hash to authenticate the batcher by. bytes32 public batcherHash; /// @notice The overhead value applied to the L1 portion of the transaction fee. uint256 public l1FeeOverhead; /// @notice The scalar value applied to the L1 portion of the transaction fee. uint256 public l1FeeScalar; /// @custom:semver 1.1.0 string public constant version = "1.1.0"; /// @notice Updates the L1 block values. /// @param _number L1 blocknumber. /// @param _timestamp L1 timestamp. /// @param _basefee L1 basefee. /// @param _hash L1 blockhash. /// @param _sequenceNumber Number of L2 blocks since epoch start. /// @param _batcherHash Versioned hash to authenticate batcher by. /// @param _l1FeeOverhead L1 fee overhead. /// @param _l1FeeScalar L1 fee scalar. function setL1BlockValues( uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar ) external { require(msg.sender == DEPOSITOR_ACCOUNT, "L1Block: only the depositor account can set L1 block values"); number = _number; timestamp = _timestamp; basefee = _basefee; hash = _hash; sequenceNumber = _sequenceNumber; batcherHash = _batcherHash; l1FeeOverhead = _l1FeeOverhead; l1FeeScalar = _l1FeeScalar; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title Predeploys /// @notice Contains constant addresses for contracts that are pre-deployed to the L2 system. library Predeploys { /// @notice Address of the L2ToL1MessagePasser predeploy. address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000016; /// @notice Address of the L2CrossDomainMessenger predeploy. address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007; /// @notice Address of the L2StandardBridge predeploy. address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010; /// @notice Address of the L2ERC721Bridge predeploy. address internal constant L2_ERC721_BRIDGE = 0x4200000000000000000000000000000000000014; //// @notice Address of the SequencerFeeWallet predeploy. address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011; /// @notice Address of the OptimismMintableERC20Factory predeploy. address internal constant OPTIMISM_MINTABLE_ERC20_FACTORY = 0x4200000000000000000000000000000000000012; /// @notice Address of the OptimismMintableERC721Factory predeploy. address internal constant OPTIMISM_MINTABLE_ERC721_FACTORY = 0x4200000000000000000000000000000000000017; /// @notice Address of the L1Block predeploy. address internal constant L1_BLOCK_ATTRIBUTES = 0x4200000000000000000000000000000000000015; /// @notice Address of the GasPriceOracle predeploy. Includes fee information /// and helpers for computing the L1 portion of the transaction fee. address internal constant GAS_PRICE_ORACLE = 0x420000000000000000000000000000000000000F; /// @custom:legacy /// @notice Address of the L1MessageSender predeploy. Deprecated. Use L2CrossDomainMessenger /// or access tx.origin (or msg.sender) in a L1 to L2 transaction instead. address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001; /// @custom:legacy /// @notice Address of the DeployerWhitelist predeploy. No longer active. address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002; /// @custom:legacy /// @notice Address of the LegacyERC20ETH predeploy. Deprecated. Balances are migrated to the /// state trie as of the Bedrock upgrade. Contract has been locked and write functions /// can no longer be accessed. address internal constant LEGACY_ERC20_ETH = 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000; /// @custom:legacy /// @notice Address of the L1BlockNumber predeploy. Deprecated. Use the L1Block predeploy /// instead, which exposes more information about the L1 state. address internal constant L1_BLOCK_NUMBER = 0x4200000000000000000000000000000000000013; /// @custom:legacy /// @notice Address of the LegacyMessagePasser predeploy. Deprecate. Use the updated /// L2ToL1MessagePasser contract instead. address internal constant LEGACY_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000; /// @notice Address of the ProxyAdmin predeploy. address internal constant PROXY_ADMIN = 0x4200000000000000000000000000000000000018; /// @notice Address of the BaseFeeVault predeploy. address internal constant BASE_FEE_VAULT = 0x4200000000000000000000000000000000000019; /// @notice Address of the L1FeeVault predeploy. address internal constant L1_FEE_VAULT = 0x420000000000000000000000000000000000001A; /// @notice Address of the GovernanceToken predeploy. address internal constant GOVERNANCE_TOKEN = 0x4200000000000000000000000000000000000042; /// @notice Address of the SchemaRegistry predeploy. address internal constant SCHEMA_REGISTRY = 0x4200000000000000000000000000000000000020; /// @notice Address of the EAS predeploy. address internal constant EAS = 0x4200000000000000000000000000000000000021; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title ISemver /// @notice ISemver is a simple contract for ensuring that contracts are /// versioned using semantic versioning. interface ISemver { /// @notice Getter for the semantic version of the contract. This is not /// meant to be used onchain but instead meant to be used by offchain /// tooling. /// @return Semver contract version as a string. function version() external view returns (string memory); }
// 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.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 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) (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.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.8.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. * * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits * * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits * * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits * * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits * * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits * * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits * * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits * * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits * * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits * * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits * * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits * * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits * * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits * * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits * * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits * * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits * * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits * * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits * * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits * * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits * * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits * * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits * * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits * * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits * * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits * * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits * * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. * * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * 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. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * 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. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * 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. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * 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. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "../../@ensdomains/buffer/v0.1.0/Buffer.sol"; /** * @dev A library for populating CBOR encoded payload in Solidity. * * https://datatracker.ietf.org/doc/html/rfc7049 * * The library offers various write* and start* methods to encode values of different types. * The resulted buffer can be obtained with data() method. * Encoding of primitive types is staightforward, whereas encoding of sequences can result * in an invalid CBOR if start/write/end flow is violated. * For the purpose of gas saving, the library does not verify start/write/end flow internally, * except for nested start/end pairs. */ library CBOR { using Buffer for Buffer.buffer; struct CBORBuffer { Buffer.buffer buf; uint256 depth; } uint8 private constant MAJOR_TYPE_INT = 0; uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; uint8 private constant MAJOR_TYPE_BYTES = 2; uint8 private constant MAJOR_TYPE_STRING = 3; uint8 private constant MAJOR_TYPE_ARRAY = 4; uint8 private constant MAJOR_TYPE_MAP = 5; uint8 private constant MAJOR_TYPE_TAG = 6; uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; uint8 private constant TAG_TYPE_BIGNUM = 2; uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3; uint8 private constant CBOR_FALSE = 20; uint8 private constant CBOR_TRUE = 21; uint8 private constant CBOR_NULL = 22; uint8 private constant CBOR_UNDEFINED = 23; function create(uint256 capacity) internal pure returns(CBORBuffer memory cbor) { Buffer.init(cbor.buf, capacity); cbor.depth = 0; return cbor; } function data(CBORBuffer memory buf) internal pure returns(bytes memory) { require(buf.depth == 0, "Invalid CBOR"); return buf.buf.buf; } function writeUInt256(CBORBuffer memory buf, uint256 value) internal pure { buf.buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM)); writeBytes(buf, abi.encode(value)); } function writeInt256(CBORBuffer memory buf, int256 value) internal pure { if (value < 0) { buf.buf.appendUint8( uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM) ); writeBytes(buf, abi.encode(uint256(-1 - value))); } else { writeUInt256(buf, uint256(value)); } } function writeUInt64(CBORBuffer memory buf, uint64 value) internal pure { writeFixedNumeric(buf, MAJOR_TYPE_INT, value); } function writeInt64(CBORBuffer memory buf, int64 value) internal pure { if(value >= 0) { writeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); } else{ writeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(-1 - value)); } } function writeBytes(CBORBuffer memory buf, bytes memory value) internal pure { writeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length)); buf.buf.append(value); } function writeString(CBORBuffer memory buf, string memory value) internal pure { writeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length)); buf.buf.append(bytes(value)); } function writeBool(CBORBuffer memory buf, bool value) internal pure { writeContentFree(buf, value ? CBOR_TRUE : CBOR_FALSE); } function writeNull(CBORBuffer memory buf) internal pure { writeContentFree(buf, CBOR_NULL); } function writeUndefined(CBORBuffer memory buf) internal pure { writeContentFree(buf, CBOR_UNDEFINED); } function startArray(CBORBuffer memory buf) internal pure { writeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); buf.depth += 1; } function startFixedArray(CBORBuffer memory buf, uint64 length) internal pure { writeDefiniteLengthType(buf, MAJOR_TYPE_ARRAY, length); } function startMap(CBORBuffer memory buf) internal pure { writeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); buf.depth += 1; } function startFixedMap(CBORBuffer memory buf, uint64 length) internal pure { writeDefiniteLengthType(buf, MAJOR_TYPE_MAP, length); } function endSequence(CBORBuffer memory buf) internal pure { writeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); buf.depth -= 1; } function writeKVString(CBORBuffer memory buf, string memory key, string memory value) internal pure { writeString(buf, key); writeString(buf, value); } function writeKVBytes(CBORBuffer memory buf, string memory key, bytes memory value) internal pure { writeString(buf, key); writeBytes(buf, value); } function writeKVUInt256(CBORBuffer memory buf, string memory key, uint256 value) internal pure { writeString(buf, key); writeUInt256(buf, value); } function writeKVInt256(CBORBuffer memory buf, string memory key, int256 value) internal pure { writeString(buf, key); writeInt256(buf, value); } function writeKVUInt64(CBORBuffer memory buf, string memory key, uint64 value) internal pure { writeString(buf, key); writeUInt64(buf, value); } function writeKVInt64(CBORBuffer memory buf, string memory key, int64 value) internal pure { writeString(buf, key); writeInt64(buf, value); } function writeKVBool(CBORBuffer memory buf, string memory key, bool value) internal pure { writeString(buf, key); writeBool(buf, value); } function writeKVNull(CBORBuffer memory buf, string memory key) internal pure { writeString(buf, key); writeNull(buf); } function writeKVUndefined(CBORBuffer memory buf, string memory key) internal pure { writeString(buf, key); writeUndefined(buf); } function writeKVMap(CBORBuffer memory buf, string memory key) internal pure { writeString(buf, key); startMap(buf); } function writeKVArray(CBORBuffer memory buf, string memory key) internal pure { writeString(buf, key); startArray(buf); } function writeFixedNumeric( CBORBuffer memory buf, uint8 major, uint64 value ) private pure { if (value <= 23) { buf.buf.appendUint8(uint8((major << 5) | value)); } else if (value <= 0xFF) { buf.buf.appendUint8(uint8((major << 5) | 24)); buf.buf.appendInt(value, 1); } else if (value <= 0xFFFF) { buf.buf.appendUint8(uint8((major << 5) | 25)); buf.buf.appendInt(value, 2); } else if (value <= 0xFFFFFFFF) { buf.buf.appendUint8(uint8((major << 5) | 26)); buf.buf.appendInt(value, 4); } else { buf.buf.appendUint8(uint8((major << 5) | 27)); buf.buf.appendInt(value, 8); } } function writeIndefiniteLengthType(CBORBuffer memory buf, uint8 major) private pure { buf.buf.appendUint8(uint8((major << 5) | 31)); } function writeDefiniteLengthType(CBORBuffer memory buf, uint8 major, uint64 length) private pure { writeFixedNumeric(buf, major, length); } function writeContentFree(CBORBuffer memory buf, uint8 value) private pure { buf.buf.appendUint8(uint8((MAJOR_TYPE_CONTENT_FREE << 5) | value)); } }
{ "remappings": [ "ds-test/=foundry-lib/forge-std/lib/ds-test/src/", "forge-std/=foundry-lib/forge-std/src/", "@openzeppelin/=node_modules/@openzeppelin/", "hardhat/=node_modules/hardhat/", "@eth-optimism/=node_modules/@eth-optimism/", "@scroll-tech/=node_modules/@scroll-tech/" ], "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"components":[{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"address","name":"signerPublicKey","type":"address"}],"internalType":"struct TermsOfServiceAllowListConfig","name":"config","type":"tuple"},{"internalType":"address[]","name":"initialAllowedSenders","type":"address[]"},{"internalType":"address[]","name":"initialBlockedSenders","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidCalldata","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidUsage","type":"error"},{"inputs":[],"name":"RecipientIsBlocked","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"AddedAccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"BlockedAccess","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"address","name":"signerPublicKey","type":"address"}],"indexed":false,"internalType":"struct TermsOfServiceAllowListConfig","name":"config","type":"tuple"}],"name":"ConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"UnblockedAccess","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"acceptor","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"}],"name":"acceptTermsOfService","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"blockSender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllAllowedSenders","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllowedSendersCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"allowedSenderIdxStart","type":"uint64"},{"internalType":"uint64","name":"allowedSenderIdxEnd","type":"uint64"}],"name":"getAllowedSendersInRange","outputs":[{"internalType":"address[]","name":"allowedSenders","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBlockedSendersCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"blockedSenderIdxStart","type":"uint64"},{"internalType":"uint64","name":"blockedSenderIdxEnd","type":"uint64"}],"name":"getBlockedSendersInRange","outputs":[{"internalType":"address[]","name":"blockedSenders","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConfig","outputs":[{"components":[{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"address","name":"signerPublicKey","type":"address"}],"internalType":"struct TermsOfServiceAllowListConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"acceptor","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"getMessage","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"hasAccess","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"isBlockedSender","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"unblockSender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"address","name":"signerPublicKey","type":"address"}],"internalType":"struct TermsOfServiceAllowListConfig","name":"config","type":"tuple"}],"name":"updateConfig","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b5060405162001a3038038062001a308339810160408190526200003491620004d9565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620001d4565b505050620000d2836200027f60201b60201c565b60005b8251811015620001255762000111838281518110620000f857620000f8620005a8565b602002602001015160026200030660201b90919060201c565b506200011d81620005be565b9050620000d5565b5060005b8151811015620001ca57620001658282815181106200014c576200014c620005a8565b602002602001015160026200032660201b90919060201c565b156200018457604051638129bbcd60e01b815260040160405180910390fd5b620001b68282815181106200019d576200019d620005a8565b602002602001015160046200030660201b90919060201c565b50620001c281620005be565b905062000129565b50505050620005e6565b336001600160a01b038216036200022e5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6200028962000349565b805160068054602080850180516001600160a81b0319909316941515610100600160a81b03198116959095176101006001600160a01b039485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a910160405180910390a150565b60006200031d836001600160a01b038416620003a7565b90505b92915050565b6001600160a01b038116600090815260018301602052604081205415156200031d565b6000546001600160a01b03163314620003a55760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b6000818152600183016020526040812054620003f05750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000320565b50600062000320565b634e487b7160e01b600052604160045260246000fd5b80516001600160a01b03811681146200042757600080fd5b919050565b600082601f8301126200043e57600080fd5b815160206001600160401b03808311156200045d576200045d620003f9565b8260051b604051601f19603f83011681018181108482111715620004855762000485620003f9565b604052938452858101830193838101925087851115620004a457600080fd5b83870191505b84821015620004ce57620004be826200040f565b83529183019190830190620004aa565b979650505050505050565b60008060008385036080811215620004f057600080fd5b6040811215620004ff57600080fd5b50604080519081016001600160401b038082118383101715620005265762000526620003f9565b816040528651915081151582146200053d57600080fd5b8183526200054e602088016200040f565b60208401526040870151929550808311156200056957600080fd5b62000577888489016200042c565b945060608701519250808311156200058e57600080fd5b50506200059e868287016200042c565b9150509250925092565b634e487b7160e01b600052603260045260246000fd5b600060018201620005df57634e487b7160e01b600052601160045260246000fd5b5060010190565b61143a80620005f66000396000f3fe608060405234801561001057600080fd5b506004361061011b5760003560e01c8063817ef62e116100b2578063a39b06e311610081578063c3f909d411610066578063c3f909d4146102c3578063cc7ebf4914610322578063f2fde38b1461032a57600080fd5b8063a39b06e314610237578063a5e1d61d146102b057600080fd5b8063817ef62e146101e157806382184c7b146101e957806389f9a2c4146101fc5780638da5cb5b1461020f57600080fd5b80633908c4d4116100ee5780633908c4d41461018e57806347663acb146101a35780636b14daf8146101b657806379ba5097146101d957600080fd5b806301a05958146101205780630a8c9c2414610146578063181f5a771461016657806320229a861461017b575b600080fd5b61012861033d565b60405167ffffffffffffffff90911681526020015b60405180910390f35b610159610154366004610fd6565b61034e565b60405161013d9190611009565b61016e6104bc565b60405161013d9190611063565b610159610189366004610fd6565b6104d8565b6101a161019c3660046110f3565b61063e565b005b6101a16101b1366004611154565b6108e9565b6101c96101c436600461116f565b61094a565b604051901515815260200161013d565b6101a1610974565b610159610a76565b6101a16101f7366004611154565b610a82565b6101a161020a366004611221565b610ae8565b60005460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161013d565b6102a26102453660046112aa565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015283901b16603482015260009060480160405160208183030381529060405280519060200120905092915050565b60405190815260200161013d565b6101c96102be366004611154565b610ba3565b60408051808201825260008082526020918201528151808301835260065460ff8116151580835273ffffffffffffffffffffffffffffffffffffffff61010090920482169284019283528451908152915116918101919091520161013d565b610128610bc3565b6101a1610338366004611154565b610bcf565b60006103496004610be3565b905090565b60608167ffffffffffffffff168367ffffffffffffffff16118061038557506103776002610be3565b8267ffffffffffffffff1610155b8061039757506103956002610be3565b155b156103ce576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103d88383611303565b6103e3906001611324565b67ffffffffffffffff1667ffffffffffffffff811115610405576104056111f2565b60405190808252806020026020018201604052801561042e578160200160208202803683370190505b50905060005b61043e8484611303565b67ffffffffffffffff1681116104b45761046d6104658267ffffffffffffffff8716611345565b600290610bed565b82828151811061047f5761047f611358565b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101526104ad81611387565b9050610434565b505b92915050565b6040518060600160405280602c8152602001611402602c913981565b60608167ffffffffffffffff168367ffffffffffffffff16118061050f57506105016004610be3565b8267ffffffffffffffff1610155b80610521575061051f6004610be3565b155b15610558576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105628383611303565b61056d906001611324565b67ffffffffffffffff1667ffffffffffffffff81111561058f5761058f6111f2565b6040519080825280602002602001820160405280156105b8578160200160208202803683370190505b50905060005b6105c88484611303565b67ffffffffffffffff1681116104b4576105f76105ef8267ffffffffffffffff8716611345565b600490610bed565b82828151811061060957610609611358565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015261063781611387565b90506105be565b610649600485610bf9565b15610680576040517f62b7a34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051606087811b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009081166020808501919091529188901b16603483015282516028818403018152604890920190925280519101206000906040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c810191909152605c01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201206006546000855291840180845281905260ff8616928401929092526060830187905260808301869052909250610100900473ffffffffffffffffffffffffffffffffffffffff169060019060a0016020604051602081039080840390855afa1580156107b4573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff161461080b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff861614158061085057503373ffffffffffffffffffffffffffffffffffffffff8716148015906108505750333b155b15610887576040517f381cfcbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610892600286610c28565b156108e15760405173ffffffffffffffffffffffffffffffffffffffff861681527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db49060200160405180910390a15b505050505050565b6108f1610c4a565b6108fc600482610ccd565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527f28bbd0761309a99e8fb5e5d02ada0b7b2db2e5357531ff5dbfc205c3f5b6592b906020015b60405180910390a150565b60065460009060ff1661095f5750600161096d565b61096a600285610bf9565b90505b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146109fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60606103496002610cef565b610a8a610c4a565b610a95600282610ccd565b50610aa1600482610c28565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527f337cd0f3f594112b6d830afb510072d3b08556b446514f73b8109162fd1151e19060200161093f565b610af0610c4a565b805160068054602080850180517fffffffffffffffffffffff0000000000000000000000000000000000000000009093169415157fffffffffffffffffffffff0000000000000000000000000000000000000000ff81169590951761010073ffffffffffffffffffffffffffffffffffffffff9485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a910161093f565b60065460009060ff16610bb857506000919050565b6104b6600483610bf9565b60006103496002610be3565b610bd7610c4a565b610be081610cfc565b50565b60006104b6825490565b600061096d8383610df1565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600183016020526040812054151561096d565b600061096d8373ffffffffffffffffffffffffffffffffffffffff8416610e1b565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ccb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016109f1565b565b600061096d8373ffffffffffffffffffffffffffffffffffffffff8416610e6a565b6060600061096d83610f5d565b3373ffffffffffffffffffffffffffffffffffffffff821603610d7b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016109f1565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000826000018281548110610e0857610e08611358565b9060005260206000200154905092915050565b6000818152600183016020526040812054610e62575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556104b6565b5060006104b6565b60008181526001830160205260408120548015610f53576000610e8e6001836113bf565b8554909150600090610ea2906001906113bf565b9050818114610f07576000866000018281548110610ec257610ec2611358565b9060005260206000200154905080876000018481548110610ee557610ee5611358565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080610f1857610f186113d2565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506104b6565b60009150506104b6565b606081600001805480602002602001604051908101604052809291908181526020018280548015610fad57602002820191906000526020600020905b815481526020019060010190808311610f99575b50505050509050919050565b803567ffffffffffffffff81168114610fd157600080fd5b919050565b60008060408385031215610fe957600080fd5b610ff283610fb9565b915061100060208401610fb9565b90509250929050565b6020808252825182820181905260009190848201906040850190845b8181101561105757835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101611025565b50909695505050505050565b600060208083528351808285015260005b8181101561109057858101830151858201604001528201611074565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610fd157600080fd5b600080600080600060a0868803121561110b57600080fd5b611114866110cf565b9450611122602087016110cf565b93506040860135925060608601359150608086013560ff8116811461114657600080fd5b809150509295509295909350565b60006020828403121561116657600080fd5b61096d826110cf565b60008060006040848603121561118457600080fd5b61118d846110cf565b9250602084013567ffffffffffffffff808211156111aa57600080fd5b818601915086601f8301126111be57600080fd5b8135818111156111cd57600080fd5b8760208285010111156111df57600080fd5b6020830194508093505050509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006040828403121561123357600080fd5b6040516040810181811067ffffffffffffffff8211171561127d577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040528235801515811461129057600080fd5b815261129e602084016110cf565b60208201529392505050565b600080604083850312156112bd57600080fd5b6112c6836110cf565b9150611000602084016110cf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b67ffffffffffffffff8281168282160390808211156104b4576104b46112d4565b67ffffffffffffffff8181168382160190808211156104b4576104b46112d4565b808201808211156104b6576104b66112d4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113b8576113b86112d4565b5060010190565b818103818111156104b6576104b66112d4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe46756e6374696f6e73205465726d73206f66205365727669636520416c6c6f77204c6973742076312e312e30a164736f6c6343000813000a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a7a82dd06901f29ab14af63faf3358ad101724a8000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061011b5760003560e01c8063817ef62e116100b2578063a39b06e311610081578063c3f909d411610066578063c3f909d4146102c3578063cc7ebf4914610322578063f2fde38b1461032a57600080fd5b8063a39b06e314610237578063a5e1d61d146102b057600080fd5b8063817ef62e146101e157806382184c7b146101e957806389f9a2c4146101fc5780638da5cb5b1461020f57600080fd5b80633908c4d4116100ee5780633908c4d41461018e57806347663acb146101a35780636b14daf8146101b657806379ba5097146101d957600080fd5b806301a05958146101205780630a8c9c2414610146578063181f5a771461016657806320229a861461017b575b600080fd5b61012861033d565b60405167ffffffffffffffff90911681526020015b60405180910390f35b610159610154366004610fd6565b61034e565b60405161013d9190611009565b61016e6104bc565b60405161013d9190611063565b610159610189366004610fd6565b6104d8565b6101a161019c3660046110f3565b61063e565b005b6101a16101b1366004611154565b6108e9565b6101c96101c436600461116f565b61094a565b604051901515815260200161013d565b6101a1610974565b610159610a76565b6101a16101f7366004611154565b610a82565b6101a161020a366004611221565b610ae8565b60005460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161013d565b6102a26102453660046112aa565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015283901b16603482015260009060480160405160208183030381529060405280519060200120905092915050565b60405190815260200161013d565b6101c96102be366004611154565b610ba3565b60408051808201825260008082526020918201528151808301835260065460ff8116151580835273ffffffffffffffffffffffffffffffffffffffff61010090920482169284019283528451908152915116918101919091520161013d565b610128610bc3565b6101a1610338366004611154565b610bcf565b60006103496004610be3565b905090565b60608167ffffffffffffffff168367ffffffffffffffff16118061038557506103776002610be3565b8267ffffffffffffffff1610155b8061039757506103956002610be3565b155b156103ce576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6103d88383611303565b6103e3906001611324565b67ffffffffffffffff1667ffffffffffffffff811115610405576104056111f2565b60405190808252806020026020018201604052801561042e578160200160208202803683370190505b50905060005b61043e8484611303565b67ffffffffffffffff1681116104b45761046d6104658267ffffffffffffffff8716611345565b600290610bed565b82828151811061047f5761047f611358565b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101526104ad81611387565b9050610434565b505b92915050565b6040518060600160405280602c8152602001611402602c913981565b60608167ffffffffffffffff168367ffffffffffffffff16118061050f57506105016004610be3565b8267ffffffffffffffff1610155b80610521575061051f6004610be3565b155b15610558576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105628383611303565b61056d906001611324565b67ffffffffffffffff1667ffffffffffffffff81111561058f5761058f6111f2565b6040519080825280602002602001820160405280156105b8578160200160208202803683370190505b50905060005b6105c88484611303565b67ffffffffffffffff1681116104b4576105f76105ef8267ffffffffffffffff8716611345565b600490610bed565b82828151811061060957610609611358565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015261063781611387565b90506105be565b610649600485610bf9565b15610680576040517f62b7a34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051606087811b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009081166020808501919091529188901b16603483015282516028818403018152604890920190925280519101206000906040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c810191909152605c01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201206006546000855291840180845281905260ff8616928401929092526060830187905260808301869052909250610100900473ffffffffffffffffffffffffffffffffffffffff169060019060a0016020604051602081039080840390855afa1580156107b4573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff161461080b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff861614158061085057503373ffffffffffffffffffffffffffffffffffffffff8716148015906108505750333b155b15610887576040517f381cfcbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610892600286610c28565b156108e15760405173ffffffffffffffffffffffffffffffffffffffff861681527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db49060200160405180910390a15b505050505050565b6108f1610c4a565b6108fc600482610ccd565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527f28bbd0761309a99e8fb5e5d02ada0b7b2db2e5357531ff5dbfc205c3f5b6592b906020015b60405180910390a150565b60065460009060ff1661095f5750600161096d565b61096a600285610bf9565b90505b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146109fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60606103496002610cef565b610a8a610c4a565b610a95600282610ccd565b50610aa1600482610c28565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527f337cd0f3f594112b6d830afb510072d3b08556b446514f73b8109162fd1151e19060200161093f565b610af0610c4a565b805160068054602080850180517fffffffffffffffffffffff0000000000000000000000000000000000000000009093169415157fffffffffffffffffffffff0000000000000000000000000000000000000000ff81169590951761010073ffffffffffffffffffffffffffffffffffffffff9485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a910161093f565b60065460009060ff16610bb857506000919050565b6104b6600483610bf9565b60006103496002610be3565b610bd7610c4a565b610be081610cfc565b50565b60006104b6825490565b600061096d8383610df1565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600183016020526040812054151561096d565b600061096d8373ffffffffffffffffffffffffffffffffffffffff8416610e1b565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ccb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016109f1565b565b600061096d8373ffffffffffffffffffffffffffffffffffffffff8416610e6a565b6060600061096d83610f5d565b3373ffffffffffffffffffffffffffffffffffffffff821603610d7b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016109f1565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000826000018281548110610e0857610e08611358565b9060005260206000200154905092915050565b6000818152600183016020526040812054610e62575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556104b6565b5060006104b6565b60008181526001830160205260408120548015610f53576000610e8e6001836113bf565b8554909150600090610ea2906001906113bf565b9050818114610f07576000866000018281548110610ec257610ec2611358565b9060005260206000200154905080876000018481548110610ee557610ee5611358565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080610f1857610f186113d2565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506104b6565b60009150506104b6565b606081600001805480602002602001604051908101604052809291908181526020018280548015610fad57602002820191906000526020600020905b815481526020019060010190808311610f99575b50505050509050919050565b803567ffffffffffffffff81168114610fd157600080fd5b919050565b60008060408385031215610fe957600080fd5b610ff283610fb9565b915061100060208401610fb9565b90509250929050565b6020808252825182820181905260009190848201906040850190845b8181101561105757835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101611025565b50909695505050505050565b600060208083528351808285015260005b8181101561109057858101830151858201604001528201611074565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610fd157600080fd5b600080600080600060a0868803121561110b57600080fd5b611114866110cf565b9450611122602087016110cf565b93506040860135925060608601359150608086013560ff8116811461114657600080fd5b809150509295509295909350565b60006020828403121561116657600080fd5b61096d826110cf565b60008060006040848603121561118457600080fd5b61118d846110cf565b9250602084013567ffffffffffffffff808211156111aa57600080fd5b818601915086601f8301126111be57600080fd5b8135818111156111cd57600080fd5b8760208285010111156111df57600080fd5b6020830194508093505050509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006040828403121561123357600080fd5b6040516040810181811067ffffffffffffffff8211171561127d577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040528235801515811461129057600080fd5b815261129e602084016110cf565b60208201529392505050565b600080604083850312156112bd57600080fd5b6112c6836110cf565b9150611000602084016110cf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b67ffffffffffffffff8281168282160390808211156104b4576104b46112d4565b67ffffffffffffffff8181168382160190808211156104b4576104b46112d4565b808201808211156104b6576104b66112d4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036113b8576113b86112d4565b5060010190565b818103818111156104b6576104b66112d4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe46756e6374696f6e73205465726d73206f66205365727669636520416c6c6f77204c6973742076312e312e30a164736f6c6343000813000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a7a82dd06901f29ab14af63faf3358ad101724a8000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : config (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [1] : 000000000000000000000000a7a82dd06901f29ab14af63faf3358ad101724a8
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.