Source Code
Overview
ETH Balance
0 ETH
Token Holdings
More Info
ContractCreator
Multichain Info
N/A
Latest 25 from a total of 1,157 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Accept Terms Of ... | 6452840 | 468 days ago | IN | 0 ETH | 0.00175636 | ||||
| Accept Terms Of ... | 6452289 | 468 days ago | IN | 0 ETH | 0.00105307 | ||||
| Accept Terms Of ... | 6446902 | 469 days ago | IN | 0 ETH | 0.00575562 | ||||
| Accept Terms Of ... | 6442107 | 470 days ago | IN | 0 ETH | 0.00355598 | ||||
| Accept Terms Of ... | 6441346 | 470 days ago | IN | 0 ETH | 0.00221942 | ||||
| Accept Terms Of ... | 6439684 | 470 days ago | IN | 0 ETH | 0.00023172 | ||||
| Accept Terms Of ... | 6439004 | 470 days ago | IN | 0 ETH | 0.00046108 | ||||
| Accept Terms Of ... | 6429050 | 472 days ago | IN | 0 ETH | 0.00093536 | ||||
| Accept Terms Of ... | 6417447 | 474 days ago | IN | 0 ETH | 0.00052879 | ||||
| Accept Terms Of ... | 6407412 | 475 days ago | IN | 0 ETH | 0.00036234 | ||||
| Accept Terms Of ... | 6406994 | 475 days ago | IN | 0 ETH | 0.00012805 | ||||
| Accept Terms Of ... | 6405313 | 476 days ago | IN | 0 ETH | 0.00017639 | ||||
| Accept Terms Of ... | 6400495 | 476 days ago | IN | 0 ETH | 0.00012224 | ||||
| Accept Terms Of ... | 6400494 | 476 days ago | IN | 0 ETH | 0.00027227 | ||||
| Accept Terms Of ... | 6398526 | 477 days ago | IN | 0 ETH | 0.0004519 | ||||
| Accept Terms Of ... | 6398523 | 477 days ago | IN | 0 ETH | 0.00108872 | ||||
| Accept Terms Of ... | 6398257 | 477 days ago | IN | 0 ETH | 0.00303235 | ||||
| Accept Terms Of ... | 6392409 | 478 days ago | IN | 0 ETH | 0.00096729 | ||||
| Accept Terms Of ... | 6392220 | 478 days ago | IN | 0 ETH | 0.00030392 | ||||
| Accept Terms Of ... | 6389918 | 478 days ago | IN | 0 ETH | 0.00107778 | ||||
| Accept Terms Of ... | 6388498 | 478 days ago | IN | 0 ETH | 0.0001147 | ||||
| Accept Terms Of ... | 6386429 | 479 days ago | IN | 0 ETH | 0.00014515 | ||||
| Accept Terms Of ... | 6386008 | 479 days ago | IN | 0 ETH | 0.00099792 | ||||
| Accept Terms Of ... | 6382091 | 479 days ago | IN | 0 ETH | 0.00025673 | ||||
| Accept Terms Of ... | 6381919 | 479 days ago | IN | 0 ETH | 0.00014639 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
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} 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.0/contracts/utils/Address.sol";
import {EnumerableSet} from "../../../../vendor/openzeppelin-solidity/v4.8.0/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
string public constant override typeAndVersion = "Functions Terms of Service Allow List v1.0.0";
EnumerableSet.AddressSet private s_allowedSenders;
mapping(address => bool) private s_blockedSenders;
event AddedAccess(address user);
event BlockedAccess(address user);
event UnblockedAccess(address user);
error InvalidSignature();
error InvalidUsage();
error RecipientIsBlocked();
// ================================================================
// | Configuration state |
// ================================================================
struct Config {
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
}
Config private s_config;
event ConfigUpdated(Config config);
// ================================================================
// | Initialization |
// ================================================================
constructor(Config memory config) ConfirmedOwner(msg.sender) {
updateConfig(config);
}
// ================================================================
// | Configuration |
// ================================================================
/// @notice Gets the contracts's configuration
/// @return config
function getConfig() external view returns (Config memory) {
return s_config;
}
/// @notice Sets the contracts's configuration
/// @param config - See the contents of the TermsOfServiceAllowList.Config struct for more information
function updateConfig(Config 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[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
s_allowedSenders.add(recipient);
emit AddedAccess(recipient);
}
/// @inheritdoc ITermsOfServiceAllowList
function getAllAllowedSenders() external view override returns (address[] memory) {
return s_allowedSenders.values();
}
/// @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[sender];
}
/// @inheritdoc ITermsOfServiceAllowList
function blockSender(address sender) external override onlyOwner {
s_allowedSenders.remove(sender);
s_blockedSenders[sender] = true;
emit BlockedAccess(sender);
}
/// @inheritdoc ITermsOfServiceAllowList
function unblockSender(address sender) external override onlyOwner {
s_blockedSenders[sender] = false;
emit UnblockedAccess(sender);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {IFunctionsSubscriptions} from "./interfaces/IFunctionsSubscriptions.sol";
import {AggregatorV3Interface} from "../../../interfaces/AggregatorV3Interface.sol";
import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol";
import {Routable} from "./Routable.sol";
import {FunctionsResponse} from "./libraries/FunctionsResponse.sol";
import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol";
/// @title Functions Billing contract
/// @notice Contract that calculates payment from users to the nodes of the Decentralized Oracle Network (DON).
/// @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD.
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
// ================================================================
// | Request Commitment state |
// ================================================================
mapping(bytes32 requestId => bytes32 commitmentHash) private s_requestCommitments;
event CommitmentDeleted(bytes32 requestId);
// ================================================================
// | Configuration state |
// ================================================================
struct Config {
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.
uint32 requestTimeoutSeconds; // ║ How many seconds it takes before we consider a request to be timed out
uint72 donFee; // ║ Additional flat fee (in Juels of LINK) that will be split between Node Operators. Max value is 2^80 - 1 == 1.2m LINK.
uint16 maxSupportedRequestDataVersion; // ═══════╝ The highest support request data version supported by the node. All lower versions should also be supported.
uint224 fallbackNativePerUnitLink; // ═══════════╸ fallback NATIVE CURRENCY / LINK conversion rate if the data feed is stale
}
Config private s_config;
event ConfigUpdated(Config config);
error UnsupportedRequestDataVersion();
error InsufficientBalance();
error InvalidSubscription();
error UnauthorizedSender();
error MustBeSubOwner(address owner);
error InvalidLinkWeiPrice(int256 linkWei);
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;
// ================================================================
// | Initialization |
// ================================================================
constructor(address router, Config memory config, address linkToNativeFeed) Routable(router) {
s_linkToNativeFeed = AggregatorV3Interface(linkToNativeFeed);
updateConfig(config);
}
// ================================================================
// | Configuration |
// ================================================================
/// @notice Gets the Chainlink Coordinator's billing configuration
/// @return config
function getConfig() external view returns (Config memory) {
return s_config;
}
/// @notice Sets the Chainlink Coordinator's billing configuration
/// @param config - See the contents of the Config struct in IFunctionsBilling.Config for more information
function updateConfig(Config memory config) public {
_onlyOwner();
s_config = config;
emit ConfigUpdated(config);
}
// ================================================================
// | Fee Calculation |
// ================================================================
/// @inheritdoc IFunctionsBilling
function getDONFee(bytes memory /* requestData */) public view override returns (uint72) {
return s_config.donFee;
}
/// @inheritdoc IFunctionsBilling
function getAdminFee() public view override returns (uint72) {
return _getRouter().getAdminFee();
}
/// @inheritdoc IFunctionsBilling
function getWeiPerUnitLink() public view returns (uint256) {
Config memory config = s_config;
(, int256 weiPerUnitLink, , uint256 timestamp, ) = s_linkToNativeFeed.latestRoundData();
// solhint-disable-next-line not-rely-on-time
if (config.feedStalenessSeconds < block.timestamp - timestamp && config.feedStalenessSeconds > 0) {
return config.fallbackNativePerUnitLink;
}
if (weiPerUnitLink <= 0) {
revert InvalidLinkWeiPrice(weiPerUnitLink);
}
return uint256(weiPerUnitLink);
}
function _getJuelsPerGas(uint256 gasPriceWei) private view returns (uint96) {
// (1e18 juels/link) * (wei/gas) / (wei/link) = juels per gas
// There are only 1e9*1e18 = 1e27 juels in existence, should not exceed uint96 (2^96 ~ 7e28)
return SafeCast.toUint96((1e18 * gasPriceWei) / getWeiPerUnitLink());
}
// ================================================================
// | 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 = getAdminFee();
uint72 donFee = getDONFee(data);
return _calculateCostEstimate(callbackGasLimit, gasPriceWei, donFee, adminFee);
}
/// @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 donFee,
uint72 adminFee
) internal view returns (uint96) {
uint256 executionGas = s_config.gasOverheadBeforeCallback + s_config.gasOverheadAfterCallback + callbackGasLimit;
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
uint96 juelsPerGas = _getJuelsPerGas(gasPriceWithOverestimation);
uint256 estimatedGasReimbursement = juelsPerGas * executionGas;
uint96 fees = uint96(donFee) + uint96(adminFee);
return SafeCast.toUint96(estimatedGasReimbursement + fees);
}
// ================================================================
// | 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) {
Config memory config = s_config;
// Nodes should support all past versions of the structure
if (request.dataVersion > config.maxSupportedRequestDataVersion) {
revert UnsupportedRequestDataVersion();
}
uint72 donFee = getDONFee(request.data);
uint96 estimatedTotalCostJuels = _calculateCostEstimate(
request.callbackGasLimit,
tx.gasprice,
donFee,
request.adminFee
);
// Check that subscription can afford the estimated cost
if ((request.availableBalance) < estimatedTotalCostJuels) {
revert InsufficientBalance();
}
bytes32 requestId = _computeRequestId(
address(this),
request.requestingContract,
request.subscriptionId,
request.initiatedRequests + 1
);
commitment = FunctionsResponse.Commitment({
adminFee: request.adminFee,
coordinator: address(this),
client: request.requestingContract,
subscriptionId: request.subscriptionId,
callbackGasLimit: request.callbackGasLimit,
estimatedTotalCostJuels: estimatedTotalCostJuels,
timeoutTimestamp: uint32(block.timestamp + config.requestTimeoutSeconds),
requestId: requestId,
donFee: donFee,
gasOverheadBeforeCallback: config.gasOverheadBeforeCallback,
gasOverheadAfterCallback: config.gasOverheadAfterCallback
});
s_requestCommitments[requestId] = keccak256(abi.encode(commitment));
return commitment;
}
/// @notice Generate a keccak hash request ID
/// @dev uses the number of requests that the consumer of a subscription has sent as a nonce
function _computeRequestId(
address don,
address client,
uint64 subscriptionId,
uint64 nonce
) private pure returns (bytes32) {
return keccak256(abi.encode(don, client, subscriptionId, nonce));
}
/// @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
/// @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 */
) internal returns (FunctionsResponse.FulfillResult) {
FunctionsResponse.Commitment memory commitment = abi.decode(onchainMetadata, (FunctionsResponse.Commitment));
if (s_requestCommitments[requestId] == bytes32(0)) {
return FunctionsResponse.FulfillResult.INVALID_REQUEST_ID;
}
if (s_requestCommitments[requestId] != keccak256(abi.encode(commitment))) {
return FunctionsResponse.FulfillResult.INVALID_COMMITMENT;
}
uint96 juelsPerGas = _getJuelsPerGas(tx.gasprice);
// Gas overhead without callback
uint96 gasOverheadJuels = juelsPerGas *
(commitment.gasOverheadBeforeCallback + commitment.gasOverheadAfterCallback);
// The Functions Router will perform the callback to the client contract
(FunctionsResponse.FulfillResult resultCode, uint96 callbackCostJuels) = _getRouter().fulfill(
response,
err,
juelsPerGas,
gasOverheadJuels + commitment.donFee, // costWithoutFulfillment
msg.sender,
commitment
);
// 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;
}
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();
if (transmitters.length == 0) {
revert NoTransmittersSet();
}
uint96 feePoolShare = s_feePool / uint96(transmitters.length);
// Bounded by "maxNumOracles" on OCR2Abstract.sol
for (uint256 i = 0; i < transmitters.length; ++i) {
s_withdrawableTokens[transmitters[i]] += feePoolShare;
}
s_feePool -= feePoolShare * uint96(transmitters.length);
}
// Overriden in FunctionsCoordinator.sol
function _onlyOwner() internal view virtual;
}// 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_router;
event RequestSent(bytes32 indexed id);
event RequestFulfilled(bytes32 indexed id);
error OnlyRouterCanFulfill();
constructor(address router) {
i_router = 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_router.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_router)) {
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 {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol";
import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol";
import {FunctionsBilling} 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
/// @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD.
contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilling {
using FunctionsResponse for FunctionsResponse.RequestMeta;
using FunctionsResponse for FunctionsResponse.Commitment;
using FunctionsResponse for FunctionsResponse.FulfillResult;
/// @inheritdoc ITypeAndVersion
string public constant override typeAndVersion = "Functions Coordinator v1.0.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,
Config memory config,
address linkToNativeFeed
) OCR2Base(true) FunctionsBilling(router, config, linkToNativeFeed) {}
/// @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) {
address[] memory nodes = s_transmitters;
// Bounded by "maxNumOracles" on OCR2Abstract.sol
for (uint256 i = 0; i < nodes.length; ++i) {
if (nodes[i] == node) {
return true;
}
}
return false;
}
/// @inheritdoc IFunctionsCoordinator
function startRequest(
FunctionsResponse.RequestMeta calldata request
) external override onlyRouter returns (FunctionsResponse.Commitment memory commitment) {
commitment = _startBilling(request);
emit OracleRequest(
commitment.requestId,
request.requestingContract,
tx.origin,
request.subscriptionId,
request.subscriptionOwner,
request.data,
request.dataVersion,
request.flags,
request.callbackGasLimit,
commitment
);
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;
}
/// @dev Report hook called within OCR2Base.sol
function _report(
uint256 /*initialGas*/,
address /*transmitter*/,
uint8 /*signerCount*/,
address[MAX_NUM_ORACLES] memory /*signers*/,
bytes calldata report
) internal override {
bytes32[] memory requestIds;
bytes[] memory results;
bytes[] memory errors;
bytes[] memory onchainMetadata;
bytes[] memory offchainMetadata;
(requestIds, results, errors, onchainMetadata, offchainMetadata) = abi.decode(
report,
(bytes32[], bytes[], bytes[], bytes[], bytes[])
);
if (
requestIds.length == 0 ||
requestIds.length != results.length ||
requestIds.length != errors.length ||
requestIds.length != onchainMetadata.length ||
requestIds.length != offchainMetadata.length
) {
revert ReportInvalid();
}
// Bounded by "MaxRequestBatchSize" on the Job's ReportingPluginConfig
for (uint256 i = 0; i < requestIds.length; ++i) {
FunctionsResponse.FulfillResult result = FunctionsResponse.FulfillResult(
_fulfillAndBill(requestIds[i], results[i], errors[i], onchainMetadata[i], offchainMetadata[i])
);
// 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(requestIds[i], msg.sender);
}
}
}
/// @dev Used in FunctionsBilling.sol
function _onlyOwner() internal view override {
_validateOwnership();
}
}// 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.0/contracts/utils/math/SafeCast.sol";
import {Pausable} from "../../../vendor/openzeppelin-solidity/v4.8.0/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;
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 GAS_FLAG_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,
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 costWithoutCallback,
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 + costWithoutCallback + 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),
costWithoutCallback
);
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;
// solhint-disable-next-line no-inline-assembly
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);
// solhint-disable-next-line no-inline-assembly
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 {LinkTokenInterface} from "../../../shared/interfaces/LinkTokenInterface.sol";
import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol";
import {IFunctionsRouter} from "./interfaces/IFunctionsRouter.sol";
import {FunctionsResponse} from "./libraries/FunctionsResponse.sol";
import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol";
import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol";
/// @title Functions Subscriptions contract
/// @notice Contract that coordinates payment from users to the nodes of the Decentralized Oracle Network (DON).
/// @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD.
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_router;
error RouterMustBeSet();
error OnlyCallableByRouter();
error OnlyCallableByRouterOwner();
/// @dev Initializes the contract.
constructor(address router) {
if (router == address(0)) {
revert RouterMustBeSet();
}
i_router = IOwnableFunctionsRouter(router);
}
/// @notice Return the Router
function _getRouter() internal view returns (IOwnableFunctionsRouter router) {
return i_router;
}
/// @notice Reverts if called by anyone other than the router.
modifier onlyRouter() {
if (msg.sender != address(i_router)) {
revert OnlyCallableByRouter();
}
_;
}
/// @notice Reverts if called by anyone other than the router owner.
modifier onlyRouterOwner() {
if (msg.sender != i_router.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 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;
}// 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 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 getDONFee(bytes memory requestCBOR) 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 getAdminFee() 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
function oracleWithdrawAll() external;
}// 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 {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;
import {IFunctionsSubscriptions} from "../interfaces/IFunctionsSubscriptions.sol";
/// @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);
function _configDigestFromConfigData(
uint256 chainId,
address contractAddress,
uint64 configCount,
address[] memory signers,
address[] memory transmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) internal pure returns (bytes32) {
uint256 h = uint256(
keccak256(
abi.encode(
chainId,
contractAddress,
configCount,
signers,
transmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig
)
)
);
uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00
uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00
return bytes32((prefix & prefixMask) | (h & ~prefixMask));
}
/**
* @notice optionally 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 THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD.
* @dev For details on its operation, see the offchain reporting protocol design
* doc, which refers to this contract as simply the "contract".
* @dev This contract is meant to aid rapid development of new applications based on OCR2.
* However, for actual production contracts, it is expected that most of the logic of this contract
* will be folded directly into the application contract. Inheritance prevents us from doing lots
* of juicy storage layout optimizations, leading to a substantial increase in gas cost.
*/
abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract {
error ReportInvalid();
error InvalidConfig(string message);
bool internal immutable i_uniqueReports;
constructor(bool uniqueReports) ConfirmedOwner(msg.sender) {
i_uniqueReports = uniqueReports;
}
uint256 private constant maxUint32 = (1 << 32) - 1;
// 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;
/*
* 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++) {
// add new signer/transmitter addresses
require(s_oracles[args.signers[i]].role == Role.Unset, "repeated signer address");
s_oracles[args.signers[i]] = Oracle(uint8(i), Role.Signer);
require(s_oracles[args.transmitters[i]].role == Role.Unset, "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 initialGas the amount of gas before validation
* @param transmitter the address of the account that submitted the report
* @param signers the addresses of all signing accounts
* @param report serialized report
*/
function _report(
uint256 initialGas,
address transmitter,
uint8 signerCount,
address[MAX_NUM_ORACLES] memory signers,
bytes calldata report
) 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
require(msg.data.length == expected, "calldata length mismatch");
}
/**
* @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 {
uint256 initialGas = gasleft(); // This line must come first
{
// 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));
ConfigInfo memory configInfo = s_configInfo;
require(configInfo.latestConfigDigest == configDigest, "configDigest mismatch");
requireExpectedMsgDataLength(report, rs, ss);
uint256 expectedNumSignatures;
if (i_uniqueReports) {
expectedNumSignatures = (configInfo.n + configInfo.f) / 2 + 1;
} else {
expectedNumSignatures = configInfo.f + 1;
}
require(rs.length == expectedNumSignatures, "wrong number of signatures");
require(rs.length == ss.length, "signatures out of registration");
Oracle memory transmitter = s_oracles[msg.sender];
require( // Check that sender is authorized to report
transmitter.role == Role.Transmitter && msg.sender == s_transmitters[transmitter.index],
"unauthorized transmitter"
);
}
address[MAX_NUM_ORACLES] memory signed;
uint8 signerCount = 0;
{
// 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];
require(o.role == Role.Signer, "address not authorized to sign");
require(signed[o.index] == address(0), "non-unique signature");
signed[o.index] = signer;
signerCount += 1;
}
}
_report(initialGas, msg.sender, signerCount, signed, report);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {FunctionsRequest} from "../../../dev/1_0_0/libraries/FunctionsRequest.sol";
import {FunctionsClient} from "../../../dev/1_0_0/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);
}
/**
* @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_router.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;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./ConfirmedOwnerWithProposal.sol";
/**
* @title The ConfirmedOwner contract
* @notice A contract with helpers for basic contract ownership.
*/
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../interfaces/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) {
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
/**
* @notice Allows an owner to begin transferring ownership to a new address,
* pending.
*/
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
/**
* @notice Allows an ownership transfer to be completed by the recipient.
*/
function acceptOwnership() external override {
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/**
* @notice Get the current owner
*/
function owner() public view override returns (address) {
return s_owner;
}
/**
* @notice validate, transfer ownership, and emit relevant events
*/
function _transferOwnership(address to) private {
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/**
* @notice validate access
*/
function _validateOwnership() internal view {
require(msg.sender == s_owner, "Only callable by owner");
}
/**
* @notice Reverts if called by anyone other than the contract owner.
*/
modifier onlyOwner() {
_validateOwnership();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
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);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface LinkTokenInterface {
function allowance(address owner, address spender) external view returns (uint256 remaining);
function approve(address spender, uint256 value) external returns (bool success);
function balanceOf(address owner) external view returns (uint256 balance);
function decimals() external view returns (uint8 decimalPlaces);
function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
function increaseApproval(address spender, uint256 subtractedValue) external;
function name() external view returns (string memory tokenName);
function symbol() external view returns (string memory tokenSymbol);
function totalSupply() external view returns (uint256 totalTokensIssued);
function transfer(address to, uint256 value) external returns (bool success);
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);
function transferFrom(address from, address to, uint256 value) external returns (bool success);
}// SPDX-License-Identifier: 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
// 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/0.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-contracts/=foundry-lib/openzeppelin-contracts/contracts/",
"@openzeppelin/=node_modules/@openzeppelin/",
"hardhat/=node_modules/hardhat/",
"@eth-optimism/=node_modules/@eth-optimism/",
"erc4626-tests/=foundry-lib/openzeppelin-contracts/lib/erc4626-tests/"
],
"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 ABI
API[{"inputs":[{"components":[{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"address","name":"signerPublicKey","type":"address"}],"internalType":"struct TermsOfServiceAllowList.Config","name":"config","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"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 TermsOfServiceAllowList.Config","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":"getConfig","outputs":[{"components":[{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"address","name":"signerPublicKey","type":"address"}],"internalType":"struct TermsOfServiceAllowList.Config","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 TermsOfServiceAllowList.Config","name":"config","type":"tuple"}],"name":"updateConfig","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60806040523480156200001157600080fd5b50604051620012c4380380620012c4833981016040819052620000349162000269565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d9565b505050620000d2816200018460201b60201c565b50620002ea565b336001600160a01b03821603620001335760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6200018e6200020b565b805160058054602080850180516001600160a81b0319909316941515610100600160a81b03198116959095176101006001600160a01b039485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a910160405180910390a150565b6000546001600160a01b03163314620002675760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b6000604082840312156200027c57600080fd5b604080519081016001600160401b0381118282101715620002ad57634e487b7160e01b600052604160045260246000fd5b60405282518015158114620002c157600080fd5b815260208301516001600160a01b0381168114620002de57600080fd5b60208201529392505050565b610fca80620002fa6000396000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c806382184c7b1161008c578063a39b06e311610066578063a39b06e3146101b8578063a5e1d61d146101d9578063c3f909d4146101ec578063f2fde38b1461024b57600080fd5b806382184c7b1461016a57806389f9a2c41461017d5780638da5cb5b1461019057600080fd5b80636b14daf8116100bd5780636b14daf81461012a57806379ba50971461014d578063817ef62e1461015557600080fd5b8063181f5a77146100e45780633908c4d41461010257806347663acb14610117575b600080fd5b6100ec61025e565b6040516100f99190610c4f565b60405180910390f35b610115610110366004610ce4565b61027a565b005b610115610125366004610d45565b6104f0565b61013d610138366004610d60565b61057b565b60405190151581526020016100f9565b6101156105a5565b61015d6106a7565b6040516100f99190610de3565b610115610178366004610d45565b6106b8565b61011561018b366004610e3d565b61074b565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6101cb6101c6366004610ec6565b610806565b6040519081526020016100f9565b61013d6101e7366004610d45565b610864565b60408051808201825260008082526020918201528151808301835260055460ff8116151580835273ffffffffffffffffffffffffffffffffffffffff6101009092048216928401928352845190815291511691810191909152016100f9565b610115610259366004610d45565b6108a5565b6040518060600160405280602c8152602001610f92602c913981565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602052604090205460ff16156102da576040517f62b7a34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006102e68686610806565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c810191909152605c01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201206005546000855291840180845281905260ff8616928401929092526060830187905260808301869052909250610100900473ffffffffffffffffffffffffffffffffffffffff169060019060a0016020604051602081039080840390855afa1580156103c0573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff1614610417576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff861614158061045c57503373ffffffffffffffffffffffffffffffffffffffff87161480159061045c5750333b155b15610493576040517f381cfcbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61049e6002866108b9565b5060405173ffffffffffffffffffffffffffffffffffffffff861681527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db49060200160405180910390a1505050505050565b6104f86108db565b73ffffffffffffffffffffffffffffffffffffffff811660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f28bbd0761309a99e8fb5e5d02ada0b7b2db2e5357531ff5dbfc205c3f5b6592b91015b60405180910390a150565b60055460009060ff166105905750600161059e565b61059b60028561095e565b90505b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461062b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60606106b3600261098d565b905090565b6106c06108db565b6106cb60028261099a565b5073ffffffffffffffffffffffffffffffffffffffff811660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f337cd0f3f594112b6d830afb510072d3b08556b446514f73b8109162fd1151e19101610570565b6107536108db565b805160058054602080850180517fffffffffffffffffffffff0000000000000000000000000000000000000000009093169415157fffffffffffffffffffffff0000000000000000000000000000000000000000ff81169590951761010073ffffffffffffffffffffffffffffffffffffffff9485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a9101610570565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015283901b1660348201526000906048016040516020818303038152906040528051906020012090505b92915050565b60055460009060ff1661087957506000919050565b5073ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205460ff1690565b6108ad6108db565b6108b6816109bc565b50565b600061059e8373ffffffffffffffffffffffffffffffffffffffff8416610ab1565b60005473ffffffffffffffffffffffffffffffffffffffff16331461095c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610622565b565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600183016020526040812054151561059e565b6060600061059e83610b00565b600061059e8373ffffffffffffffffffffffffffffffffffffffff8416610b5c565b3373ffffffffffffffffffffffffffffffffffffffff821603610a3b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610622565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054610af85750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561085e565b50600061085e565b606081600001805480602002602001604051908101604052809291908181526020018280548015610b5057602002820191906000526020600020905b815481526020019060010190808311610b3c575b50505050509050919050565b60008181526001830160205260408120548015610c45576000610b80600183610ef9565b8554909150600090610b9490600190610ef9565b9050818114610bf9576000866000018281548110610bb457610bb4610f33565b9060005260206000200154905080876000018481548110610bd757610bd7610f33565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080610c0a57610c0a610f62565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061085e565b600091505061085e565b600060208083528351808285015260005b81811015610c7c57858101830151858201604001528201610c60565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610cdf57600080fd5b919050565b600080600080600060a08688031215610cfc57600080fd5b610d0586610cbb565b9450610d1360208701610cbb565b93506040860135925060608601359150608086013560ff81168114610d3757600080fd5b809150509295509295909350565b600060208284031215610d5757600080fd5b61059e82610cbb565b600080600060408486031215610d7557600080fd5b610d7e84610cbb565b9250602084013567ffffffffffffffff80821115610d9b57600080fd5b818601915086601f830112610daf57600080fd5b813581811115610dbe57600080fd5b876020828501011115610dd057600080fd5b6020830194508093505050509250925092565b6020808252825182820181905260009190848201906040850190845b81811015610e3157835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101610dff565b50909695505050505050565b600060408284031215610e4f57600080fd5b6040516040810181811067ffffffffffffffff82111715610e99577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405282358015158114610eac57600080fd5b8152610eba60208401610cbb565b60208201529392505050565b60008060408385031215610ed957600080fd5b610ee283610cbb565b9150610ef060208401610cbb565b90509250929050565b8181038181111561085e577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe46756e6374696f6e73205465726d73206f66205365727669636520416c6c6f77204c6973742076312e302e30a164736f6c6343000813000a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a7a82dd06901f29ab14af63faf3358ad101724a8
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100df5760003560e01c806382184c7b1161008c578063a39b06e311610066578063a39b06e3146101b8578063a5e1d61d146101d9578063c3f909d4146101ec578063f2fde38b1461024b57600080fd5b806382184c7b1461016a57806389f9a2c41461017d5780638da5cb5b1461019057600080fd5b80636b14daf8116100bd5780636b14daf81461012a57806379ba50971461014d578063817ef62e1461015557600080fd5b8063181f5a77146100e45780633908c4d41461010257806347663acb14610117575b600080fd5b6100ec61025e565b6040516100f99190610c4f565b60405180910390f35b610115610110366004610ce4565b61027a565b005b610115610125366004610d45565b6104f0565b61013d610138366004610d60565b61057b565b60405190151581526020016100f9565b6101156105a5565b61015d6106a7565b6040516100f99190610de3565b610115610178366004610d45565b6106b8565b61011561018b366004610e3d565b61074b565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6101cb6101c6366004610ec6565b610806565b6040519081526020016100f9565b61013d6101e7366004610d45565b610864565b60408051808201825260008082526020918201528151808301835260055460ff8116151580835273ffffffffffffffffffffffffffffffffffffffff6101009092048216928401928352845190815291511691810191909152016100f9565b610115610259366004610d45565b6108a5565b6040518060600160405280602c8152602001610f92602c913981565b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602052604090205460ff16156102da576040517f62b7a34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006102e68686610806565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c810191909152605c01604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201206005546000855291840180845281905260ff8616928401929092526060830187905260808301869052909250610100900473ffffffffffffffffffffffffffffffffffffffff169060019060a0016020604051602081039080840390855afa1580156103c0573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff1614610417576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff861614158061045c57503373ffffffffffffffffffffffffffffffffffffffff87161480159061045c5750333b155b15610493576040517f381cfcbd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61049e6002866108b9565b5060405173ffffffffffffffffffffffffffffffffffffffff861681527f87286ad1f399c8e82bf0c4ef4fcdc570ea2e1e92176e5c848b6413545b885db49060200160405180910390a1505050505050565b6104f86108db565b73ffffffffffffffffffffffffffffffffffffffff811660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905590519182527f28bbd0761309a99e8fb5e5d02ada0b7b2db2e5357531ff5dbfc205c3f5b6592b91015b60405180910390a150565b60055460009060ff166105905750600161059e565b61059b60028561095e565b90505b9392505050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461062b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60606106b3600261098d565b905090565b6106c06108db565b6106cb60028261099a565b5073ffffffffffffffffffffffffffffffffffffffff811660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590519182527f337cd0f3f594112b6d830afb510072d3b08556b446514f73b8109162fd1151e19101610570565b6107536108db565b805160058054602080850180517fffffffffffffffffffffff0000000000000000000000000000000000000000009093169415157fffffffffffffffffffffff0000000000000000000000000000000000000000ff81169590951761010073ffffffffffffffffffffffffffffffffffffffff9485160217909355604080519485529251909116908301527f0d22b8a99f411b3dd338c961284f608489ca0dab9cdad17366a343c361bcf80a9101610570565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015283901b1660348201526000906048016040516020818303038152906040528051906020012090505b92915050565b60055460009060ff1661087957506000919050565b5073ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205460ff1690565b6108ad6108db565b6108b6816109bc565b50565b600061059e8373ffffffffffffffffffffffffffffffffffffffff8416610ab1565b60005473ffffffffffffffffffffffffffffffffffffffff16331461095c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610622565b565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600183016020526040812054151561059e565b6060600061059e83610b00565b600061059e8373ffffffffffffffffffffffffffffffffffffffff8416610b5c565b3373ffffffffffffffffffffffffffffffffffffffff821603610a3b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610622565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054610af85750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561085e565b50600061085e565b606081600001805480602002602001604051908101604052809291908181526020018280548015610b5057602002820191906000526020600020905b815481526020019060010190808311610b3c575b50505050509050919050565b60008181526001830160205260408120548015610c45576000610b80600183610ef9565b8554909150600090610b9490600190610ef9565b9050818114610bf9576000866000018281548110610bb457610bb4610f33565b9060005260206000200154905080876000018481548110610bd757610bd7610f33565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080610c0a57610c0a610f62565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061085e565b600091505061085e565b600060208083528351808285015260005b81811015610c7c57858101830151858201604001528201610c60565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610cdf57600080fd5b919050565b600080600080600060a08688031215610cfc57600080fd5b610d0586610cbb565b9450610d1360208701610cbb565b93506040860135925060608601359150608086013560ff81168114610d3757600080fd5b809150509295509295909350565b600060208284031215610d5757600080fd5b61059e82610cbb565b600080600060408486031215610d7557600080fd5b610d7e84610cbb565b9250602084013567ffffffffffffffff80821115610d9b57600080fd5b818601915086601f830112610daf57600080fd5b813581811115610dbe57600080fd5b876020828501011115610dd057600080fd5b6020830194508093505050509250925092565b6020808252825182820181905260009190848201906040850190845b81811015610e3157835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101610dff565b50909695505050505050565b600060408284031215610e4f57600080fd5b6040516040810181811067ffffffffffffffff82111715610e99577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405282358015158114610eac57600080fd5b8152610eba60208401610cbb565b60208201529392505050565b60008060408385031215610ed957600080fd5b610ee283610cbb565b9150610ef060208401610cbb565b90509250929050565b8181038181111561085e577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfe46756e6374696f6e73205465726d73206f66205365727669636520416c6c6f77204c6973742076312e302e30a164736f6c6343000813000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a7a82dd06901f29ab14af63faf3358ad101724a8
-----Decoded View---------------
Arg [0] : config (tuple):
Arg [1] : enabled (bool): True
Arg [2] : signerPublicKey (address): 0xa7a82DD06901F29aB14AF63faF3358AD101724A8
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [1] : 000000000000000000000000a7a82dd06901f29ab14af63faf3358ad101724a8
Loading...
Loading
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.