diff --git a/contracts/src/v0.8/dev/VRFCoordinatorV2.sol b/contracts/src/v0.8/dev/VRFCoordinatorV2.sol index 907bb831a45..a0a5e146225 100644 --- a/contracts/src/v0.8/dev/VRFCoordinatorV2.sol +++ b/contracts/src/v0.8/dev/VRFCoordinatorV2.sol @@ -26,12 +26,19 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { error OnlyCallableFromLink(); error InvalidCalldata(); error MustBeSubOwner(address owner); + error PendingRequestExists(); error MustBeRequestedOwner(address proposedOwner); error BalanceInvariantViolated(uint256 internalBalance, uint256 externalBalance); // Should never happen event FundsRecovered(address to, uint256 amount); + // We use the subscription struct (1 word) + // at fulfillment time. struct Subscription { // There are only 1e9*1e18 = 1e27 juels in existence, so the balance can fit in uint96 (2^96 ~ 7e28) uint96 balance; // Common link balance used for all consumer requests. + uint64 reqCount; // For fee tiers + } + // We use the config for the mgmt APIs + struct SubscriptionConfig { address owner; // Owner can fund/withdraw/cancel the sub. address requestedOwner; // For safely transferring sub ownership. // Maintains the list of keys in s_consumers. @@ -42,17 +49,16 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { // consumer is valid without reading all the consumers from storage. address[] consumers; } - struct Consumer { - uint64 subId; - uint64 nonce; - } - mapping(address => mapping(uint64 => Consumer)) /* consumer */ /* subId */ + // Note a nonce of 0 indicates an the consumer is not assigned to that subscription. + mapping(address => mapping(uint64 => uint64)) /* consumer */ /* subId */ /* nonce */ private s_consumers; + mapping(uint64 => SubscriptionConfig) /* subId */ /* subscriptionConfig */ + private s_subscriptionConfigs; mapping(uint64 => Subscription) /* subId */ /* subscription */ private s_subscriptions; uint64 private s_currentSubId; // s_totalBalance tracks the total link sent to/from - // this contract through onTokenTransfer, defundSubscription, cancelSubscription and oracleWithdraw. + // this contract through onTokenTransfer, cancelSubscription and oracleWithdraw. // A discrepancy with this contract's link balance indicates someone // sent tokens using transfer and so we may need to use recoverFunds. uint96 public s_totalBalance; @@ -60,7 +66,6 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { event SubscriptionFunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance); event SubscriptionConsumerAdded(uint64 indexed subId, address consumer); event SubscriptionConsumerRemoved(uint64 indexed subId, address consumer); - event SubscriptionDefunded(uint64 indexed subId, uint256 oldBalance, uint256 newBalance); event SubscriptionCanceled(uint64 indexed subId, address to, uint256 amount); event SubscriptionOwnerTransferRequested(uint64 indexed subId, address from, address to); event SubscriptionOwnerTransferred(uint64 indexed subId, address from, address to); @@ -69,9 +74,9 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { // the request before requiring the block hash feeder. uint16 public constant MAX_REQUEST_CONFIRMATIONS = 200; uint32 public constant MAX_NUM_WORDS = 500; - // The minimum gas limit that could be requested for a callback. - // Set to 5k to ensure plenty of room to make the call itself. - uint256 public constant MIN_GAS_LIMIT = 5_000; + // 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100) + // and some arithmetic operations. + uint256 private constant GAS_FOR_CALL_EXACT_CHECK = 5_000; error InvalidRequestConfirmations(uint16 have, uint16 min, uint16 max); error GasLimitTooBig(uint32 have, uint32 want); error NumWordsTooBig(uint32 have, uint32 want); @@ -93,6 +98,7 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { } mapping(bytes32 => address) /* keyHash */ /* oracle */ private s_provingKeys; + bytes32[] public s_provingKeyHashes; mapping(address => uint96) /* oracle */ /* LINK balance */ private s_withdrawableTokens; mapping(uint256 => bytes32) /* requestID */ /* commitment */ @@ -113,30 +119,39 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { struct Config { uint16 minimumRequestConfirmations; - // Flat fee charged per fulfillment in millionths of link - // So fee range is [0, 2^32/10^6]. - uint32 fulfillmentFlatFeeLinkPPM; uint32 maxGasLimit; + // Re-entrancy protection. + bool reentrancyLock; // stalenessSeconds is how long before we consider the feed price to be stale // and fallback to fallbackWeiPerUnitLink. uint32 stalenessSeconds; // Gas to cover oracle payment after we calculate the payment. // We make it configurable in case those operations are repriced. uint32 gasAfterPaymentCalculation; - uint96 minimumSubscriptionBalance; - // Re-entrancy protection. - bool reentrancyLock; } - int256 internal s_fallbackWeiPerUnitLink; - Config private s_config; + int256 public s_fallbackWeiPerUnitLink; + Config public s_config; + FeeConfig public s_feeConfig; + struct FeeConfig { + // Flat fee charged per fulfillment in millionths of link + // So fee range is [0, 2^32/10^6]. + uint32 fulfillmentFlatFeeLinkPPMTier1; + uint32 fulfillmentFlatFeeLinkPPMTier2; + uint32 fulfillmentFlatFeeLinkPPMTier3; + uint32 fulfillmentFlatFeeLinkPPMTier4; + uint32 fulfillmentFlatFeeLinkPPMTier5; + uint24 reqsForTier2; + uint24 reqsForTier3; + uint24 reqsForTier4; + uint24 reqsForTier5; + } event ConfigSet( uint16 minimumRequestConfirmations, - uint32 fulfillmentFlatFeeLinkPPM, uint32 maxGasLimit, uint32 stalenessSeconds, uint32 gasAfterPaymentCalculation, - uint96 minimumSubscriptionBalance, - int256 fallbackWeiPerUnitLink + int256 fallbackWeiPerUnitLink, + FeeConfig feeConfig ); constructor( @@ -160,6 +175,7 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { revert ProvingKeyAlreadyRegistered(kh); } s_provingKeys[kh] = oracle; + s_provingKeyHashes.push(kh); emit ProvingKeyRegistered(kh, oracle); } @@ -174,6 +190,14 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { revert NoSuchProvingKey(kh); } delete s_provingKeys[kh]; + for (uint256 i = 0; i < s_provingKeyHashes.length; i++) { + if (s_provingKeyHashes[i] == kh) { + bytes32 last = s_provingKeyHashes[s_provingKeyHashes.length - 1]; + // Copy last element and overwrite kh to be deleted with it + s_provingKeyHashes[i] = last; + s_provingKeyHashes.pop(); + } + } emit ProvingKeyDeregistered(kh, oracle); } @@ -187,12 +211,11 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { function setConfig( uint16 minimumRequestConfirmations, - uint32 fulfillmentFlatFeeLinkPPM, uint32 maxGasLimit, uint32 stalenessSeconds, uint32 gasAfterPaymentCalculation, - uint96 minimumSubscriptionBalance, - int256 fallbackWeiPerUnitLink + int256 fallbackWeiPerUnitLink, + FeeConfig memory feeConfig ) external onlyOwner { if (minimumRequestConfirmations > MAX_REQUEST_CONFIRMATIONS) { revert InvalidRequestConfirmations( @@ -206,51 +229,30 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { } s_config = Config({ minimumRequestConfirmations: minimumRequestConfirmations, - fulfillmentFlatFeeLinkPPM: fulfillmentFlatFeeLinkPPM, maxGasLimit: maxGasLimit, stalenessSeconds: stalenessSeconds, gasAfterPaymentCalculation: gasAfterPaymentCalculation, - minimumSubscriptionBalance: minimumSubscriptionBalance, reentrancyLock: false }); + s_feeConfig = feeConfig; s_fallbackWeiPerUnitLink = fallbackWeiPerUnitLink; emit ConfigSet( minimumRequestConfirmations, - fulfillmentFlatFeeLinkPPM, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, - minimumSubscriptionBalance, - fallbackWeiPerUnitLink + fallbackWeiPerUnitLink, + s_feeConfig ); } - /** - * @notice read the current configuration of the coordinator. - */ - function getConfig() - external - view - returns ( - uint16 minimumRequestConfirmations, - uint32 fulfillmentFlatFeeLinkPPM, - uint32 maxGasLimit, - uint32 stalenessSeconds, - uint32 gasAfterPaymentCalculation, - uint96 minimumSubscriptionBalance, - int256 fallbackWeiPerUnitLink - ) - { - Config memory config = s_config; - return ( - config.minimumRequestConfirmations, - config.fulfillmentFlatFeeLinkPPM, - config.maxGasLimit, - config.stalenessSeconds, - config.gasAfterPaymentCalculation, - config.minimumSubscriptionBalance, - s_fallbackWeiPerUnitLink - ); + // A protective measure, the owner can cancel anyone's subscription, + // sending the link directly to the subscription owner. + function ownerCancelSubscription(uint64 subId) external onlyOwner { + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + cancelSubscriptionHelper(subId, s_subscriptionConfigs[subId].owner); } function recoverFunds(address to) external onlyOwner { @@ -278,13 +280,14 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { uint32 numWords // Desired number of random words ) external nonReentrant returns (uint256) { // Input validation using the subscription storage. - if (s_subscriptions[subId].owner == address(0)) { + if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } // Its important to ensure that the consumer is in fact who they say they // are, otherwise they could use someone else's subscription balance. - Consumer memory consumer = s_consumers[msg.sender][subId]; - if (consumer.subId == 0) { + // A nonce of 0 indicates consumer is not allocated to the sub. + uint64 currentNonce = s_consumers[msg.sender][subId]; + if (currentNonce == 0) { revert InvalidConsumer(subId, msg.sender); } // Input validation using the config storage word. @@ -297,9 +300,9 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { MAX_REQUEST_CONFIRMATIONS ); } - if (s_subscriptions[subId].balance < s_config.minimumSubscriptionBalance) { - revert InsufficientBalance(); - } + // No lower bound on the requested gas limit. A user could request 0 + // and they would simply be billed for the proof verification and wouldn't be + // able to do anything with the random value. if (callbackGasLimit > s_config.maxGasLimit) { revert GasLimitTooBig(callbackGasLimit, s_config.maxGasLimit); } @@ -309,9 +312,8 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { // Note we do not check whether the keyHash is valid to save gas. // The consequence for users is that they can send requests // for invalid keyHashes which will simply not be fulfilled. - uint64 nonce = consumer.nonce + 1; - uint256 preSeed = uint256(keccak256(abi.encode(keyHash, msg.sender, subId, nonce))); - uint256 requestId = uint256(keccak256(abi.encode(keyHash, preSeed))); + uint64 nonce = currentNonce + 1; + (uint256 requestId, uint256 preSeed) = computeRequestId(keyHash, msg.sender, subId, nonce); s_requestCommitments[requestId] = keccak256( abi.encode(requestId, block.number, subId, callbackGasLimit, numWords, msg.sender) @@ -326,7 +328,7 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { numWords, msg.sender ); - s_consumers[msg.sender][subId].nonce = nonce; + s_consumers[msg.sender][subId] = nonce; return requestId; } @@ -335,11 +337,19 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { return s_requestCommitments[requestId]; } + function computeRequestId( + bytes32 keyHash, + address sender, + uint64 subId, + uint64 nonce + ) private pure returns (uint256, uint256) { + uint256 preSeed = uint256(keccak256(abi.encode(keyHash, sender, subId, nonce))); + return (uint256(keccak256(abi.encode(keyHash, preSeed))), preSeed); + } + /** * @dev calls target address with exactly gasAmount gas and data as calldata * or reverts if at least gasAmount gas is not available. - * The maximum amount of gasAmount is all gas available but 1/64th. - * The minimum amount of gasAmount is MIN_GAS_LIMIT. */ function callWithExactGas( uint256 gasAmount, @@ -349,11 +359,16 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { // solhint-disable-next-line no-inline-assembly assembly { let g := gas() - // Compute g -= MIN_GAS_LIMIT and check for underflow - if lt(g, MIN_GAS_LIMIT) { + // Compute g -= GAS_FOR_CALL_EXACT_CHECK 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. GAS_FOR_CALL_EXACT_CHECK ensures we have at least enough gas to be able + // to revert if gasAmount > 63//64*gas available. + if lt(g, GAS_FOR_CALL_EXACT_CHECK) { revert(0, 0) } - g := sub(g, MIN_GAS_LIMIT) + g := sub(g, GAS_FOR_CALL_EXACT_CHECK) // if g - g//64 <= gasAmount, revert // (we subtract g//64 because of EIP-150) if iszero(gt(sub(g, div(g, 64)), gasAmount)) { @@ -364,6 +379,7 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { revert(0, 0) } // call and return whether we succeeded. ignore return data + // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) } return success; @@ -408,7 +424,25 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { randomness = VRF.randomValueFromVRFProof(proof, actualSeed); // Reverts on failure } - function fulfillRandomWords(Proof memory proof, RequestCommitment memory rc) external nonReentrant { + // Select the fee tier based on the request count + function getFeeTier(uint64 reqCount) public view returns (uint32) { + FeeConfig memory fc = s_feeConfig; + if (0 <= reqCount && reqCount <= fc.reqsForTier2) { + return fc.fulfillmentFlatFeeLinkPPMTier1; + } + if (fc.reqsForTier2 < reqCount && reqCount <= fc.reqsForTier3) { + return fc.fulfillmentFlatFeeLinkPPMTier2; + } + if (fc.reqsForTier3 < reqCount && reqCount <= fc.reqsForTier4) { + return fc.fulfillmentFlatFeeLinkPPMTier3; + } + if (fc.reqsForTier4 < reqCount && reqCount <= fc.reqsForTier5) { + return fc.fulfillmentFlatFeeLinkPPMTier4; + } + return fc.fulfillmentFlatFeeLinkPPMTier5; + } + + function fulfillRandomWords(Proof memory proof, RequestCommitment memory rc) external nonReentrant returns (uint96) { uint256 startGas = gasleft(); (bytes32 keyHash, uint256 requestId, uint256 randomness) = getRandomnessFromProof(proof, rc); @@ -420,19 +454,21 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { delete s_requestCommitments[requestId]; VRFConsumerBaseV2 v; bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomWords.selector, proof.seed, randomWords); - uint256 gasPreCallback = gasleft(); - if (gasPreCallback < rc.callbackGasLimit) { - revert InsufficientGasForConsumer(gasPreCallback, rc.callbackGasLimit); - } // Call with explicitly the amount of callback gas requested // Important to not let them exhaust the gas budget and avoid oracle payment. // Do not allow any non-view/non-pure coordinator functions to be called // during the consumers callback code via reentrancyLock. + // Note that callWithExactGas will revert if we do not have sufficient gas + // to give the callee their requested amount. s_config.reentrancyLock = true; bool success = callWithExactGas(rc.callbackGasLimit, rc.sender, resp); emit RandomWordsFulfilled(requestId, randomWords, success); s_config.reentrancyLock = false; + // Increment the req count for fee tier selection. + uint64 reqCount = s_subscriptions[rc.subId].reqCount; + s_subscriptions[rc.subId].reqCount += 1; + // We want to charge users exactly for how much gas they use in their callback. // The gasAfterPaymentCalculation is meant to cover these additional operations where we // decrement the subscription balance and increment the oracles withdrawable balance. @@ -442,7 +478,7 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { uint96 payment = calculatePaymentAmount( startGas, s_config.gasAfterPaymentCalculation, - s_config.fulfillmentFlatFeeLinkPPM, + getFeeTier(reqCount), tx.gasprice ); if (s_subscriptions[rc.subId].balance < payment) { @@ -450,6 +486,7 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { } s_subscriptions[rc.subId].balance -= payment; s_withdrawableTokens[s_provingKeys[keyHash]] += payment; + return payment; } // Get the amount of gas used for fulfillment @@ -510,10 +547,10 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { revert InvalidCalldata(); } uint64 subId = abi.decode(data, (uint64)); - if (s_subscriptions[subId].owner == address(0)) { + if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } - address owner = s_subscriptions[subId].owner; + address owner = s_subscriptionConfigs[subId].owner; if (owner != sender) { revert MustBeSubOwner(owner); } @@ -532,18 +569,18 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { address[] memory consumers ) { - if (s_subscriptions[subId].owner == address(0)) { + if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } - return (s_subscriptions[subId].balance, s_subscriptions[subId].owner, s_subscriptions[subId].consumers); + return (s_subscriptions[subId].balance, s_subscriptionConfigs[subId].owner, s_subscriptionConfigs[subId].consumers); } function createSubscription() external nonReentrant returns (uint64) { s_currentSubId++; uint64 currentSubId = s_currentSubId; address[] memory consumers = new address[](0); - s_subscriptions[currentSubId] = Subscription({ - balance: 0, + s_subscriptions[currentSubId] = Subscription({balance: 0, reqCount: 0}); + s_subscriptionConfigs[currentSubId] = SubscriptionConfig({ owner: msg.sender, requestedOwner: address(0), consumers: consumers @@ -555,39 +592,39 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external onlySubOwner(subId) nonReentrant { // Proposing to address(0) would never be claimable so don't need to check. - if (s_subscriptions[subId].requestedOwner != newOwner) { - s_subscriptions[subId].requestedOwner = newOwner; + if (s_subscriptionConfigs[subId].requestedOwner != newOwner) { + s_subscriptionConfigs[subId].requestedOwner = newOwner; emit SubscriptionOwnerTransferRequested(subId, msg.sender, newOwner); } } function acceptSubscriptionOwnerTransfer(uint64 subId) external nonReentrant { - if (s_subscriptions[subId].owner == address(0)) { + if (s_subscriptionConfigs[subId].owner == address(0)) { revert InvalidSubscription(); } - if (s_subscriptions[subId].requestedOwner != msg.sender) { - revert MustBeRequestedOwner(s_subscriptions[subId].requestedOwner); + if (s_subscriptionConfigs[subId].requestedOwner != msg.sender) { + revert MustBeRequestedOwner(s_subscriptionConfigs[subId].requestedOwner); } - address oldOwner = s_subscriptions[subId].owner; - s_subscriptions[subId].owner = msg.sender; - s_subscriptions[subId].requestedOwner = address(0); + address oldOwner = s_subscriptionConfigs[subId].owner; + s_subscriptionConfigs[subId].owner = msg.sender; + s_subscriptionConfigs[subId].requestedOwner = address(0); emit SubscriptionOwnerTransferred(subId, oldOwner, msg.sender); } function removeConsumer(uint64 subId, address consumer) external onlySubOwner(subId) nonReentrant { - if (s_consumers[consumer][subId].subId == 0) { + if (s_consumers[consumer][subId] == 0) { revert InvalidConsumer(subId, consumer); } // Note bounded by MAX_CONSUMERS - address[] memory consumers = s_subscriptions[subId].consumers; + address[] memory consumers = s_subscriptionConfigs[subId].consumers; uint256 lastConsumerIndex = consumers.length - 1; for (uint256 i = 0; i < consumers.length; i++) { if (consumers[i] == consumer) { address last = consumers[lastConsumerIndex]; // Storage write to preserve last element - s_subscriptions[subId].consumers[i] = last; + s_subscriptionConfigs[subId].consumers[i] = last; // Storage remove last element - s_subscriptions[subId].consumers.pop(); + s_subscriptionConfigs[subId].consumers.pop(); break; } } @@ -597,47 +634,40 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { function addConsumer(uint64 subId, address consumer) external onlySubOwner(subId) nonReentrant { // Already maxed, cannot add any more consumers. - if (s_subscriptions[subId].consumers.length == MAX_CONSUMERS) { + if (s_subscriptionConfigs[subId].consumers.length == MAX_CONSUMERS) { revert TooManyConsumers(); } - if (s_consumers[consumer][subId].subId != 0) { + if (s_consumers[consumer][subId] != 0) { // Idempotence - do nothing if already added. // Ensures uniqueness in s_subscriptions[subId].consumers. return; } - s_consumers[consumer][subId] = Consumer({subId: subId, nonce: 0}); - s_subscriptions[subId].consumers.push(consumer); + // Initialize the nonce to 1, indicating the consumer is allocated. + s_consumers[consumer][subId] = 1; + s_subscriptionConfigs[subId].consumers.push(consumer); emit SubscriptionConsumerAdded(subId, consumer); } - function defundSubscription( - uint64 subId, - address to, - uint96 amount - ) external onlySubOwner(subId) nonReentrant { - if (s_subscriptions[subId].balance < amount) { - revert InsufficientBalance(); - } - uint256 oldBalance = s_subscriptions[subId].balance; - s_subscriptions[subId].balance -= amount; - s_totalBalance -= amount; - if (!LINK.transfer(to, amount)) { - revert InsufficientBalance(); - } - emit SubscriptionDefunded(subId, oldBalance, s_subscriptions[subId].balance); - } - // Keep this separate from zeroing, perhaps there is a use case where consumers // want to keep the subId, but withdraw all the link. function cancelSubscription(uint64 subId, address to) external onlySubOwner(subId) nonReentrant { + if (pendingRequestExists(subId)) { + revert PendingRequestExists(); + } + cancelSubscriptionHelper(subId, to); + } + + function cancelSubscriptionHelper(uint64 subId, address to) private nonReentrant { + SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; Subscription memory sub = s_subscriptions[subId]; uint96 balance = sub.balance; // Note bounded by MAX_CONSUMERS; // If no consumers, does nothing. - for (uint256 i = 0; i < sub.consumers.length; i++) { - delete s_consumers[sub.consumers[i]][subId]; + for (uint256 i = 0; i < subConfig.consumers.length; i++) { + delete s_consumers[subConfig.consumers[i]][subId]; } + delete s_subscriptionConfigs[subId]; delete s_subscriptions[subId]; s_totalBalance -= balance; if (!LINK.transfer(to, uint256(balance))) { @@ -646,8 +676,29 @@ contract VRFCoordinatorV2 is VRF, ConfirmedOwner, TypeAndVersionInterface { emit SubscriptionCanceled(subId, to, balance); } + // Check to see if there exists a request commitment consumers + // for all consumers and keyhashes for a given sub. + // Looping is bounded to MAX_CONSUMERS*(number of keyhashes). + function pendingRequestExists(uint64 subId) public view returns (bool) { + SubscriptionConfig memory subConfig = s_subscriptionConfigs[subId]; + for (uint256 i = 0; i < subConfig.consumers.length; i++) { + for (uint256 j = 0; j < s_provingKeyHashes.length; j++) { + (uint256 reqId, ) = computeRequestId( + s_provingKeyHashes[j], + subConfig.consumers[i], + subId, + s_consumers[subConfig.consumers[i]][subId] + ); + if (s_requestCommitments[reqId] != 0) { + return true; + } + } + } + return false; + } + modifier onlySubOwner(uint64 subId) { - address owner = s_subscriptions[subId].owner; + address owner = s_subscriptionConfigs[subId].owner; if (owner == address(0)) { revert InvalidSubscription(); } diff --git a/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol b/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol index ee216a07051..eac75afc161 100644 --- a/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol +++ b/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol @@ -2,34 +2,6 @@ pragma solidity ^0.8.0; interface VRFCoordinatorV2Interface { - /** - * @notice Returns the global config that applies to all VRF requests. - * @return minimumRequestBlockConfirmations - A minimum number of confirmation - * blocks on VRF requests before oracles should respond. - * @return fulfillmentFlatFeeLinkPPM - The charge per request on top of the gas fees. - * Its flat fee specified in millionths of LINK. - * @return maxGasLimit - The maximum gas limit supported for a fulfillRandomWords callback. - * @return stalenessSeconds - How long we wait until we consider the ETH/LINK price - * (used for converting gas costs to LINK) is stale and use `fallbackWeiPerUnitLink` - * @return gasAfterPaymentCalculation - How much gas is used outside of the payment calculation, - * i.e. the gas overhead of actually making the payment to oracles. - * @return minimumSubscriptionBalance - The minimum subscription balance required to make a request. Its set to be about 300% - * of the cost of a single request to handle in ETH/LINK price between request and fulfillment time. - * @return fallbackWeiPerUnitLink - fallback ETH/LINK price in the case of a stale feed. - */ - function getConfig() - external - view - returns ( - uint16 minimumRequestBlockConfirmations, - uint32 fulfillmentFlatFeeLinkPPM, - uint32 maxGasLimit, - uint32 stalenessSeconds, - uint32 gasAfterPaymentCalculation, - uint96 minimumSubscriptionBalance, - int256 fallbackWeiPerUnitLink - ); - /** * @notice Request a set of random words. * @param keyHash - Corresponds to a particular oracle job which uses @@ -47,7 +19,7 @@ interface VRFCoordinatorV2Interface { * may be slightly less than this amount because of gas used calling the function * (argument decoding etc.), so you may need to request slightly more than you expect * to have inside fulfillRandomWords. The acceptable range is - * [5000, maxGasLimit]. + * [0, maxGasLimit] * @param numWords - The number of uint256 random values you'd like to receive * in your fulfillRandomWords callback. Note these numbers are expanded in a * secure way by the VRFCoordinator from a single random value supplied by the oracle. @@ -119,18 +91,6 @@ interface VRFCoordinatorV2Interface { */ function removeConsumer(uint64 subId, address consumer) external; - /** - * @notice Withdraw funds from a VRF subscription - * @param subId - ID of the subscription - * @param to - Where to send the withdrawn LINK to - * @param amount - How much to withdraw in juels - */ - function defundSubscription( - uint64 subId, - address to, - uint96 amount - ) external; - /** * @notice Cancel a subscription * @param subId - ID of the subscription diff --git a/contracts/src/v0.8/tests/VRFConsumerV2.sol b/contracts/src/v0.8/tests/VRFConsumerV2.sol index 012dffde2a3..14f713a4e16 100644 --- a/contracts/src/v0.8/tests/VRFConsumerV2.sol +++ b/contracts/src/v0.8/tests/VRFConsumerV2.sol @@ -33,6 +33,12 @@ contract VRFConsumerV2 is VRFConsumerBaseV2 { LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_subId)); } + function topUpSubscription(uint96 amount) external { + require(s_subId != 0, "sub not set"); + // Approve the link transfer. + LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_subId)); + } + function updateSubscription(address[] memory consumers) external { require(s_subId != 0, "subID not set"); for (uint256 i = 0; i < consumers.length; i++) { diff --git a/contracts/test/test-helpers/helpers.ts b/contracts/test/test-helpers/helpers.ts index f84e0f603ce..f36025678f7 100644 --- a/contracts/test/test-helpers/helpers.ts +++ b/contracts/test/test-helpers/helpers.ts @@ -1,5 +1,5 @@ import { Contract, ContractTransaction } from 'ethers' -import type { providers } from 'ethers' +import { providers } from 'ethers' import { assert } from 'chai' import { ethers } from 'hardhat' import cbor from 'cbor' diff --git a/contracts/test/v0.8/dev/VRFCoordinatorV2.test.ts b/contracts/test/v0.8/dev/VRFCoordinatorV2.test.ts index fc206dc375e..a7ab3df8d60 100644 --- a/contracts/test/v0.8/dev/VRFCoordinatorV2.test.ts +++ b/contracts/test/v0.8/dev/VRFCoordinatorV2.test.ts @@ -20,12 +20,10 @@ describe('VRFCoordinatorV2', () => { const linkEth = BigNumber.from(300000000) type config = { minimumRequestBlockConfirmations: number - fulfillmentFlatFeePPM: number maxGasLimit: number stalenessSeconds: number gasAfterPaymentCalculation: number weiPerUnitLink: BigNumber - minimumSubscriptionBalance: BigNumber } let c: config @@ -81,24 +79,25 @@ describe('VRFCoordinatorV2', () => { ) // 1 link c = { minimumRequestBlockConfirmations: 1, - fulfillmentFlatFeePPM: 0, maxGasLimit: 1000000, stalenessSeconds: 86400, gasAfterPaymentCalculation: 21000 + 5000 + 2100 + 20000 + 2 * 2100 - 15000 + 7315, weiPerUnitLink: BigNumber.from('10000000000000000'), - minimumSubscriptionBalance: BigNumber.from('100000000000000000'), // 0.1 link } + // Note if you try and use an object, ethers + // confuses that with an override object and will error. + // It appears that only arrays work for struct args. + const fc = [0, 0, 0, 0, 0, 0, 0, 0, 0] await vrfCoordinatorV2 .connect(owner) .setConfig( c.minimumRequestBlockConfirmations, - c.fulfillmentFlatFeePPM, c.maxGasLimit, c.stalenessSeconds, c.gasAfterPaymentCalculation, c.weiPerUnitLink, - c.minimumSubscriptionBalance, + fc, ) }) @@ -106,17 +105,22 @@ describe('VRFCoordinatorV2', () => { publicAbi(vrfCoordinatorV2, [ // Public constants 'MAX_CONSUMERS', - 'MIN_GAS_LIMIT', 'MAX_NUM_WORDS', 'MAX_REQUEST_CONFIRMATIONS', // Owner 'acceptOwnership', 'transferOwnership', 'owner', - 'getConfig', + 's_feeConfig', + 's_config', + 's_fallbackWeiPerUnitLink', 'setConfig', 'recoverFunds', + 'ownerCancelSubscription', + 'getFeeTier', + 'pendingRequestExists', 's_totalBalance', + 's_provingKeyHashes', // Oracle 'requestRandomWords', 'getCommitment', // Note we use this to check if a request is already fulfilled. @@ -131,7 +135,6 @@ describe('VRFCoordinatorV2', () => { 'removeConsumer', 'getSubscription', 'onTokenTransfer', // Effectively the fundSubscription. - 'defundSubscription', 'cancelSubscription', 'requestSubscriptionOwnerTransfer', 'acceptSubscriptionOwnerTransfer', @@ -150,22 +153,21 @@ describe('VRFCoordinatorV2', () => { .connect(subOwner) .setConfig( c.minimumRequestBlockConfirmations, - c.fulfillmentFlatFeePPM, c.maxGasLimit, c.stalenessSeconds, c.gasAfterPaymentCalculation, - c.minimumSubscriptionBalance, c.weiPerUnitLink, + [0, 0, 0, 0, 0, 0, 0, 0, 0], ), ).to.be.revertedWith('Only callable by owner') // Anyone can read the config. - const resp = await vrfCoordinatorV2.connect(random).getConfig() + const resp = await vrfCoordinatorV2.connect(random).s_config() + console.log('config', resp) assert(resp[0] == c.minimumRequestBlockConfirmations) - assert(resp[1] == c.fulfillmentFlatFeePPM) - assert(resp[2] == c.maxGasLimit) + assert(resp[1] == c.maxGasLimit) + assert(resp[2] == false) // locked assert(resp[3] == c.stalenessSeconds) assert(resp[4].toString() == c.gasAfterPaymentCalculation.toString()) - assert(resp[5].toString() == c.weiPerUnitLink.toString()) }) it('max req confs', async function () { @@ -174,12 +176,11 @@ describe('VRFCoordinatorV2', () => { .connect(owner) .setConfig( 201, - c.fulfillmentFlatFeePPM, c.maxGasLimit, c.stalenessSeconds, c.gasAfterPaymentCalculation, - c.minimumSubscriptionBalance, c.weiPerUnitLink, + [0, 0, 0, 0, 0, 0, 0, 0, 0], ), ).to.be.revertedWith('InvalidRequestConfirmations(201, 201, 200)') }) @@ -190,12 +191,11 @@ describe('VRFCoordinatorV2', () => { .connect(owner) .setConfig( c.minimumRequestBlockConfirmations, - c.fulfillmentFlatFeePPM, c.maxGasLimit, c.stalenessSeconds, c.gasAfterPaymentCalculation, - c.minimumSubscriptionBalance, 0, + [0, 0, 0, 0, 0, 0, 0, 0, 0], ), ).to.be.revertedWith('InvalidLinkWeiPrice(0)') await expect( @@ -203,12 +203,11 @@ describe('VRFCoordinatorV2', () => { .connect(owner) .setConfig( c.minimumRequestBlockConfirmations, - c.fulfillmentFlatFeePPM, c.maxGasLimit, c.stalenessSeconds, c.gasAfterPaymentCalculation, - c.minimumSubscriptionBalance, -1, + [0, 0, 0, 0, 0, 0, 0, 0, 0], ), ).to.be.revertedWith('InvalidLinkWeiPrice(-1)') }) @@ -434,63 +433,6 @@ describe('VRFCoordinatorV2', () => { }) }) - describe('#defundSubscription', async function () { - let subId: number - beforeEach(async () => { - subId = await createSubscription() - }) - it('subscription must exist', async function () { - await expect( - vrfCoordinatorV2 - .connect(subOwner) - .defundSubscription( - 1203123123, - subOwnerAddress, - BigNumber.from('1000'), - ), - ).to.be.revertedWith(`InvalidSubscription`) - }) - it('must be owner', async function () { - await expect( - vrfCoordinatorV2 - .connect(random) - .defundSubscription(subId, subOwnerAddress, BigNumber.from('1000')), - ).to.be.revertedWith(`MustBeSubOwner("${subOwnerAddress}")`) - }) - it('insufficient balance', async function () { - await linkToken - .connect(subOwner) - .transferAndCall( - vrfCoordinatorV2.address, - BigNumber.from('1000'), - ethers.utils.defaultAbiCoder.encode(['uint64'], [subId]), - ) - await expect( - vrfCoordinatorV2 - .connect(subOwner) - .defundSubscription(subId, subOwnerAddress, BigNumber.from('1001')), - ).to.be.revertedWith(`InsufficientBalance()`) - }) - it('can defund', async function () { - await linkToken - .connect(subOwner) - .transferAndCall( - vrfCoordinatorV2.address, - BigNumber.from('1000'), - ethers.utils.defaultAbiCoder.encode(['uint64'], [subId]), - ) - await expect( - vrfCoordinatorV2 - .connect(subOwner) - .defundSubscription(subId, randomAddress, BigNumber.from('999')), - ) - .to.emit(vrfCoordinatorV2, 'SubscriptionDefunded') - .withArgs(subId, BigNumber.from('1000'), BigNumber.from('1')) - const randomBalance = await linkToken.balanceOf(randomAddress) - assert.equal(randomBalance.toString(), '1000000000000000999') - }) - }) - describe('#cancelSubscription', async function () { let subId: number beforeEach(async () => { @@ -547,6 +489,40 @@ describe('VRFCoordinatorV2', () => { // The cancel should have removed this consumer, so we can add it again. await vrfCoordinatorV2.connect(subOwner).addConsumer(subId, randomAddress) }) + it('cannot cancel with pending req', async function () { + await linkToken + .connect(subOwner) + .transferAndCall( + vrfCoordinatorV2.address, + BigNumber.from('1000'), + ethers.utils.defaultAbiCoder.encode(['uint64'], [subId]), + ) + await vrfCoordinatorV2.connect(subOwner).addConsumer(subId, randomAddress) + const testKey = [BigNumber.from('1'), BigNumber.from('2')] + await vrfCoordinatorV2.registerProvingKey(subOwnerAddress, testKey) + await vrfCoordinatorV2.connect(owner).reg + const kh = await vrfCoordinatorV2.hashOfKey(testKey) + await vrfCoordinatorV2.connect(consumer).requestRandomWords( + kh, // keyhash + subId, // subId + 1, // minReqConf + 1000000, // callbackGasLimit + 1, // numWords + ) + // Should revert with outstanding requests + await expect( + vrfCoordinatorV2 + .connect(subOwner) + .cancelSubscription(subId, randomAddress), + ).to.be.revertedWith('PendingRequestExists()') + // However the owner is able to cancel + // funds go to the sub owner. + await expect( + vrfCoordinatorV2.connect(owner).ownerCancelSubscription(subId), + ) + .to.emit(vrfCoordinatorV2, 'SubscriptionCanceled') + .withArgs(subId, subOwnerAddress, BigNumber.from('1000')) + }) }) describe('#recoverFunds', async function () { @@ -573,21 +549,13 @@ describe('VRFCoordinatorV2', () => { }, BigNumber.from('1000'), ], - [ - async function () { - await vrfCoordinatorV2 - .connect(subOwner) - .defundSubscription(subId, randomAddress, BigNumber.from('100')) - }, - BigNumber.from('-100'), - ], [ async function () { await vrfCoordinatorV2 .connect(subOwner) .cancelSubscription(subId, randomAddress) }, - BigNumber.from('-900'), + BigNumber.from('-1000'), ], ] for (const [fn, expectedBalanceChange] of balanceChangingFns) { @@ -661,32 +629,6 @@ describe('VRFCoordinatorV2', () => { .to.emit(vrfCoordinatorV2, 'SubscriptionFunded') .withArgs(subId, BigNumber.from(0), BigNumber.from('1000000000000000000')) - // Non-owners cannot withdraw - await expect( - vrfCoordinatorV2 - .connect(random) - .defundSubscription( - subId, - randomAddress, - BigNumber.from('1000000000000000000'), - ), - ).to.be.revertedWith(`MustBeSubOwner("${subOwnerAddress}")`) - - // Withdraw from the subscription - await expect( - vrfCoordinatorV2 - .connect(subOwner) - .defundSubscription(subId, randomAddress, BigNumber.from('100')), - ) - .to.emit(vrfCoordinatorV2, 'SubscriptionDefunded') - .withArgs( - subId, - BigNumber.from('1000000000000000000'), - BigNumber.from('999999999999999900'), - ) - const randomBalance = await linkToken.balanceOf(randomAddress) - assert.equal(randomBalance.toString(), '1000000000000000100') - // Non-owners cannot change the consumers await expect( vrfCoordinatorV2.connect(random).addConsumer(subId, randomAddress), @@ -740,7 +682,7 @@ describe('VRFCoordinatorV2', () => { .cancelSubscription(subId, randomAddress), ) .to.emit(vrfCoordinatorV2, 'SubscriptionCanceled') - .withArgs(subId, randomAddress, BigNumber.from('999999999999999900')) + .withArgs(subId, randomAddress, BigNumber.from('1000000000000000000')) const random2Balance = await linkToken.balanceOf(randomAddress) assert.equal(random2Balance.toString(), '2000000000000000000') }) @@ -788,17 +730,6 @@ describe('VRFCoordinatorV2', () => { ), ).to.be.revertedWith(`InvalidRequestConfirmations(0, 1, 200)`) }) - it('below minimum balance', async function () { - await expect( - vrfCoordinatorV2.connect(consumer).requestRandomWords( - kh, // keyhash - subId, // subId - 1, // minReqConf - 1000, // callbackGasLimit - 1, // numWords - ), - ).to.be.revertedWith(`InsufficientBalance()`) - }) it('gas limit too high', async function () { await linkToken.connect(subOwner).transferAndCall( vrfCoordinatorV2.address, @@ -1034,6 +965,9 @@ describe('VRFCoordinatorV2', () => { ) .to.emit(vrfCoordinatorV2, 'ProvingKeyRegistered') .withArgs(kh, subOwnerAddress) + assert(kh, await vrfCoordinatorV2.s_provingKeyHashes(0)) + // Only one keyhash saved + await expect(vrfCoordinatorV2.s_provingKeyHashes(1)).to.be.reverted }) it('cannot re-register key', async function () { const testKey = [BigNumber.from('1'), BigNumber.from('2')] @@ -1050,6 +984,8 @@ describe('VRFCoordinatorV2', () => { await expect(vrfCoordinatorV2.deregisterProvingKey(testKey)) .to.emit(vrfCoordinatorV2, 'ProvingKeyDeregistered') .withArgs(kh, subOwnerAddress) + // No longer any keyhashes saved. + await expect(vrfCoordinatorV2.s_provingKeyHashes(0)).to.be.reverted }) it('cannot deregister unregistered key', async function () { const testKey = [BigNumber.from('1'), BigNumber.from('2')] @@ -1163,6 +1099,45 @@ describe('VRFCoordinatorV2', () => { }) }) + describe('#getFeeTier', async function () { + beforeEach(async () => { + await expect( + vrfCoordinatorV2 + .connect(owner) + .setConfig( + c.minimumRequestBlockConfirmations, + c.maxGasLimit, + c.stalenessSeconds, + c.gasAfterPaymentCalculation, + c.weiPerUnitLink, + [10000, 1000, 100, 10, 1, 10, 20, 30, 40], + ), + ) + }) + it('tier1', async function () { + assert((await vrfCoordinatorV2.connect(random).getFeeTier(0)) == 10000) + assert((await vrfCoordinatorV2.connect(random).getFeeTier(5)) == 10000) + assert((await vrfCoordinatorV2.connect(random).getFeeTier(10)) == 10000) + }) + it('tier2', async function () { + assert((await vrfCoordinatorV2.connect(random).getFeeTier(11)) == 1000) + assert((await vrfCoordinatorV2.connect(random).getFeeTier(12)) == 1000) + assert((await vrfCoordinatorV2.connect(random).getFeeTier(20)) == 1000) + }) + it('tier3', async function () { + assert((await vrfCoordinatorV2.connect(random).getFeeTier(21)) == 100) + assert((await vrfCoordinatorV2.connect(random).getFeeTier(30)) == 100) + }) + it('tier4', async function () { + assert((await vrfCoordinatorV2.connect(random).getFeeTier(31)) == 10) + assert((await vrfCoordinatorV2.connect(random).getFeeTier(40)) == 10) + }) + it('tier5', async function () { + assert((await vrfCoordinatorV2.connect(random).getFeeTier(41)) == 1) + assert((await vrfCoordinatorV2.connect(random).getFeeTier(123102)) == 1) + }) + }) + /* Note that all the fulfillment happy path testing is done in Go, to make use of the existing go code to produce proofs offchain. diff --git a/core/internal/cltest/simulated_backend.go b/core/internal/cltest/simulated_backend.go index 9e7f974876b..00746c94767 100644 --- a/core/internal/cltest/simulated_backend.go +++ b/core/internal/cltest/simulated_backend.go @@ -406,7 +406,6 @@ func (c *SimulatedBackendClient) SendTransaction(ctx context.Context, tx *types. } err = c.b.SendTransaction(ctx, tx) - c.b.Commit() return err } diff --git a/core/internal/features_test.go b/core/internal/features_test.go index 3426dea223f..9c7005adfdd 100644 --- a/core/internal/features_test.go +++ b/core/internal/features_test.go @@ -399,7 +399,7 @@ func TestIntegration_DirectRequest(t *testing.T) { empty := big.NewInt(0) assertPricesUint256(t, empty, empty, empty, operatorContracts.multiWord) - stopBlocks := finiteTicker(100*time.Millisecond, func() { + stopBlocks := utils.FiniteTicker(100*time.Millisecond, func() { triggerAllKeys(t, app) b.Commit() }) @@ -576,7 +576,7 @@ func TestIntegration_OCR(t *testing.T) { }) } - stopBlocks := finiteTicker(time.Second, func() { + stopBlocks := utils.FiniteTicker(time.Second, func() { b.Commit() }) defer stopBlocks() @@ -878,25 +878,3 @@ func assertPricesUint256(t *testing.T, usd, eur, jpy *big.Int, consumer *multiwo require.NoError(t, err) assert.True(t, jpy.Cmp(haveJpy) == 0) } - -func finiteTicker(period time.Duration, onTick func()) func() { - tick := time.NewTicker(period) - chStop := make(chan struct{}) - go func() { - for { - select { - case <-tick.C: - onTick() - case <-chStop: - return - } - } - }() - - // NOTE: tick.Stop does not close the ticker channel, - // so we still need another way of returning (chStop). - return func() { - tick.Stop() - close(chStop) - } -} diff --git a/core/internal/gethwrappers/generated/vrf_consumer_v2/vrf_consumer_v2.go b/core/internal/gethwrappers/generated/vrf_consumer_v2/vrf_consumer_v2.go index bc296fb0a72..a1ff117ed33 100644 --- a/core/internal/gethwrappers/generated/vrf_consumer_v2/vrf_consumer_v2.go +++ b/core/internal/gethwrappers/generated/vrf_consumer_v2/vrf_consumer_v2.go @@ -28,8 +28,8 @@ var ( ) var VRFConsumerV2MetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vrfCoordinator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"}],\"name\":\"rawFulfillRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_gasAvailable\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_randomWords\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_requestId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_subId\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"testCreateSubscriptionAndFund\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"minReqConfs\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"}],\"name\":\"testRequestRandomness\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"}],\"name\":\"updateSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60a060405234801561001057600080fd5b50604051610d09380380610d0983398101604081905261002f9161008e565b6001600160601b0319606083901b16608052600280546001600160a01b03199081166001600160a01b0394851617909155600380549290931691161790556100c1565b80516001600160a01b038116811461008957600080fd5b919050565b600080604083850312156100a157600080fd5b6100aa83610072565b91506100b860208401610072565b90509250929050565b60805160601c610c236100e66000396000818161017001526101d80152610c236000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063706da1ca1161005b578063706da1ca146100ee578063e89e106a14610133578063f08c5daa1461013c578063f6eaffc81461014557600080fd5b80631fe543e31461008d57806327784fad146100a257806336bfffed146100c85780636802f726146100db575b600080fd5b6100a061009b366004610937565b610158565b005b6100b56100b036600461089c565b610218565b6040519081526020015b60405180910390f35b6100a06100d63660046107b4565b6102f5565b6100a06100e93660046109f8565b61047d565b60035461011a9074010000000000000000000000000000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016100bf565b6100b560015481565b6100b560045481565b6100b5610153366004610905565b6106fc565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461020a576040517f1cf993f400000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201526044015b60405180910390fd5b610214828261071d565b5050565b6002546040517f5d3b1d300000000000000000000000000000000000000000000000000000000081526004810187905267ffffffffffffffff8616602482015261ffff8516604482015263ffffffff80851660648301528316608482015260009173ffffffffffffffffffffffffffffffffffffffff1690635d3b1d309060a401602060405180830381600087803b1580156102b357600080fd5b505af11580156102c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102eb919061091e565b9695505050505050565b60035474010000000000000000000000000000000000000000900467ffffffffffffffff16610380576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f7375624944206e6f7420736574000000000000000000000000000000000000006044820152606401610201565b60005b815181101561021457600254600354835173ffffffffffffffffffffffffffffffffffffffff90921691637341c10c9174010000000000000000000000000000000000000000900467ffffffffffffffff16908590859081106103e8576103e8610b9f565b60200260200101516040518363ffffffff1660e01b815260040161043892919067ffffffffffffffff92909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b15801561045257600080fd5b505af1158015610466573d6000803e3d6000fd5b50505050808061047590610b3f565b915050610383565b60035474010000000000000000000000000000000000000000900467ffffffffffffffff1661062857600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a21a23e46040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561051057600080fd5b505af1158015610524573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061054891906109db565b600380547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000067ffffffffffffffff938416810291909117918290556002546040517f7341c10c00000000000000000000000000000000000000000000000000000000815291909204909216600483015230602483015273ffffffffffffffffffffffffffffffffffffffff1690637341c10c90604401600060405180830381600087803b15801561060f57600080fd5b505af1158015610623573d6000803e3d6000fd5b505050505b6003546002546040805174010000000000000000000000000000000000000000840467ffffffffffffffff16602082015273ffffffffffffffffffffffffffffffffffffffff93841693634000aea09316918591016040516020818303038152906040526040518463ffffffff1660e01b81526004016106aa93929190610a26565b602060405180830381600087803b1580156106c457600080fd5b505af11580156106d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102149190610873565b6000818154811061070c57600080fd5b600091825260209091200154905081565b5a600455805161073490600090602084019061073b565b5050600155565b828054828255906000526020600020908101928215610776579160200282015b8281111561077657825182559160200191906001019061075b565b50610782929150610786565b5090565b5b808211156107825760008155600101610787565b803563ffffffff811681146107af57600080fd5b919050565b600060208083850312156107c757600080fd5b823567ffffffffffffffff8111156107de57600080fd5b8301601f810185136107ef57600080fd5b80356108026107fd82610b1b565b610acc565b80828252848201915084840188868560051b870101111561082257600080fd5b60009450845b8481101561086557813573ffffffffffffffffffffffffffffffffffffffff81168114610853578687fd5b84529286019290860190600101610828565b509098975050505050505050565b60006020828403121561088557600080fd5b8151801515811461089557600080fd5b9392505050565b600080600080600060a086880312156108b457600080fd5b8535945060208601356108c681610bfd565b9350604086013561ffff811681146108dd57600080fd5b92506108eb6060870161079b565b91506108f96080870161079b565b90509295509295909350565b60006020828403121561091757600080fd5b5035919050565b60006020828403121561093057600080fd5b5051919050565b6000806040838503121561094a57600080fd5b8235915060208084013567ffffffffffffffff81111561096957600080fd5b8401601f8101861361097a57600080fd5b80356109886107fd82610b1b565b80828252848201915084840189868560051b87010111156109a857600080fd5b600094505b838510156109cb5780358352600194909401939185019185016109ad565b5080955050505050509250929050565b6000602082840312156109ed57600080fd5b815161089581610bfd565b600060208284031215610a0a57600080fd5b81356bffffffffffffffffffffffff8116811461089557600080fd5b73ffffffffffffffffffffffffffffffffffffffff84168152600060206bffffffffffffffffffffffff85168184015260606040840152835180606085015260005b81811015610a8457858101830151858201608001528201610a68565b81811115610a96576000608083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160800195945050505050565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715610b1357610b13610bce565b604052919050565b600067ffffffffffffffff821115610b3557610b35610bce565b5060051b60200190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610b98577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff81168114610c1357600080fd5b5056fea164736f6c6343000806000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"vrfCoordinator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"internalType\":\"uint256[]\",\"name\":\"randomWords\",\"type\":\"uint256[]\"}],\"name\":\"rawFulfillRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_gasAvailable\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_randomWords\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_requestId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_subId\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"testCreateSubscriptionAndFund\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"minReqConfs\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"}],\"name\":\"testRequestRandomness\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"topUpSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"}],\"name\":\"updateSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a060405234801561001057600080fd5b50604051610e1f380380610e1f83398101604081905261002f9161008e565b6001600160601b0319606083901b16608052600280546001600160a01b03199081166001600160a01b0394851617909155600380549290931691161790556100c1565b80516001600160a01b038116811461008957600080fd5b919050565b600080604083850312156100a157600080fd5b6100aa83610072565b91506100b860208401610072565b90509250929050565b60805160601c610d396100e66000396000818161019e01526102060152610d396000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c80636802f72611610076578063e89e106a1161005b578063e89e106a14610161578063f08c5daa1461016a578063f6eaffc81461017357600080fd5b80636802f72614610109578063706da1ca1461011c57600080fd5b80631fe543e3146100a857806327784fad146100bd5780632fa4e442146100e357806336bfffed146100f6575b600080fd5b6100bb6100b6366004610a4d565b610186565b005b6100d06100cb3660046109b2565b610246565b6040519081526020015b60405180910390f35b6100bb6100f1366004610b0e565b610323565b6100bb6101043660046108ca565b610483565b6100bb610117366004610b0e565b61060b565b6003546101489074010000000000000000000000000000000000000000900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016100da565b6100d060015481565b6100d060045481565b6100d0610181366004610a1b565b610812565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610238576040517f1cf993f400000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201526044015b60405180910390fd5b6102428282610833565b5050565b6002546040517f5d3b1d300000000000000000000000000000000000000000000000000000000081526004810187905267ffffffffffffffff8616602482015261ffff8516604482015263ffffffff80851660648301528316608482015260009173ffffffffffffffffffffffffffffffffffffffff1690635d3b1d309060a401602060405180830381600087803b1580156102e157600080fd5b505af11580156102f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103199190610a34565b9695505050505050565b60035474010000000000000000000000000000000000000000900467ffffffffffffffff166103ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f737562206e6f7420736574000000000000000000000000000000000000000000604482015260640161022f565b6003546002546040805174010000000000000000000000000000000000000000840467ffffffffffffffff16602082015273ffffffffffffffffffffffffffffffffffffffff93841693634000aea09316918591015b6040516020818303038152906040526040518463ffffffff1660e01b815260040161043193929190610b3c565b602060405180830381600087803b15801561044b57600080fd5b505af115801561045f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102429190610989565b60035474010000000000000000000000000000000000000000900467ffffffffffffffff1661050e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f7375624944206e6f742073657400000000000000000000000000000000000000604482015260640161022f565b60005b815181101561024257600254600354835173ffffffffffffffffffffffffffffffffffffffff90921691637341c10c9174010000000000000000000000000000000000000000900467ffffffffffffffff169085908590811061057657610576610cb5565b60200260200101516040518363ffffffff1660e01b81526004016105c692919067ffffffffffffffff92909216825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b1580156105e057600080fd5b505af11580156105f4573d6000803e3d6000fd5b50505050808061060390610c55565b915050610511565b60035474010000000000000000000000000000000000000000900467ffffffffffffffff166103ae57600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a21a23e46040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561069e57600080fd5b505af11580156106b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106d69190610af1565b600380547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000067ffffffffffffffff938416810291909117918290556002546040517f7341c10c00000000000000000000000000000000000000000000000000000000815291909204909216600483015230602483015273ffffffffffffffffffffffffffffffffffffffff1690637341c10c90604401600060405180830381600087803b15801561079d57600080fd5b505af11580156107b1573d6000803e3d6000fd5b50506003546002546040805174010000000000000000000000000000000000000000840467ffffffffffffffff16602082015273ffffffffffffffffffffffffffffffffffffffff9384169550634000aea094509290911691859101610404565b6000818154811061082257600080fd5b600091825260209091200154905081565b5a600455805161084a906000906020840190610851565b5050600155565b82805482825590600052602060002090810192821561088c579160200282015b8281111561088c578251825591602001919060010190610871565b5061089892915061089c565b5090565b5b80821115610898576000815560010161089d565b803563ffffffff811681146108c557600080fd5b919050565b600060208083850312156108dd57600080fd5b823567ffffffffffffffff8111156108f457600080fd5b8301601f8101851361090557600080fd5b803561091861091382610c31565b610be2565b80828252848201915084840188868560051b870101111561093857600080fd5b60009450845b8481101561097b57813573ffffffffffffffffffffffffffffffffffffffff81168114610969578687fd5b8452928601929086019060010161093e565b509098975050505050505050565b60006020828403121561099b57600080fd5b815180151581146109ab57600080fd5b9392505050565b600080600080600060a086880312156109ca57600080fd5b8535945060208601356109dc81610d13565b9350604086013561ffff811681146109f357600080fd5b9250610a01606087016108b1565b9150610a0f608087016108b1565b90509295509295909350565b600060208284031215610a2d57600080fd5b5035919050565b600060208284031215610a4657600080fd5b5051919050565b60008060408385031215610a6057600080fd5b8235915060208084013567ffffffffffffffff811115610a7f57600080fd5b8401601f81018613610a9057600080fd5b8035610a9e61091382610c31565b80828252848201915084840189868560051b8701011115610abe57600080fd5b600094505b83851015610ae1578035835260019490940193918501918501610ac3565b5080955050505050509250929050565b600060208284031215610b0357600080fd5b81516109ab81610d13565b600060208284031215610b2057600080fd5b81356bffffffffffffffffffffffff811681146109ab57600080fd5b73ffffffffffffffffffffffffffffffffffffffff84168152600060206bffffffffffffffffffffffff85168184015260606040840152835180606085015260005b81811015610b9a57858101830151858201608001528201610b7e565b81811115610bac576000608083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160800195945050505050565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715610c2957610c29610ce4565b604052919050565b600067ffffffffffffffff821115610c4b57610c4b610ce4565b5060051b60200190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610cae577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff81168114610d2957600080fd5b5056fea164736f6c6343000806000a", } var VRFConsumerV2ABI = VRFConsumerV2MetaData.ABI @@ -292,6 +292,18 @@ func (_VRFConsumerV2 *VRFConsumerV2TransactorSession) TestRequestRandomness(keyH return _VRFConsumerV2.Contract.TestRequestRandomness(&_VRFConsumerV2.TransactOpts, keyHash, subId, minReqConfs, callbackGasLimit, numWords) } +func (_VRFConsumerV2 *VRFConsumerV2Transactor) TopUpSubscription(opts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) { + return _VRFConsumerV2.contract.Transact(opts, "topUpSubscription", amount) +} + +func (_VRFConsumerV2 *VRFConsumerV2Session) TopUpSubscription(amount *big.Int) (*types.Transaction, error) { + return _VRFConsumerV2.Contract.TopUpSubscription(&_VRFConsumerV2.TransactOpts, amount) +} + +func (_VRFConsumerV2 *VRFConsumerV2TransactorSession) TopUpSubscription(amount *big.Int) (*types.Transaction, error) { + return _VRFConsumerV2.Contract.TopUpSubscription(&_VRFConsumerV2.TransactOpts, amount) +} + func (_VRFConsumerV2 *VRFConsumerV2Transactor) UpdateSubscription(opts *bind.TransactOpts, consumers []common.Address) (*types.Transaction, error) { return _VRFConsumerV2.contract.Transact(opts, "updateSubscription", consumers) } @@ -323,6 +335,8 @@ type VRFConsumerV2Interface interface { TestRequestRandomness(opts *bind.TransactOpts, keyHash [32]byte, subId uint64, minReqConfs uint16, callbackGasLimit uint32, numWords uint32) (*types.Transaction, error) + TopUpSubscription(opts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) + UpdateSubscription(opts *bind.TransactOpts, consumers []common.Address) (*types.Transaction, error) Address() common.Address diff --git a/core/internal/gethwrappers/generated/vrf_coordinator_v2/vrf_coordinator_v2.go b/core/internal/gethwrappers/generated/vrf_coordinator_v2/vrf_coordinator_v2.go index 72a81d53468..0023846f4a7 100644 --- a/core/internal/gethwrappers/generated/vrf_coordinator_v2/vrf_coordinator_v2.go +++ b/core/internal/gethwrappers/generated/vrf_coordinator_v2/vrf_coordinator_v2.go @@ -29,6 +29,18 @@ var ( _ = event.NewSubscription ) +type VRFCoordinatorV2FeeConfig struct { + FulfillmentFlatFeeLinkPPMTier1 uint32 + FulfillmentFlatFeeLinkPPMTier2 uint32 + FulfillmentFlatFeeLinkPPMTier3 uint32 + FulfillmentFlatFeeLinkPPMTier4 uint32 + FulfillmentFlatFeeLinkPPMTier5 uint32 + ReqsForTier2 *big.Int + ReqsForTier3 *big.Int + ReqsForTier4 *big.Int + ReqsForTier5 *big.Int +} + type VRFCoordinatorV2RequestCommitment struct { BlockNum uint64 SubId uint64 @@ -50,8 +62,8 @@ type VRFProof struct { } var VRFCoordinatorV2MetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"blockhashStore\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkEthFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"minimumSubscriptionBalance\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FundsRecovered\",\"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\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"}],\"name\":\"ProvingKeyDeregistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"}],\"name\":\"ProvingKeyRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"output\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"RandomWordsFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RandomWordsRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"SubscriptionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionDefunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BLOCKHASH_STORE\",\"outputs\":[{\"internalType\":\"contractBlockhashStoreInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK_ETH_FEED\",\"outputs\":[{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_CONSUMERS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_NUM_WORDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REQUEST_CONFIRMATIONS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MIN_GAS_LIMIT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"}],\"name\":\"acceptSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"addConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"cancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createSubscription\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"defundSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"deregisterProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256[2]\",\"name\":\"pk\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"gamma\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"c\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"seed\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"uWitness\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"cGammaWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"sHashWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"zInv\",\"type\":\"uint256\"}],\"internalType\":\"structVRF.Proof\",\"name\":\"proof\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"blockNum\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"internalType\":\"structVRFCoordinatorV2.RequestCommitment\",\"name\":\"rc\",\"type\":\"tuple\"}],\"name\":\"fulfillRandomWords\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"}],\"name\":\"getCommitment\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"minimumSubscriptionBalance\",\"type\":\"uint96\"},{\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"}],\"name\":\"getSubscription\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicKey\",\"type\":\"uint256[2]\"}],\"name\":\"hashOfKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"registerProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"removeConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"}],\"name\":\"requestRandomWords\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"requestSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"minimumSubscriptionBalance\",\"type\":\"uint96\"},{\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"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\":\"pure\",\"type\":\"function\"}]", - Bin: "0x60e06040523480156200001157600080fd5b5060405162005bcc38038062005bcc8339810160408190526200003491620001b1565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000e8565b5050506001600160601b0319606093841b811660805290831b811660a052911b1660c052620001fb565b6001600160a01b038116331415620001435760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620001ac57600080fd5b919050565b600080600060608486031215620001c757600080fd5b620001d28462000194565b9250620001e26020850162000194565b9150620001f26040850162000194565b90509250925092565b60805160601c60a05160601c60c05160601c6159606200026c6000396000818161032f01526138bc01526000818161049b0152613d0a01526000818161027601528181610bf10152818161140c0152818161270801528181613228015281816133d6015261352c01526159606000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80637ce1ffeb1161010f578063a4c0ed36116100a2578063caf70c4a11610071578063caf70c4a14610603578063d7ae1d3014610616578063e72f6e3014610629578063f2fde38b1461063c57600080fd5b8063a4c0ed3614610483578063ad17836114610496578063af198b97146104bd578063c3f909d4146104d057600080fd5b80638da5cb5b116100de5780638da5cb5b1461040f5780639f87fad71461042d578063a21a23e414610440578063a47c76961461046157600080fd5b80637ce1ffeb1461039f57806382359740146103a8578063862545d1146103bb57806386fe91c7146103ce57600080fd5b80635d3b1d301161018757806369bcdb7d1161015657806369bcdb7d146103515780636f64f03f146103715780637341c10c1461038457806379ba50971461039757600080fd5b80635d3b1d30146102ee57806364d51a2a1461030f57806366316d8d14610317578063689c45171461032a57600080fd5b8063181f5a77116101c3578063181f5a77146102325780631b6b6d231461027157806340d6bb82146102bd57806344380d1f146102db57600080fd5b806304c357cb146101ea57806308821d58146101ff57806315c48b8414610212575b600080fd5b6101fd6101f83660046153c6565b61064f565b005b6101fd61020d36600461517a565b61086c565b61021a60c881565b60405161ffff90911681526020015b60405180910390f35b604080518082018252601681527f565246436f6f7264696e61746f72563220312e302e30000000000000000000006020820152905161022991906155ac565b6102987f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610229565b6102c66101f481565b60405163ffffffff9091168152602001610229565b6101fd6102e93660046153f0565b610990565b6103016102fc3660046151ed565b610d6f565b604051908152602001610229565b61021a606481565b6101fd610325366004615150565b61129f565b6102987f000000000000000000000000000000000000000000000000000000000000000081565b61030161035f366004615392565b60009081526007602052604090205490565b6101fd61037f366004615095565b611520565b6101fd6103923660046153c6565b611639565b6101fd611915565b61030161138881565b6101fd6103b63660046153ab565b611a12565b6101fd6103c9366004615313565b611c4f565b6004546103f2906801000000000000000090046bffffffffffffffffffffffff1681565b6040516bffffffffffffffffffffffff9091168152602001610229565b60005473ffffffffffffffffffffffffffffffffffffffff16610298565b6101fd61043b3660046153c6565b611ece565b61044861236b565b60405167ffffffffffffffff9091168152602001610229565b61047461046f3660046153ab565b612549565b6040516102299392919061564c565b6101fd6104913660046150c9565b612691565b6102987f000000000000000000000000000000000000000000000000000000000000000081565b6101fd6104cb36600461524b565b6129cb565b6105ad6040805160e08101825260095461ffff811680835262010000820463ffffffff908116602085018190526601000000000000840482169585018690526a010000000000000000000084048216606086018190526e010000000000000000000000000000850490921660808601819052720100000000000000000000000000000000000085046bffffffffffffffffffffffff1660a087018190527e0100000000000000000000000000000000000000000000000000000000000090950460ff16151560c09096019590955260085492969095949193919290565b6040805161ffff909816885263ffffffff96871660208901529486169487019490945291841660608601529290921660808401526bffffffffffffffffffffffff90911660a083015260c082015260e001610229565b610301610611366004615196565b612e7c565b6101fd6106243660046153c6565b612eac565b6101fd61063736600461507a565b61339d565b6101fd61064a36600461507a565b613601565b67ffffffffffffffff821660009081526003602052604090205482906c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16806106c8576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614610734576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024015b60405180910390fd5b6009547e01000000000000000000000000000000000000000000000000000000000000900460ff1615610793576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff8481169116146108665767ffffffffffffffff841660008181526003602090815260409182902060010180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88169081179091558251338152918201527f69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be91015b60405180910390a25b50505050565b610874613615565b6040805180820182526000916108a3919084906002908390839080828437600092019190915250612e7c915050565b60008181526005602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1680610905576040517f77f5b84c0000000000000000000000000000000000000000000000000000000081526004810183905260240161072b565b6000828152600560205260409081902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555173ffffffffffffffffffffffffffffffffffffffff8216907f72be339577868f868798bac2c93e52d6f034fef4689a9848996c14ebb7416c0d906109839085815260200190565b60405180910390a2505050565b67ffffffffffffffff831660009081526003602052604090205483906c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1680610a09576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614610a70576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260240161072b565b6009547e01000000000000000000000000000000000000000000000000000000000000900460ff1615610acf576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff85166000908152600360205260409020546bffffffffffffffffffffffff80851691161015610b33576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8516600090815260036020526040812080546bffffffffffffffffffffffff1691859190610b6a83856157c6565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555083600460088282829054906101000a90046bffffffffffffffffffffffff16610bc191906157c6565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb86866040518363ffffffff1660e01b8152600401610c7992919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b602060405180830381600087803b158015610c9357600080fd5b505af1158015610ca7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ccb91906151b2565b610d01576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff86166000818152600360209081526040918290205482518581526bffffffffffffffffffffffff909116918101919091527ffd47f8dea665a4ba9fd5ac2d5d60d09da40628a32c3e5b3ee970f0a36b43dd3291015b60405180910390a2505050505050565b6009546000907e01000000000000000000000000000000000000000000000000000000000000900460ff1615610dd1576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff85166000908152600360205260409020546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16610e47576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260026020908152604080832067ffffffffffffffff808a16855290835292819020815180830190925254808416808352680100000000000000009091049093169181019190915290610edd576040517ff0019fe600000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8716600482015233602482015260440161072b565b60095461ffff9081169086161080610ef9575060c861ffff8616115b15610f49576009546040517fa738697600000000000000000000000000000000000000000000000000000000815261ffff8088166004830152909116602482015260c8604482015260640161072b565b60095467ffffffffffffffff87166000908152600360205260409020546bffffffffffffffffffffffff7201000000000000000000000000000000000000909204821691161015610fc6576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60095463ffffffff660100000000000090910481169085161115611035576009546040517ff5d7e01e00000000000000000000000000000000000000000000000000000000815263ffffffff80871660048301526601000000000000909204909116602482015260440161072b565b6101f463ffffffff84161115611087576040517f47386bec00000000000000000000000000000000000000000000000000000000815263ffffffff841660048201526101f4602482015260440161072b565b60008160200151600161109a919061570b565b60408051602081018b9052339181019190915267ffffffffffffffff808a1660608301528216608082015290915060009060a001604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815282825280516020918201209083018c90529082018190529150600090606001604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181528282528051602091820120908301819052439183019190915267ffffffffffffffff8b16606083015263ffffffff808a166080840152881660a08301523360c0830152915060e001604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152828252805160209182012060008581526007835283902055838352820184905267ffffffffffffffff8b169082015261ffff8916606082015263ffffffff8089166080830152871660a082015233908b907f63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a97729060c00160405180910390a333600090815260026020908152604080832067ffffffffffffffff808e168552925290912080549190941668010000000000000000027fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff909116179092555091505095945050505050565b6009547e01000000000000000000000000000000000000000000000000000000000000900460ff16156112fe576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600660205260409020546bffffffffffffffffffffffff80831691161015611358576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260066020526040812080548392906113859084906bffffffffffffffffffffffff166157c6565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080600460088282829054906101000a90046bffffffffffffffffffffffff166113dc91906157c6565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b815260040161149492919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b602060405180830381600087803b1580156114ae57600080fd5b505af11580156114c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e691906151b2565b61151c576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b611528613615565b604080518082018252600091611557919084906002908390839080828437600092019190915250612e7c915050565b60008181526005602052604090205490915073ffffffffffffffffffffffffffffffffffffffff16156115b9576040517f4a0b8fa70000000000000000000000000000000000000000000000000000000081526004810182905260240161072b565b60008181526005602090815260409182902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff871690811790915591518381527fe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b89101610983565b67ffffffffffffffff821660009081526003602052604090205482906c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16806116b2576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614611719576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260240161072b565b6009547e01000000000000000000000000000000000000000000000000000000000000900460ff1615611778576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8416600090815260036020526040902060020154606414156117cf576040517f05a48e0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260026020908152604080832067ffffffffffffffff8089168552925290912054161561181657610866565b60408051808201825267ffffffffffffffff8681168083526000602080850182815273ffffffffffffffffffffffffffffffffffffffff8a1680845260028084528885208686528452888520975188549351881668010000000000000000027fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941697169690961791909117909555600381528582209093018054600181018255908252908390200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905592519182527f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e0910161085d565b60015473ffffffffffffffffffffffffffffffffffffffff163314611996576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e657200000000000000000000604482015260640161072b565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6009547e01000000000000000000000000000000000000000000000000000000000000900460ff1615611a71576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81166000908152600360205260409020546c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16611ae7576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff163314611b895767ffffffffffffffff8116600090815260036020526040908190206001015490517fd084e97500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116600482015260240161072b565b67ffffffffffffffff811660008181526003602090815260409182902080546bffffffffffffffffffffffff81166c0100000000000000000000000033818102929092178455600190930180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905584519290910473ffffffffffffffffffffffffffffffffffffffff16808352928201529092917f6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f0910160405180910390a25050565b611c57613615565b60c861ffff88161115611caa576040517fa738697600000000000000000000000000000000000000000000000000000000815261ffff881660048201819052602482015260c8604482015260640161072b565b60008113611ce7576040517f43d4cf660000000000000000000000000000000000000000000000000000000081526004810182905260240161072b565b6040805160e0808201835261ffff8a1680835263ffffffff8a811660208086018290528b83168688018190528b84166060808901829052948c166080808a018290526bffffffffffffffffffffffff8d1660a0808c01829052600060c09c8d0152600980547fffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000168b17620100008a02177fffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffff16660100000000000088027fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff16176a01000000000000000000008702177fffff00000000000000000000000000000000ffffffffffffffffffffffffffff166e01000000000000000000000000000086027fffff000000000000000000000000ffffffffffffffffffffffffffffffffffff161772010000000000000000000000000000000000008402177fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16905560088e90558c51998a5295890196909652998701929092529385019390935295830191909152938101939093529082018390527f56583fc0e609f432152501e64e8a4aaf7ecc715b33697f1cacb307f4b562d2c4910160405180910390a150505050505050565b67ffffffffffffffff821660009081526003602052604090205482906c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1680611f47576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614611fae576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260240161072b565b6009547e01000000000000000000000000000000000000000000000000000000000000900460ff161561200d576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260026020908152604080832067ffffffffffffffff8089168552925290912054166120a8576040517ff0019fe600000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015260440161072b565b67ffffffffffffffff841660009081526003602090815260408083206002018054825181850281018501909352808352919290919083018282801561212357602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116120f8575b5050505050905060006001825161213a91906157af565b905060005b82518110156122d9578573ffffffffffffffffffffffffffffffffffffffff16838281518110612171576121716158f5565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156122c75760008383815181106121a9576121a96158f5565b6020026020010151905080600360008a67ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060020183815481106121ef576121ef6158f5565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff949094169390931790925567ffffffffffffffff8a168152600390915260409020600201805480612269576122696158c6565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055019055506122d9565b806122d1816157f3565b91505061213f565b5073ffffffffffffffffffffffffffffffffffffffff8516600081815260026020908152604080832067ffffffffffffffff8b168085529083529281902080547fffffffffffffffffffffffffffffffff000000000000000000000000000000001690555192835290917f182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b9101610d5f565b6009546000907e01000000000000000000000000000000000000000000000000000000000000900460ff16156123cd576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805467ffffffffffffffff169060006123e78361582c565b82546101009290920a67ffffffffffffffff81810219909316918316021790915560045416905060008060405190808252806020026020018201604052801561243a578160200160208202803683370190505b506040805160808101825260008082523360208084019182528385018381526060850187815267ffffffffffffffff8a1685526003835295909320845192516bffffffffffffffffffffffff9093166c0100000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9485160217815592516001840180547fffffffffffffffffffffffff00000000000000000000000000000000000000001691909316179091559251805194955091939092612501926002850192910190614db1565b505060405133815267ffffffffffffffff841691507f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf9060200160405180910390a250905090565b67ffffffffffffffff811660009081526003602052604081205481906060906c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff166125c4576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff84166000908152600360209081526040918290208054600290910180548451818502810185019095528085526bffffffffffffffffffffffff8316946c0100000000000000000000000090930473ffffffffffffffffffffffffffffffffffffffff169391929183919083018282801561267d57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612652575b505050505090509250925092509193909250565b6009547e01000000000000000000000000000000000000000000000000000000000000900460ff16156126f0576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461275f576040517f44b0e3c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208114612799576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006127a7828401846153ab565b67ffffffffffffffff81166000908152600360205260409020549091506c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff16612820576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff6c01000000000000000000000000909104811690861681146128b5576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260240161072b565b67ffffffffffffffff8216600090815260036020526040812080546bffffffffffffffffffffffff16918791906128ec8385615737565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555085600460088282829054906101000a90046bffffffffffffffffffffffff166129439190615737565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508267ffffffffffffffff167fd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f88288846129aa91906156f3565b6040805192835260208301919091520160405180910390a250505050505050565b6009547e01000000000000000000000000000000000000000000000000000000000000900460ff1615612a2a576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005a90506000806000612a3e8686613698565b9250925092506000856060015163ffffffff1667ffffffffffffffff811115612a6957612a69615924565b604051908082528060200260200182016040528015612a92578160200160208202803683370190505b50905060005b866060015163ffffffff16811015612b065760408051602081018590529081018290526060016040516020818303038152906040528051906020012060001c828281518110612ae957612ae96158f5565b602090810291909101015280612afe816157f3565b915050612a98565b506000838152600760205260408082208290556080890151905182917f1fe543e30000000000000000000000000000000000000000000000000000000091612b5391908690602401615633565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152905060005a9050886040015163ffffffff16811015612c30576040808a015190517fd17e76640000000000000000000000000000000000000000000000000000000081526004810183905263ffffffff909116602482015260440161072b565b600980547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179055604089015160808a0151600091612c999163ffffffff90911690856139e7565b9050867f969e72fbacf24da85b4bce2a3cef3d8dc2497b1750c4cc5a06b52c10413383378683604051612ccd929190615588565b60405180910390a2600980547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690819055600090612d2f908b9063ffffffff6e0100000000000000000000000000008204811691620100009004163a613a35565b6020808d015167ffffffffffffffff166000908152600390915260409020549091506bffffffffffffffffffffffff80831691161015612d9b576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020808c015167ffffffffffffffff1660009081526003909152604081208054839290612dd79084906bffffffffffffffffffffffff166157c6565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008b81526005602090815260408083205473ffffffffffffffffffffffffffffffffffffffff1683526006909152812080548594509092612e4091859116615737565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550505050505050505050505050565b600081604051602001612e8f919061557a565b604051602081830303815290604052805190602001209050919050565b67ffffffffffffffff821660009081526003602052604090205482906c01000000000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1680612f25576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614612f8c576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260240161072b565b6009547e01000000000000000000000000000000000000000000000000000000000000900460ff1615612feb576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff84166000908152600360209081526040808320815160808101835281546bffffffffffffffffffffffff8116825273ffffffffffffffffffffffffffffffffffffffff6c01000000000000000000000000909104811682860152600183015416818401526002820180548451818702810187019095528085529194929360608601939092908301828280156130bf57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311613094575b5050509190925250508151919250600090505b8260600151518110156131785760026000846060015183815181106130f9576130f96158f5565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040908101600090812067ffffffffffffffff8b168252909252902080547fffffffffffffffffffffffffffffffff0000000000000000000000000000000016905580613170816157f3565b9150506130d2565b5067ffffffffffffffff861660009081526003602052604081208181556001810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055906131cd6002830182614e3b565b505080600460088282829054906101000a90046bffffffffffffffffffffffff166131f891906157c6565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb86836bffffffffffffffffffffffff166040518363ffffffff1660e01b81526004016132b092919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b602060405180830381600087803b1580156132ca57600080fd5b505af11580156132de573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061330291906151b2565b613338576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805173ffffffffffffffffffffffffffffffffffffffff871681526bffffffffffffffffffffffff8316602082015267ffffffffffffffff8816917fe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd498159101610d5f565b6133a5613615565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b15801561342d57600080fd5b505afa158015613441573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061346591906151d4565b6004549091506801000000000000000090046bffffffffffffffffffffffff16818111156134c9576040517fa99da302000000000000000000000000000000000000000000000000000000008152600481018290526024810183905260440161072b565b818110156135fc5760006134dd82846157af565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018390529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb90604401602060405180830381600087803b15801561357257600080fd5b505af1158015613586573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135aa91906151b2565b506040805173ffffffffffffffffffffffffffffffffffffffff86168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600910160405180910390a1505b505050565b613609613615565b61361281613b3d565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314613696576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640161072b565b565b60008060006136aa8560000151612e7c565b60008181526005602052604090205490935073ffffffffffffffffffffffffffffffffffffffff168061370c576040517f77f5b84c0000000000000000000000000000000000000000000000000000000081526004810185905260240161072b565b608086015160405161372b918691602001918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060008181526007909352912054909350806137a8576040517f3688124a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85516020808801516040808a015160608b015160808c01519251613821968b96909594910195865267ffffffffffffffff948516602087015292909316604085015263ffffffff908116606085015291909116608083015273ffffffffffffffffffffffffffffffffffffffff1660a082015260c00190565b60405160208183030381529060405280519060200120811461386f576040517fd529142c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855167ffffffffffffffff1640806139935786516040517fe9413d3800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063e9413d389060240160206040518083038186803b15801561391357600080fd5b505afa158015613927573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061394b91906151d4565b9050806139935786516040517f175dadad00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff909116600482015260240161072b565b60008860800151826040516020016139b5929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c90506139da8982613c33565b9450505050509250925092565b60005a6113888110156139f957600080fd5b611388810390508460408204820311613a1157600080fd5b50823b613a1d57600080fd5b60008083516020850160008789f190505b9392505050565b600080613a40613cbc565b905060008113613a7f576040517f43d4cf660000000000000000000000000000000000000000000000000000000081526004810182905260240161072b565b6000815a613a8d89896156f3565b613a9791906157af565b613aa986670de0b6b3a7640000615772565b613ab39190615772565b613abd919061575e565b90506000613ad663ffffffff871664e8d4a51000615772565b9050613aee816b033b2e3c9fd0803ce80000006157af565b821115613b27576040517fe80fa38100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613b3181836156f3565b98975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116331415613bbd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161072b565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000613c678360000151846020015185604001518660600151868860a001518960c001518a60e001518b6101000151613dd3565b60038360200151604051602001613c7f92919061561f565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b600954604080517ffeaf968c00000000000000000000000000000000000000000000000000000000815290516000926a0100000000000000000000900463ffffffff169182151591849182917f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169163feaf968c9160048083019260a0929190829003018186803b158015613d6557600080fd5b505afa158015613d79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d9d9190615433565b509450909250849150508015613dc15750613db882426157af565b8463ffffffff16105b15613dcb57506008545b949350505050565b613ddc896140aa565b613e42576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f7075626c6963206b6579206973206e6f74206f6e206375727665000000000000604482015260640161072b565b613e4b886140aa565b613eb1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f67616d6d61206973206e6f74206f6e2063757276650000000000000000000000604482015260640161072b565b613eba836140aa565b613f20576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e206375727665000000604482015260640161072b565b613f29826140aa565b613f8f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f73486173685769746e657373206973206e6f74206f6e20637572766500000000604482015260640161072b565b613f9b878a8887614205565b614001576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f6164647228632a706b2b732a6729213d5f755769746e65737300000000000000604482015260640161072b565b600061400d8a876143a8565b90506000614020898b878b868989614412565b90506000614031838d8d8a8661459a565b9050808a1461409c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f696e76616c69642070726f6f6600000000000000000000000000000000000000604482015260640161072b565b505050505050505050505050565b80516000907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f11614137576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c696420782d6f7264696e6174650000000000000000000000000000604482015260640161072b565b60208201517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f116141c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c696420792d6f7264696e6174650000000000000000000000000000604482015260640161072b565b60208201517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f9080096141fe8360005b60200201516145f8565b1492915050565b600073ffffffffffffffffffffffffffffffffffffffff8216614284576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f626164207769746e657373000000000000000000000000000000000000000000604482015260640161072b565b60208401516000906001161561429b57601c61429e565b601b5b905060007ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418587600060200201510986517ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141918203925060009190890987516040805160008082526020820180845287905260ff88169282019290925260608101929092526080820183905291925060019060a0016020604051602081039080840390855afa158015614355573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff9081169088161495505050505050949350505050565b6143b0614e59565b6143dd600184846040516020016143c993929190615559565b604051602081830303815290604052614650565b90505b6143e9816140aa565b61440c57805160408051602081019290925261440591016143c9565b90506143e0565b92915050565b61441a614e59565b825186517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f90819006910614156144ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e63740000604482015260640161072b565b6144b88789886146b9565b61451e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4669727374206d756c20636865636b206661696c656400000000000000000000604482015260640161072b565b6145298486856146b9565b61458f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5365636f6e64206d756c20636865636b206661696c6564000000000000000000604482015260640161072b565b613b31868484614846565b6000600286868685876040516020016145b8969594939291906154e7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209695505050505050565b6000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80848509840990507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f600782089392505050565b614658614e59565b61466182614975565b81526146766146718260006141f4565b6149ca565b6020820181905260029006600114156146b4576020810180517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0390525b919050565b600082614722576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f7a65726f207363616c6172000000000000000000000000000000000000000000604482015260640161072b565b8351602085015160009061473890600290615854565b1561474457601c614747565b601b5b905060007ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418387096040805160008082526020820180845281905260ff86169282019290925260608101869052608081018390529192509060019060a0016020604051602081039080840390855afa1580156147c7573d6000803e3d6000fd5b5050506020604051035190506000866040516020016147e691906154d5565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012073ffffffffffffffffffffffffffffffffffffffff92831692169190911498975050505050505050565b61484e614e59565b83516020808601518551918601516000938493849361486f93909190614a04565b919450925090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f858209600114614903576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e765a206d75737420626520696e7665727365206f66207a00000000000000604482015260640161072b565b60405180604001604052807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8061493c5761493c615897565b87860981526020017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8785099052979650505050505050565b805160208201205b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f81106146b45760408051602080820193909352815180820384018152908201909152805191012061497d565b600061440c8260026149fd7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f60016156f3565b901c614b9a565b60008080600180827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f897ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f038808905060007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f038a0890506000614aac83838585614c8e565b9098509050614abd88828e88614ce6565b9098509050614ace88828c87614ce6565b90985090506000614ae18d878b85614ce6565b9098509050614af288828686614c8e565b9098509050614b0388828e89614ce6565b9098509050818114614b86577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f818a0998507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f82890997507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183099650614b8a565b8196505b5050505050509450945094915050565b600080614ba5614e77565b6020808252818101819052604082015260608101859052608081018490527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f60a0820152614bf1614e95565b60208160c08460057ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa925082614c84576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6269674d6f64457870206661696c757265210000000000000000000000000000604482015260640161072b565b5195945050505050565b6000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8487097ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8487099097909650945050505050565b600080807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f878509905060007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f87877ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f030990507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183087ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f86890990999098509650505050505050565b828054828255906000526020600020908101928215614e2b579160200282015b82811115614e2b57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190614dd1565b50614e37929150614eb3565b5090565b50805460008255906000526020600020908101906136129190614eb3565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b80821115614e375760008155600101614eb4565b803573ffffffffffffffffffffffffffffffffffffffff811681146146b457600080fd5b806040810183101561440c57600080fd5b600082601f830112614f0e57600080fd5b6040516040810181811067ffffffffffffffff82111715614f3157614f31615924565b8060405250808385604086011115614f4857600080fd5b60005b6002811015614f6a578135835260209283019290910190600101614f4b565b509195945050505050565b600060a08284031215614f8757600080fd5b60405160a0810181811067ffffffffffffffff82111715614faa57614faa615924565b604052905080614fb98361502c565b8152614fc76020840161502c565b6020820152614fd860408401615018565b6040820152614fe960608401615018565b6060820152614ffa60808401614ec8565b60808201525092915050565b803561ffff811681146146b457600080fd5b803563ffffffff811681146146b457600080fd5b803567ffffffffffffffff811681146146b457600080fd5b805169ffffffffffffffffffff811681146146b457600080fd5b80356bffffffffffffffffffffffff811681146146b457600080fd5b60006020828403121561508c57600080fd5b613a2e82614ec8565b600080606083850312156150a857600080fd5b6150b183614ec8565b91506150c08460208501614eec565b90509250929050565b600080600080606085870312156150df57600080fd5b6150e885614ec8565b935060208501359250604085013567ffffffffffffffff8082111561510c57600080fd5b818701915087601f83011261512057600080fd5b81358181111561512f57600080fd5b88602082850101111561514157600080fd5b95989497505060200194505050565b6000806040838503121561516357600080fd5b61516c83614ec8565b91506150c06020840161505e565b60006040828403121561518c57600080fd5b613a2e8383614eec565b6000604082840312156151a857600080fd5b613a2e8383614efd565b6000602082840312156151c457600080fd5b81518015158114613a2e57600080fd5b6000602082840312156151e657600080fd5b5051919050565b600080600080600060a0868803121561520557600080fd5b853594506152156020870161502c565b935061522360408701615006565b925061523160608701615018565b915061523f60808701615018565b90509295509295909350565b60008082840361024081121561526057600080fd5b6101a08082121561527057600080fd5b6152786156c9565b91506152848686614efd565b82526152938660408701614efd565b60208301526080850135604083015260a0850135606083015260c085013560808301526152c260e08601614ec8565b60a08301526101006152d687828801614efd565b60c08401526152e9876101408801614efd565b60e0840152610180860135818401525081935061530886828701614f75565b925050509250929050565b600080600080600080600060e0888a03121561532e57600080fd5b61533788615006565b965061534560208901615018565b955061535360408901615018565b945061536160608901615018565b935061536f60808901615018565b925061537d60a0890161505e565b915060c0880135905092959891949750929550565b6000602082840312156153a457600080fd5b5035919050565b6000602082840312156153bd57600080fd5b613a2e8261502c565b600080604083850312156153d957600080fd5b6153e28361502c565b91506150c060208401614ec8565b60008060006060848603121561540557600080fd5b61540e8461502c565b925061541c60208501614ec8565b915061542a6040850161505e565b90509250925092565b600080600080600060a0868803121561544b57600080fd5b61545486615044565b945060208601519350604086015192506060860151915061523f60808701615044565b8060005b600281101561086657815184526020938401939091019060010161547b565b600081518084526020808501945080840160005b838110156154ca578151875295820195908201906001016154ae565b509495945050505050565b6154df8183615477565b604001919050565b8681526154f76020820187615477565b6155046060820186615477565b61551160a0820185615477565b61551e60e0820184615477565b60609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166101208201526101340195945050505050565b8381526155696020820184615477565b606081019190915260800192915050565b6040810161440c8284615477565b60408152600061559b604083018561549a565b905082151560208301529392505050565b600060208083528351808285015260005b818110156155d9578581018301518582016040015282016155bd565b818111156155eb576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b82815260608101613a2e6020830184615477565b828152604060208201526000613dcb604083018461549a565b6000606082016bffffffffffffffffffffffff86168352602073ffffffffffffffffffffffffffffffffffffffff8087168286015260606040860152828651808552608087019150838801945060005b818110156156ba57855184168352948401949184019160010161569c565b50909998505050505050505050565b604051610120810167ffffffffffffffff811182821017156156ed576156ed615924565b60405290565b6000821982111561570657615706615868565b500190565b600067ffffffffffffffff80831681851680830382111561572e5761572e615868565b01949350505050565b60006bffffffffffffffffffffffff80831681851680830382111561572e5761572e615868565b60008261576d5761576d615897565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156157aa576157aa615868565b500290565b6000828210156157c1576157c1615868565b500390565b60006bffffffffffffffffffffffff838116908316818110156157eb576157eb615868565b039392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561582557615825615868565b5060010190565b600067ffffffffffffffff8083168181141561584a5761584a615868565b6001019392505050565b60008261586357615863615897565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"blockhashStore\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkEthFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier1\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier2\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier3\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier4\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier5\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier2\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier3\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier4\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier5\",\"type\":\"uint24\"}],\"indexed\":false,\"internalType\":\"structVRFCoordinatorV2.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FundsRecovered\",\"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\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"}],\"name\":\"ProvingKeyDeregistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"}],\"name\":\"ProvingKeyRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"output\",\"type\":\"uint256[]\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"RandomWordsFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RandomWordsRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"SubscriptionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionDefunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BLOCKHASH_STORE\",\"outputs\":[{\"internalType\":\"contractBlockhashStoreInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK_ETH_FEED\",\"outputs\":[{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_CONSUMERS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_NUM_WORDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REQUEST_CONFIRMATIONS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"}],\"name\":\"acceptSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"addConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"cancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createSubscription\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"deregisterProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256[2]\",\"name\":\"pk\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"gamma\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"c\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"seed\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"uWitness\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"cGammaWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"sHashWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"zInv\",\"type\":\"uint256\"}],\"internalType\":\"structVRF.Proof\",\"name\":\"proof\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"blockNum\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"internalType\":\"structVRFCoordinatorV2.RequestCommitment\",\"name\":\"rc\",\"type\":\"tuple\"}],\"name\":\"fulfillRandomWords\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"}],\"name\":\"getCommitment\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"reqCount\",\"type\":\"uint64\"}],\"name\":\"getFeeTier\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"}],\"name\":\"getSubscription\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicKey\",\"type\":\"uint256[2]\"}],\"name\":\"hashOfKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"}],\"name\":\"ownerCancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"}],\"name\":\"pendingRequestExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"registerProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"removeConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"}],\"name\":\"requestRandomWords\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subId\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"requestSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_config\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"reentrancyLock\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_feeConfig\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier1\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier2\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier3\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier4\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier5\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier2\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier3\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier4\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier5\",\"type\":\"uint24\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_provingKeyHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier1\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier2\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier3\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier4\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkPPMTier5\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier2\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier3\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier4\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"reqsForTier5\",\"type\":\"uint24\"}],\"internalType\":\"structVRFCoordinatorV2.FeeConfig\",\"name\":\"feeConfig\",\"type\":\"tuple\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"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\":\"pure\",\"type\":\"function\"}]", + Bin: "0x60e06040523480156200001157600080fd5b5060405162006058380380620060588339810160408190526200003491620001b1565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000e8565b5050506001600160601b0319606093841b811660805290831b811660a052911b1660c052620001fb565b6001600160a01b038116331415620001435760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620001ac57600080fd5b919050565b600080600060608486031215620001c757600080fd5b620001d28462000194565b9250620001e26020850162000194565b9150620001f26040850162000194565b90509250925092565b60805160601c60a05160601c60c05160601c615df362000265600039600081816104050152613beb015260008181610689015261403601526000818161034c01528181611531015281816125140152818161303e0152818161319401526138480152615df36000f3fe608060405234801561001057600080fd5b50600436106102265760003560e01c806379ba50971161012a578063ad178361116100bd578063d7ae1d301161008c578063e72f6e3011610071578063e72f6e301461070a578063e82ad7d41461071d578063f2fde38b1461074057600080fd5b8063d7ae1d30146106e4578063d98e620e146106f757600080fd5b8063ad17836114610684578063af198b97146106ab578063caf70c4a146106be578063d2f9f9a7146106d157600080fd5b80639f87fad7116100f95780639f87fad71461061b578063a21a23e41461062e578063a47c76961461064f578063a4c0ed361461067157600080fd5b806379ba5097146105a157806382359740146105a957806386fe91c7146105bc5780638da5cb5b146105fd57600080fd5b80634cb48a54116101bd578063689c45171161018c5780636b6feccc116101715780636b6feccc146104475780636f64f03f1461057b5780637341c10c1461058e57600080fd5b8063689c45171461040057806369bcdb7d1461042757600080fd5b80634cb48a54146103b15780635d3b1d30146103c457806364d51a2a146103e557806366316d8d146103ed57600080fd5b806315c48b84116101f957806315c48b84146102ed578063181f5a77146103085780631b6b6d231461034757806340d6bb821461039357600080fd5b806302bcc5b61461022b57806304c357cb14610240578063088070f51461025357806308821d58146102da575b600080fd5b61023e6102393660046157a4565b610753565b005b61023e61024e3660046157bf565b6107ff565b600b5461029d9061ffff81169063ffffffff62010000820481169160ff66010000000000008204169167010000000000000082048116916b01000000000000000000000090041685565b6040805161ffff909616865263ffffffff9485166020870152921515928501929092528216606084015216608082015260a0015b60405180910390f35b61023e6102e83660046154b5565b6109f4565b6102f560c881565b60405161ffff90911681526020016102d1565b604080518082018252601681527f565246436f6f7264696e61746f72563220312e302e3000000000000000000000602082015290516102d19190615962565b61036e7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102d1565b61039c6101f481565b60405163ffffffff90911681526020016102d1565b61023e6103bf36600461564e565b610bd3565b6103d76103d2366004615528565b610fca565b6040519081526020016102d1565b6102f5606481565b61023e6103fb36600461546d565b6113dc565b61036e7f000000000000000000000000000000000000000000000000000000000000000081565b6103d761043536600461578b565b60009081526009602052604090205490565b600c5461051d9063ffffffff8082169164010000000081048216916801000000000000000082048116916c0100000000000000000000000081048216917001000000000000000000000000000000008204169062ffffff7401000000000000000000000000000000000000000082048116917701000000000000000000000000000000000000000000000081048216917a01000000000000000000000000000000000000000000000000000082048116917d01000000000000000000000000000000000000000000000000000000000090041689565b6040805163ffffffff9a8b168152988a1660208a015296891696880196909652938716606087015291909516608085015262ffffff94851660a0850152841660c0840152831660e083015291909116610100820152610120016102d1565b61023e6105893660046153b2565b611645565b61023e61059c3660046157bf565b61178f565b61023e611a1d565b61023e6105b73660046157a4565b611b1a565b6005546105e0906801000000000000000090046bffffffffffffffffffffffff1681565b6040516bffffffffffffffffffffffff90911681526020016102d1565b60005473ffffffffffffffffffffffffffffffffffffffff1661036e565b61023e6106293660046157bf565b611d14565b610636612194565b60405167ffffffffffffffff90911681526020016102d1565b61066261065d3660046157a4565b612384565b6040516102d193929190615adf565b61023e61067f3660046153e6565b6124b5565b61036e7f000000000000000000000000000000000000000000000000000000000000000081565b6105e06106b9366004615586565b6127b7565b6103d76106cc3660046154d1565b612c5e565b61039c6106df3660046157a4565b612c8e565b61023e6106f23660046157bf565b612e83565b6103d761070536600461578b565b612fe4565b61023e610718366004615397565b613005565b61073061072b3660046157a4565b613269565b60405190151581526020016102d1565b61023e61074e366004615397565b6134c0565b61075b6134d1565b67ffffffffffffffff811660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff166107c1576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff81166000908152600360205260409020546107fc90829073ffffffffffffffffffffffffffffffffffffffff16613554565b50565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff1680610868576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff8216146108d4576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024015b60405180910390fd5b600b546601000000000000900460ff161561091b576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff8481169116146109ee5767ffffffffffffffff841660008181526003602090815260409182902060010180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88169081179091558251338152918201527f69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be91015b60405180910390a25b50505050565b6109fc6134d1565b604080518082018252600091610a2b919084906002908390839080828437600092019190915250612c5e915050565b60008181526006602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1680610a8d576040517f77f5b84c000000000000000000000000000000000000000000000000000000008152600481018390526024016108cb565b600082815260066020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b600754811015610b7d578260078281548110610ae057610ae0615d88565b90600052602060002001541415610b6b576007805460009190610b0590600190615c42565b81548110610b1557610b15615d88565b906000526020600020015490508060078381548110610b3657610b36615d88565b6000918252602090912001556007805480610b5357610b53615d59565b60019003818190600052602060002001600090559055505b80610b7581615c86565b915050610ac2565b508073ffffffffffffffffffffffffffffffffffffffff167f72be339577868f868798bac2c93e52d6f034fef4689a9848996c14ebb7416c0d83604051610bc691815260200190565b60405180910390a2505050565b610bdb6134d1565b60c861ffff87161115610c2e576040517fa738697600000000000000000000000000000000000000000000000000000000815261ffff871660048201819052602482015260c860448201526064016108cb565b60008213610c6b576040517f43d4cf66000000000000000000000000000000000000000000000000000000008152600481018390526024016108cb565b6040805160a0808201835261ffff891680835263ffffffff89811660208086018290526000868801528a831660608088018290528b85166080988901819052600b80547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001690971762010000909502949094177fffffffffffffffffffffffffffffffffff000000000000000000ffffffffffff166701000000000000009092027fffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff16919091176b010000000000000000000000909302929092179093558651600c80549489015189890151938a0151978a0151968a015160c08b015160e08c01516101008d01519588167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009099169890981764010000000093881693909302929092177fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff1668010000000000000000958716959095027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff16949094176c0100000000000000000000000098861698909802979097177fffffffffffffffffff00000000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000096909416959095027fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff16929092177401000000000000000000000000000000000000000062ffffff92831602177fffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffff1677010000000000000000000000000000000000000000000000958216959095027fffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffff16949094177a01000000000000000000000000000000000000000000000000000092851692909202919091177cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167d0100000000000000000000000000000000000000000000000000000000009390911692909202919091178155600a84905590517fc21e3bd2e0b339d2848f0dd956947a88966c242c0c0c582a33137a5c1ceb5cb291610fba9189918991899189918991906159d5565b60405180910390a1505050505050565b600b546000906601000000000000900460ff1615611014576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff851660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff1661107a576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260026020908152604080832067ffffffffffffffff808a16855292529091205416806110ea576040517ff0019fe600000000000000000000000000000000000000000000000000000000815267ffffffffffffffff871660048201523360248201526044016108cb565b600b5461ffff9081169086161080611106575060c861ffff8616115b1561115657600b546040517fa738697600000000000000000000000000000000000000000000000000000000815261ffff8088166004830152909116602482015260c860448201526064016108cb565b600b5463ffffffff62010000909104811690851611156111bd57600b546040517ff5d7e01e00000000000000000000000000000000000000000000000000000000815263ffffffff80871660048301526201000090920490911660248201526044016108cb565b6101f463ffffffff8416111561120f576040517f47386bec00000000000000000000000000000000000000000000000000000000815263ffffffff841660048201526101f460248201526044016108cb565b600061121c826001615b9e565b6040805160208082018c9052338284015267ffffffffffffffff808c16606084015284166080808401919091528351808403909101815260a08301845280519082012060c083018d905260e080840182905284518085039091018152610100909301909352815191012091925060009182916040805160208101849052439181019190915267ffffffffffffffff8c16606082015263ffffffff808b166080830152891660a08201523360c0820152919350915060e001604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152828252805160209182012060008681526009835283902055848352820183905267ffffffffffffffff8b169082015261ffff8916606082015263ffffffff8089166080830152871660a082015233908b907f63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a97729060c00160405180910390a35033600090815260026020908152604080832067ffffffffffffffff808d16855292529091208054919093167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009091161790915591505095945050505050565b600b546601000000000000900460ff1615611423576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600860205260409020546bffffffffffffffffffffffff8083169116101561147d576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260086020526040812080548392906114aa9084906bffffffffffffffffffffffff16615c59565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080600560088282829054906101000a90046bffffffffffffffffffffffff166115019190615c59565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b81526004016115b992919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b602060405180830381600087803b1580156115d357600080fd5b505af11580156115e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061160b91906154ed565b611641576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b61164d6134d1565b60408051808201825260009161167c919084906002908390839080828437600092019190915250612c5e915050565b60008181526006602052604090205490915073ffffffffffffffffffffffffffffffffffffffff16156116de576040517f4a0b8fa7000000000000000000000000000000000000000000000000000000008152600481018290526024016108cb565b600081815260066020908152604080832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88169081179091556007805460018101825594527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688909301849055518381527fe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b89101610bc6565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff16806117f8576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff82161461185f576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016108cb565b600b546601000000000000900460ff16156118a6576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8416600090815260036020526040902060020154606414156118fd576040517f05a48e0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260026020908152604080832067ffffffffffffffff80891685529252909120541615611944576109ee565b73ffffffffffffffffffffffffffffffffffffffff8316600081815260026020818152604080842067ffffffffffffffff8a1680865290835281852080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000166001908117909155600384528286209094018054948501815585529382902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001685179055905192835290917f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e091016109e5565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a9e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016108cb565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b600b546601000000000000900460ff1615611b61576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff16611bc7576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff163314611c695767ffffffffffffffff8116600090815260036020526040908190206001015490517fd084e97500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016108cb565b67ffffffffffffffff81166000818152600360209081526040918290208054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560019093018054909316909255835173ffffffffffffffffffffffffffffffffffffffff909116808252928101919091529092917f6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f0910160405180910390a25050565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff1680611d7d576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614611de4576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016108cb565b600b546601000000000000900460ff1615611e2b576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260026020908152604080832067ffffffffffffffff808916855292529091205416611ec6576040517ff0019fe600000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201526044016108cb565b67ffffffffffffffff8416600090815260036020908152604080832060020180548251818502810185019093528083529192909190830182828015611f4157602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611f16575b50505050509050600060018251611f589190615c42565b905060005b82518110156120f7578573ffffffffffffffffffffffffffffffffffffffff16838281518110611f8f57611f8f615d88565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156120e5576000838381518110611fc757611fc7615d88565b6020026020010151905080600360008a67ffffffffffffffff1667ffffffffffffffff168152602001908152602001600020600201838154811061200d5761200d615d88565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff949094169390931790925567ffffffffffffffff8a16815260039091526040902060020180548061208757612087615d59565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055019055506120f7565b806120ef81615c86565b915050611f5d565b5073ffffffffffffffffffffffffffffffffffffffff8516600081815260026020908152604080832067ffffffffffffffff8b168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001690555192835290917f182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b910160405180910390a2505050505050565b600b546000906601000000000000900460ff16156121de576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005805467ffffffffffffffff169060006121f883615cbf565b82546101009290920a67ffffffffffffffff81810219909316918316021790915560055416905060008060405190808252806020026020018201604052801561224b578160200160208202803683370190505b506040805180820182526000808252602080830182815267ffffffffffffffff888116808552600484528685209551865493516bffffffffffffffffffffffff9091167fffffffffffffffffffffffff0000000000000000000000000000000000000000948516176c010000000000000000000000009190931602919091179094558451606081018652338152808301848152818701888152958552600384529590932083518154831673ffffffffffffffffffffffffffffffffffffffff9182161782559551600182018054909316961695909517905591518051949550909361233c92600285019201906150d7565b505060405133815267ffffffffffffffff841691507f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf9060200160405180910390a250905090565b67ffffffffffffffff8116600090815260036020526040812054819060609073ffffffffffffffffffffffffffffffffffffffff166123ef576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660009081526004602090815260408083205460038352928190208054600290910180548351818602810186019094528084526bffffffffffffffffffffffff9095169473ffffffffffffffffffffffffffffffffffffffff9092169390929183918301828280156124a157602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612476575b505050505090509250925092509193909250565b600b546601000000000000900460ff16156124fc576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461256b576040517f44b0e3c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602081146125a5576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006125b3828401846157a4565b67ffffffffffffffff811660009081526003602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1661261c576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff90811690861681146126a1576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016108cb565b67ffffffffffffffff8216600090815260046020526040812080546bffffffffffffffffffffffff16918791906126d88385615bca565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555085600560088282829054906101000a90046bffffffffffffffffffffffff1661272f9190615bca565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508267ffffffffffffffff167fd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f88288846127969190615b86565b6040805192835260208301919091520160405180910390a250505050505050565b600b546000906601000000000000900460ff1615612801576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005a9050600080600061281587876139c7565b9250925092506000866060015163ffffffff1667ffffffffffffffff81111561284057612840615db7565b604051908082528060200260200182016040528015612869578160200160208202803683370190505b50905060005b876060015163ffffffff168110156128dd5760408051602081018590529081018290526060016040516020818303038152906040528051906020012060001c8282815181106128c0576128c0615d88565b6020908102919091010152806128d581615c86565b91505061286f565b5060008381526009602052604080822082905560808a0151905182917f1fe543e3000000000000000000000000000000000000000000000000000000009161292a91908690602401615ac6565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252600b80547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff166601000000000000179055908a015160808b01519192506000916129f89163ffffffff169084613d16565b9050857f969e72fbacf24da85b4bce2a3cef3d8dc2497b1750c4cc5a06b52c10413383378583604051612a2c92919061593e565b60405180910390a2600b80547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff1690556020808b01805167ffffffffffffffff9081166000908152600490935260408084205492518216845290922080546c0100000000000000000000000092839004841693600193600c92612ab492869290910416615b9e565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000612b0b8a600b600001600b9054906101000a900463ffffffff1663ffffffff16612b0585612c8e565b3a613d64565b6020808e015167ffffffffffffffff166000908152600490915260409020549091506bffffffffffffffffffffffff80831691161015612b77576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020808d015167ffffffffffffffff1660009081526004909152604081208054839290612bb39084906bffffffffffffffffffffffff16615c59565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008b81526006602090815260408083205473ffffffffffffffffffffffffffffffffffffffff1683526008909152812080548594509092612c1c91859116615bca565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550809a50505050505050505050505b92915050565b600081604051602001612c719190615930565b604051602081830303815290604052805190602001209050919050565b6040805161012081018252600c5463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c010000000000000000000000008104831660608301527001000000000000000000000000000000008104909216608082015262ffffff740100000000000000000000000000000000000000008304811660a08301819052770100000000000000000000000000000000000000000000008404821660c08401527a0100000000000000000000000000000000000000000000000000008404821660e08401527d0100000000000000000000000000000000000000000000000000000000009093041661010082015260009167ffffffffffffffff841611612dac575192915050565b8267ffffffffffffffff168160a0015162ffffff16108015612de157508060c0015162ffffff168367ffffffffffffffff1611155b15612df0576020015192915050565b8267ffffffffffffffff168160c0015162ffffff16108015612e2557508060e0015162ffffff168367ffffffffffffffff1611155b15612e34576040015192915050565b8267ffffffffffffffff168160e0015162ffffff16108015612e6a575080610100015162ffffff168367ffffffffffffffff1611155b15612e79576060015192915050565b6080015192915050565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff1680612eec576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614612f53576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016108cb565b600b546601000000000000900460ff1615612f9a576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612fa384613269565b15612fda576040517fb42f66e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109ee8484613554565b60078181548110612ff457600080fd5b600091825260209091200154905081565b61300d6134d1565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b15801561309557600080fd5b505afa1580156130a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130cd919061550f565b6005549091506801000000000000000090046bffffffffffffffffffffffff1681811115613131576040517fa99da30200000000000000000000000000000000000000000000000000000000815260048101829052602481018390526044016108cb565b818110156132645760006131458284615c42565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018390529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb90604401602060405180830381600087803b1580156131da57600080fd5b505af11580156131ee573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061321291906154ed565b506040805173ffffffffffffffffffffffffffffffffffffffff86168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600910160405180910390a1505b505050565b67ffffffffffffffff811660009081526003602090815260408083208151606081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561331857602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116132ed575b505050505081525050905060005b8160400151518110156134b65760005b6007548110156134a357600061346c6007838154811061335857613358615d88565b90600052602060002001548560400151858151811061337957613379615d88565b602002602001015188600260008960400151898151811061339c5761339c615d88565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040908101600090812067ffffffffffffffff808f168352935220541660408051602080820187905273ffffffffffffffffffffffffffffffffffffffff959095168183015267ffffffffffffffff9384166060820152919092166080808301919091528251808303909101815260a08201835280519084012060c082019490945260e080820185905282518083039091018152610100909101909152805191012091565b50600081815260096020526040902054909150156134905750600195945050505050565b508061349b81615c86565b915050613336565b50806134ae81615c86565b915050613326565b5060009392505050565b6134c86134d1565b6107fc81613e6c565b60005473ffffffffffffffffffffffffffffffffffffffff163314613552576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016108cb565b565b600b546601000000000000900460ff161561359b576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff821660009081526003602090815260408083208151606081018352815473ffffffffffffffffffffffffffffffffffffffff90811682526001830154168185015260028201805484518187028101870186528181529295939486019383018282801561364657602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161361b575b5050509190925250505067ffffffffffffffff80851660009081526004602090815260408083208151808301909252546bffffffffffffffffffffffff81168083526c01000000000000000000000000909104909416918101919091529293505b83604001515181101561374d5760026000856040015183815181106136ce576136ce615d88565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040908101600090812067ffffffffffffffff8a168252909252902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001690558061374581615c86565b9150506136a7565b5067ffffffffffffffff8516600090815260036020526040812080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811682556001820180549091169055906137a86002830182615161565b505067ffffffffffffffff8516600090815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055600580548291906008906138189084906801000000000000000090046bffffffffffffffffffffffff16615c59565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85836bffffffffffffffffffffffff166040518363ffffffff1660e01b81526004016138d092919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b602060405180830381600087803b1580156138ea57600080fd5b505af11580156138fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061392291906154ed565b613958576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805173ffffffffffffffffffffffffffffffffffffffff861681526bffffffffffffffffffffffff8316602082015267ffffffffffffffff8716917fe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd49815910160405180910390a25050505050565b60008060006139d98560000151612c5e565b60008181526006602052604090205490935073ffffffffffffffffffffffffffffffffffffffff1680613a3b576040517f77f5b84c000000000000000000000000000000000000000000000000000000008152600481018590526024016108cb565b6080860151604051613a5a918691602001918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301206000818152600990935291205490935080613ad7576040517f3688124a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85516020808801516040808a015160608b015160808c01519251613b50968b96909594910195865267ffffffffffffffff948516602087015292909316604085015263ffffffff908116606085015291909116608083015273ffffffffffffffffffffffffffffffffffffffff1660a082015260c00190565b604051602081830303815290604052805190602001208114613b9e576040517fd529142c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855167ffffffffffffffff164080613cc25786516040517fe9413d3800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063e9413d389060240160206040518083038186803b158015613c4257600080fd5b505afa158015613c56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c7a919061550f565b905080613cc25786516040517f175dadad00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016108cb565b6000886080015182604051602001613ce4929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c9050613d098982613f62565b9450505050509250925092565b60005a611388811015613d2857600080fd5b611388810390508460408204820311613d4057600080fd5b50823b613d4c57600080fd5b60008083516020850160008789f190505b9392505050565b600080613d6f613feb565b905060008113613dae576040517f43d4cf66000000000000000000000000000000000000000000000000000000008152600481018290526024016108cb565b6000815a613dbc8989615b86565b613dc69190615c42565b613dd886670de0b6b3a7640000615c05565b613de29190615c05565b613dec9190615bf1565b90506000613e0563ffffffff871664e8d4a51000615c05565b9050613e1d816b033b2e3c9fd0803ce8000000615c42565b821115613e56576040517fe80fa38100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613e608183615b86565b98975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116331415613eec576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016108cb565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000613f968360000151846020015185604001518660600151868860a001518960c001518a60e001518b61010001516140ff565b60038360200151604051602001613fae929190615ab2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b600b54604080517ffeaf968c0000000000000000000000000000000000000000000000000000000081529051600092670100000000000000900463ffffffff169182151591849182917f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169163feaf968c9160048083019260a0929190829003018186803b15801561409157600080fd5b505afa1580156140a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140c991906157e9565b5094509092508491505080156140ed57506140e48242615c42565b8463ffffffff16105b156140f75750600a545b949350505050565b614108896143d6565b61416e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f7075626c6963206b6579206973206e6f74206f6e20637572766500000000000060448201526064016108cb565b614177886143d6565b6141dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f67616d6d61206973206e6f74206f6e206375727665000000000000000000000060448201526064016108cb565b6141e6836143d6565b61424c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e20637572766500000060448201526064016108cb565b614255826143d6565b6142bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f73486173685769746e657373206973206e6f74206f6e2063757276650000000060448201526064016108cb565b6142c7878a8887614531565b61432d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f6164647228632a706b2b732a6729213d5f755769746e6573730000000000000060448201526064016108cb565b60006143398a876146d4565b9050600061434c898b878b868989614738565b9050600061435d838d8d8a866148c0565b9050808a146143c8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f696e76616c69642070726f6f660000000000000000000000000000000000000060448201526064016108cb565b505050505050505050505050565b80516000907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f11614463576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c696420782d6f7264696e617465000000000000000000000000000060448201526064016108cb565b60208201517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f116144f0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c696420792d6f7264696e617465000000000000000000000000000060448201526064016108cb565b60208201517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f90800961452a8360005b602002015161491e565b1492915050565b600073ffffffffffffffffffffffffffffffffffffffff82166145b0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f626164207769746e65737300000000000000000000000000000000000000000060448201526064016108cb565b6020840151600090600116156145c757601c6145ca565b601b5b905060007ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418587600060200201510986517ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141918203925060009190890987516040805160008082526020820180845287905260ff88169282019290925260608101929092526080820183905291925060019060a0016020604051602081039080840390855afa158015614681573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff9081169088161495505050505050949350505050565b6146dc61517f565b614709600184846040516020016146f59392919061590f565b604051602081830303815290604052614976565b90505b614715816143d6565b612c5857805160408051602081019290925261473191016146f5565b905061470c565b61474061517f565b825186517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f90819006910614156147d3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e6374000060448201526064016108cb565b6147de8789886149df565b614844576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4669727374206d756c20636865636b206661696c65640000000000000000000060448201526064016108cb565b61484f8486856149df565b6148b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5365636f6e64206d756c20636865636b206661696c656400000000000000000060448201526064016108cb565b613e60868484614b6c565b6000600286868685876040516020016148de9695949392919061589d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209695505050505050565b6000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80848509840990507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f600782089392505050565b61497e61517f565b61498782614c9b565b815261499c614997826000614520565b614cf0565b6020820181905260029006600114156149da576020810180517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0390525b919050565b600082614a48576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f7a65726f207363616c617200000000000000000000000000000000000000000060448201526064016108cb565b83516020850151600090614a5e90600290615ce7565b15614a6a57601c614a6d565b601b5b905060007ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418387096040805160008082526020820180845281905260ff86169282019290925260608101869052608081018390529192509060019060a0016020604051602081039080840390855afa158015614aed573d6000803e3d6000fd5b505050602060405103519050600086604051602001614b0c919061588b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012073ffffffffffffffffffffffffffffffffffffffff92831692169190911498975050505050505050565b614b7461517f565b835160208086015185519186015160009384938493614b9593909190614d2a565b919450925090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f858209600114614c29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e765a206d75737420626520696e7665727365206f66207a0000000000000060448201526064016108cb565b60405180604001604052807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80614c6257614c62615d2a565b87860981526020017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8785099052979650505050505050565b805160208201205b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f81106149da57604080516020808201939093528151808203840181529082019091528051910120614ca3565b6000612c58826002614d237ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f6001615b86565b901c614ec0565b60008080600180827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f897ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f038808905060007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f038a0890506000614dd283838585614fb4565b9098509050614de388828e8861500c565b9098509050614df488828c8761500c565b90985090506000614e078d878b8561500c565b9098509050614e1888828686614fb4565b9098509050614e2988828e8961500c565b9098509050818114614eac577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f818a0998507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f82890997507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183099650614eb0565b8196505b5050505050509450945094915050565b600080614ecb61519d565b6020808252818101819052604082015260608101859052608081018490527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f60a0820152614f176151bb565b60208160c08460057ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa925082614faa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6269674d6f64457870206661696c75726521000000000000000000000000000060448201526064016108cb565b5195945050505050565b6000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8487097ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8487099097909650945050505050565b600080807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f878509905060007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f87877ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f030990507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183087ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f86890990999098509650505050505050565b828054828255906000526020600020908101928215615151579160200282015b8281111561515157825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9091161782556020909201916001909101906150f7565b5061515d9291506151d9565b5090565b50805460008255906000526020600020908101906107fc91906151d9565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b8082111561515d57600081556001016151da565b803573ffffffffffffffffffffffffffffffffffffffff811681146149da57600080fd5b8060408101831015612c5857600080fd5b600082601f83011261523457600080fd5b6040516040810181811067ffffffffffffffff8211171561525757615257615db7565b806040525080838560408601111561526e57600080fd5b60005b6002811015615290578135835260209283019290910190600101615271565b509195945050505050565b600060a082840312156152ad57600080fd5b60405160a0810181811067ffffffffffffffff821117156152d0576152d0615db7565b6040529050806152df83615365565b81526152ed60208401615365565b60208201526152fe60408401615351565b604082015261530f60608401615351565b6060820152615320608084016151ee565b60808201525092915050565b803561ffff811681146149da57600080fd5b803562ffffff811681146149da57600080fd5b803563ffffffff811681146149da57600080fd5b803567ffffffffffffffff811681146149da57600080fd5b805169ffffffffffffffffffff811681146149da57600080fd5b6000602082840312156153a957600080fd5b613d5d826151ee565b600080606083850312156153c557600080fd5b6153ce836151ee565b91506153dd8460208501615212565b90509250929050565b600080600080606085870312156153fc57600080fd5b615405856151ee565b935060208501359250604085013567ffffffffffffffff8082111561542957600080fd5b818701915087601f83011261543d57600080fd5b81358181111561544c57600080fd5b88602082850101111561545e57600080fd5b95989497505060200194505050565b6000806040838503121561548057600080fd5b615489836151ee565b915060208301356bffffffffffffffffffffffff811681146154aa57600080fd5b809150509250929050565b6000604082840312156154c757600080fd5b613d5d8383615212565b6000604082840312156154e357600080fd5b613d5d8383615223565b6000602082840312156154ff57600080fd5b81518015158114613d5d57600080fd5b60006020828403121561552157600080fd5b5051919050565b600080600080600060a0868803121561554057600080fd5b8535945061555060208701615365565b935061555e6040870161532c565b925061556c60608701615351565b915061557a60808701615351565b90509295509295909350565b60008082840361024081121561559b57600080fd5b6101a0808212156155ab57600080fd5b6155b3615b5c565b91506155bf8686615223565b82526155ce8660408701615223565b60208301526080850135604083015260a0850135606083015260c085013560808301526155fd60e086016151ee565b60a083015261010061561187828801615223565b60c0840152615624876101408801615223565b60e084015261018086013581840152508193506156438682870161529b565b925050509250929050565b6000806000806000808688036101c081121561566957600080fd5b6156728861532c565b965061568060208901615351565b955061568e60408901615351565b945061569c60608901615351565b935060808801359250610120807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60830112156156d757600080fd5b6156df615b5c565b91506156ed60a08a01615351565b82526156fb60c08a01615351565b602083015261570c60e08a01615351565b604083015261010061571f818b01615351565b606084015261572f828b01615351565b60808401526157416101408b0161533e565b60a08401526157536101608b0161533e565b60c08401526157656101808b0161533e565b60e08401526157776101a08b0161533e565b818401525050809150509295509295509295565b60006020828403121561579d57600080fd5b5035919050565b6000602082840312156157b657600080fd5b613d5d82615365565b600080604083850312156157d257600080fd5b6157db83615365565b91506153dd602084016151ee565b600080600080600060a0868803121561580157600080fd5b61580a8661537d565b945060208601519350604086015192506060860151915061557a6080870161537d565b8060005b60028110156109ee578151845260209384019390910190600101615831565b600081518084526020808501945080840160005b8381101561588057815187529582019590820190600101615864565b509495945050505050565b615895818361582d565b604001919050565b8681526158ad602082018761582d565b6158ba606082018661582d565b6158c760a082018561582d565b6158d460e082018461582d565b60609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166101208201526101340195945050505050565b83815261591f602082018461582d565b606081019190915260800192915050565b60408101612c58828461582d565b6040815260006159516040830185615850565b905082151560208301529392505050565b600060208083528351808285015260005b8181101561598f57858101830151858201604001528201615973565b818111156159a1576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60006101c08201905061ffff8816825263ffffffff808816602084015280871660408401528086166060840152846080840152835481811660a0850152615a2960c08501838360201c1663ffffffff169052565b615a4060e08501838360401c1663ffffffff169052565b615a586101008501838360601c1663ffffffff169052565b615a706101208501838360801c1663ffffffff169052565b62ffffff60a082901c811661014086015260b882901c811661016086015260d082901c1661018085015260e81c6101a090930192909252979650505050505050565b82815260608101613d5d602083018461582d565b8281526040602082015260006140f76040830184615850565b6000606082016bffffffffffffffffffffffff86168352602073ffffffffffffffffffffffffffffffffffffffff8087168286015260606040860152828651808552608087019150838801945060005b81811015615b4d578551841683529484019491840191600101615b2f565b50909998505050505050505050565b604051610120810167ffffffffffffffff81118282101715615b8057615b80615db7565b60405290565b60008219821115615b9957615b99615cfb565b500190565b600067ffffffffffffffff808316818516808303821115615bc157615bc1615cfb565b01949350505050565b60006bffffffffffffffffffffffff808316818516808303821115615bc157615bc1615cfb565b600082615c0057615c00615d2a565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615c3d57615c3d615cfb565b500290565b600082821015615c5457615c54615cfb565b500390565b60006bffffffffffffffffffffffff83811690831681811015615c7e57615c7e615cfb565b039392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415615cb857615cb8615cfb565b5060010190565b600067ffffffffffffffff80831681811415615cdd57615cdd615cfb565b6001019392505050565b600082615cf657615cf6615d2a565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", } var VRFCoordinatorV2ABI = VRFCoordinatorV2MetaData.ABI @@ -322,28 +334,6 @@ func (_VRFCoordinatorV2 *VRFCoordinatorV2CallerSession) MAXREQUESTCONFIRMATIONS( return _VRFCoordinatorV2.Contract.MAXREQUESTCONFIRMATIONS(&_VRFCoordinatorV2.CallOpts) } -func (_VRFCoordinatorV2 *VRFCoordinatorV2Caller) MINGASLIMIT(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _VRFCoordinatorV2.contract.Call(opts, &out, "MIN_GAS_LIMIT") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) MINGASLIMIT() (*big.Int, error) { - return _VRFCoordinatorV2.Contract.MINGASLIMIT(&_VRFCoordinatorV2.CallOpts) -} - -func (_VRFCoordinatorV2 *VRFCoordinatorV2CallerSession) MINGASLIMIT() (*big.Int, error) { - return _VRFCoordinatorV2.Contract.MINGASLIMIT(&_VRFCoordinatorV2.CallOpts) -} - func (_VRFCoordinatorV2 *VRFCoordinatorV2Caller) GetCommitment(opts *bind.CallOpts, requestId *big.Int) ([32]byte, error) { var out []interface{} err := _VRFCoordinatorV2.contract.Call(opts, &out, "getCommitment", requestId) @@ -366,39 +356,26 @@ func (_VRFCoordinatorV2 *VRFCoordinatorV2CallerSession) GetCommitment(requestId return _VRFCoordinatorV2.Contract.GetCommitment(&_VRFCoordinatorV2.CallOpts, requestId) } -func (_VRFCoordinatorV2 *VRFCoordinatorV2Caller) GetConfig(opts *bind.CallOpts) (GetConfig, - - error) { +func (_VRFCoordinatorV2 *VRFCoordinatorV2Caller) GetFeeTier(opts *bind.CallOpts, reqCount uint64) (uint32, error) { var out []interface{} - err := _VRFCoordinatorV2.contract.Call(opts, &out, "getConfig") + err := _VRFCoordinatorV2.contract.Call(opts, &out, "getFeeTier", reqCount) - outstruct := new(GetConfig) if err != nil { - return *outstruct, err + return *new(uint32), err } - outstruct.MinimumRequestConfirmations = *abi.ConvertType(out[0], new(uint16)).(*uint16) - outstruct.FulfillmentFlatFeeLinkPPM = *abi.ConvertType(out[1], new(uint32)).(*uint32) - outstruct.MaxGasLimit = *abi.ConvertType(out[2], new(uint32)).(*uint32) - outstruct.StalenessSeconds = *abi.ConvertType(out[3], new(uint32)).(*uint32) - outstruct.GasAfterPaymentCalculation = *abi.ConvertType(out[4], new(uint32)).(*uint32) - outstruct.MinimumSubscriptionBalance = *abi.ConvertType(out[5], new(*big.Int)).(**big.Int) - outstruct.FallbackWeiPerUnitLink = *abi.ConvertType(out[6], new(*big.Int)).(**big.Int) + out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) - return *outstruct, err + return out0, err } -func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) GetConfig() (GetConfig, - - error) { - return _VRFCoordinatorV2.Contract.GetConfig(&_VRFCoordinatorV2.CallOpts) +func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) GetFeeTier(reqCount uint64) (uint32, error) { + return _VRFCoordinatorV2.Contract.GetFeeTier(&_VRFCoordinatorV2.CallOpts, reqCount) } -func (_VRFCoordinatorV2 *VRFCoordinatorV2CallerSession) GetConfig() (GetConfig, - - error) { - return _VRFCoordinatorV2.Contract.GetConfig(&_VRFCoordinatorV2.CallOpts) +func (_VRFCoordinatorV2 *VRFCoordinatorV2CallerSession) GetFeeTier(reqCount uint64) (uint32, error) { + return _VRFCoordinatorV2.Contract.GetFeeTier(&_VRFCoordinatorV2.CallOpts, reqCount) } func (_VRFCoordinatorV2 *VRFCoordinatorV2Caller) GetSubscription(opts *bind.CallOpts, subId uint64) (GetSubscription, @@ -476,6 +453,120 @@ func (_VRFCoordinatorV2 *VRFCoordinatorV2CallerSession) Owner() (common.Address, return _VRFCoordinatorV2.Contract.Owner(&_VRFCoordinatorV2.CallOpts) } +func (_VRFCoordinatorV2 *VRFCoordinatorV2Caller) PendingRequestExists(opts *bind.CallOpts, subId uint64) (bool, error) { + var out []interface{} + err := _VRFCoordinatorV2.contract.Call(opts, &out, "pendingRequestExists", subId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) PendingRequestExists(subId uint64) (bool, error) { + return _VRFCoordinatorV2.Contract.PendingRequestExists(&_VRFCoordinatorV2.CallOpts, subId) +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2CallerSession) PendingRequestExists(subId uint64) (bool, error) { + return _VRFCoordinatorV2.Contract.PendingRequestExists(&_VRFCoordinatorV2.CallOpts, subId) +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2Caller) SConfig(opts *bind.CallOpts) (SConfig, + + error) { + var out []interface{} + err := _VRFCoordinatorV2.contract.Call(opts, &out, "s_config") + + outstruct := new(SConfig) + if err != nil { + return *outstruct, err + } + + outstruct.MinimumRequestConfirmations = *abi.ConvertType(out[0], new(uint16)).(*uint16) + outstruct.MaxGasLimit = *abi.ConvertType(out[1], new(uint32)).(*uint32) + outstruct.ReentrancyLock = *abi.ConvertType(out[2], new(bool)).(*bool) + outstruct.StalenessSeconds = *abi.ConvertType(out[3], new(uint32)).(*uint32) + outstruct.GasAfterPaymentCalculation = *abi.ConvertType(out[4], new(uint32)).(*uint32) + + return *outstruct, err + +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) SConfig() (SConfig, + + error) { + return _VRFCoordinatorV2.Contract.SConfig(&_VRFCoordinatorV2.CallOpts) +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2CallerSession) SConfig() (SConfig, + + error) { + return _VRFCoordinatorV2.Contract.SConfig(&_VRFCoordinatorV2.CallOpts) +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2Caller) SFeeConfig(opts *bind.CallOpts) (SFeeConfig, + + error) { + var out []interface{} + err := _VRFCoordinatorV2.contract.Call(opts, &out, "s_feeConfig") + + outstruct := new(SFeeConfig) + if err != nil { + return *outstruct, err + } + + outstruct.FulfillmentFlatFeeLinkPPMTier1 = *abi.ConvertType(out[0], new(uint32)).(*uint32) + outstruct.FulfillmentFlatFeeLinkPPMTier2 = *abi.ConvertType(out[1], new(uint32)).(*uint32) + outstruct.FulfillmentFlatFeeLinkPPMTier3 = *abi.ConvertType(out[2], new(uint32)).(*uint32) + outstruct.FulfillmentFlatFeeLinkPPMTier4 = *abi.ConvertType(out[3], new(uint32)).(*uint32) + outstruct.FulfillmentFlatFeeLinkPPMTier5 = *abi.ConvertType(out[4], new(uint32)).(*uint32) + outstruct.ReqsForTier2 = *abi.ConvertType(out[5], new(*big.Int)).(**big.Int) + outstruct.ReqsForTier3 = *abi.ConvertType(out[6], new(*big.Int)).(**big.Int) + outstruct.ReqsForTier4 = *abi.ConvertType(out[7], new(*big.Int)).(**big.Int) + outstruct.ReqsForTier5 = *abi.ConvertType(out[8], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) SFeeConfig() (SFeeConfig, + + error) { + return _VRFCoordinatorV2.Contract.SFeeConfig(&_VRFCoordinatorV2.CallOpts) +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2CallerSession) SFeeConfig() (SFeeConfig, + + error) { + return _VRFCoordinatorV2.Contract.SFeeConfig(&_VRFCoordinatorV2.CallOpts) +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2Caller) SProvingKeyHashes(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) { + var out []interface{} + err := _VRFCoordinatorV2.contract.Call(opts, &out, "s_provingKeyHashes", arg0) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) SProvingKeyHashes(arg0 *big.Int) ([32]byte, error) { + return _VRFCoordinatorV2.Contract.SProvingKeyHashes(&_VRFCoordinatorV2.CallOpts, arg0) +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2CallerSession) SProvingKeyHashes(arg0 *big.Int) ([32]byte, error) { + return _VRFCoordinatorV2.Contract.SProvingKeyHashes(&_VRFCoordinatorV2.CallOpts, arg0) +} + func (_VRFCoordinatorV2 *VRFCoordinatorV2Caller) STotalBalance(opts *bind.CallOpts) (*big.Int, error) { var out []interface{} err := _VRFCoordinatorV2.contract.Call(opts, &out, "s_totalBalance") @@ -580,18 +671,6 @@ func (_VRFCoordinatorV2 *VRFCoordinatorV2TransactorSession) CreateSubscription() return _VRFCoordinatorV2.Contract.CreateSubscription(&_VRFCoordinatorV2.TransactOpts) } -func (_VRFCoordinatorV2 *VRFCoordinatorV2Transactor) DefundSubscription(opts *bind.TransactOpts, subId uint64, to common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2.contract.Transact(opts, "defundSubscription", subId, to, amount) -} - -func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) DefundSubscription(subId uint64, to common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2.Contract.DefundSubscription(&_VRFCoordinatorV2.TransactOpts, subId, to, amount) -} - -func (_VRFCoordinatorV2 *VRFCoordinatorV2TransactorSession) DefundSubscription(subId uint64, to common.Address, amount *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2.Contract.DefundSubscription(&_VRFCoordinatorV2.TransactOpts, subId, to, amount) -} - func (_VRFCoordinatorV2 *VRFCoordinatorV2Transactor) DeregisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) { return _VRFCoordinatorV2.contract.Transact(opts, "deregisterProvingKey", publicProvingKey) } @@ -640,6 +719,18 @@ func (_VRFCoordinatorV2 *VRFCoordinatorV2TransactorSession) OracleWithdraw(recip return _VRFCoordinatorV2.Contract.OracleWithdraw(&_VRFCoordinatorV2.TransactOpts, recipient, amount) } +func (_VRFCoordinatorV2 *VRFCoordinatorV2Transactor) OwnerCancelSubscription(opts *bind.TransactOpts, subId uint64) (*types.Transaction, error) { + return _VRFCoordinatorV2.contract.Transact(opts, "ownerCancelSubscription", subId) +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) OwnerCancelSubscription(subId uint64) (*types.Transaction, error) { + return _VRFCoordinatorV2.Contract.OwnerCancelSubscription(&_VRFCoordinatorV2.TransactOpts, subId) +} + +func (_VRFCoordinatorV2 *VRFCoordinatorV2TransactorSession) OwnerCancelSubscription(subId uint64) (*types.Transaction, error) { + return _VRFCoordinatorV2.Contract.OwnerCancelSubscription(&_VRFCoordinatorV2.TransactOpts, subId) +} + func (_VRFCoordinatorV2 *VRFCoordinatorV2Transactor) RecoverFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { return _VRFCoordinatorV2.contract.Transact(opts, "recoverFunds", to) } @@ -700,16 +791,16 @@ func (_VRFCoordinatorV2 *VRFCoordinatorV2TransactorSession) RequestSubscriptionO return _VRFCoordinatorV2.Contract.RequestSubscriptionOwnerTransfer(&_VRFCoordinatorV2.TransactOpts, subId, newOwner) } -func (_VRFCoordinatorV2 *VRFCoordinatorV2Transactor) SetConfig(opts *bind.TransactOpts, minimumRequestConfirmations uint16, fulfillmentFlatFeeLinkPPM uint32, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, minimumSubscriptionBalance *big.Int, fallbackWeiPerUnitLink *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2.contract.Transact(opts, "setConfig", minimumRequestConfirmations, fulfillmentFlatFeeLinkPPM, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, minimumSubscriptionBalance, fallbackWeiPerUnitLink) +func (_VRFCoordinatorV2 *VRFCoordinatorV2Transactor) SetConfig(opts *bind.TransactOpts, minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig VRFCoordinatorV2FeeConfig) (*types.Transaction, error) { + return _VRFCoordinatorV2.contract.Transact(opts, "setConfig", minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, feeConfig) } -func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) SetConfig(minimumRequestConfirmations uint16, fulfillmentFlatFeeLinkPPM uint32, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, minimumSubscriptionBalance *big.Int, fallbackWeiPerUnitLink *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2.Contract.SetConfig(&_VRFCoordinatorV2.TransactOpts, minimumRequestConfirmations, fulfillmentFlatFeeLinkPPM, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, minimumSubscriptionBalance, fallbackWeiPerUnitLink) +func (_VRFCoordinatorV2 *VRFCoordinatorV2Session) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig VRFCoordinatorV2FeeConfig) (*types.Transaction, error) { + return _VRFCoordinatorV2.Contract.SetConfig(&_VRFCoordinatorV2.TransactOpts, minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, feeConfig) } -func (_VRFCoordinatorV2 *VRFCoordinatorV2TransactorSession) SetConfig(minimumRequestConfirmations uint16, fulfillmentFlatFeeLinkPPM uint32, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, minimumSubscriptionBalance *big.Int, fallbackWeiPerUnitLink *big.Int) (*types.Transaction, error) { - return _VRFCoordinatorV2.Contract.SetConfig(&_VRFCoordinatorV2.TransactOpts, minimumRequestConfirmations, fulfillmentFlatFeeLinkPPM, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, minimumSubscriptionBalance, fallbackWeiPerUnitLink) +func (_VRFCoordinatorV2 *VRFCoordinatorV2TransactorSession) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig VRFCoordinatorV2FeeConfig) (*types.Transaction, error) { + return _VRFCoordinatorV2.Contract.SetConfig(&_VRFCoordinatorV2.TransactOpts, minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, feeConfig) } func (_VRFCoordinatorV2 *VRFCoordinatorV2Transactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { @@ -786,12 +877,11 @@ func (it *VRFCoordinatorV2ConfigSetIterator) Close() error { type VRFCoordinatorV2ConfigSet struct { MinimumRequestConfirmations uint16 - FulfillmentFlatFeeLinkPPM uint32 MaxGasLimit uint32 StalenessSeconds uint32 GasAfterPaymentCalculation uint32 - MinimumSubscriptionBalance *big.Int FallbackWeiPerUnitLink *big.Int + FeeConfig VRFCoordinatorV2FeeConfig Raw types.Log } @@ -2795,19 +2885,28 @@ func (_VRFCoordinatorV2 *VRFCoordinatorV2Filterer) ParseSubscriptionOwnerTransfe return event, nil } -type GetConfig struct { +type GetSubscription struct { + Balance *big.Int + Owner common.Address + Consumers []common.Address +} +type SConfig struct { MinimumRequestConfirmations uint16 - FulfillmentFlatFeeLinkPPM uint32 MaxGasLimit uint32 + ReentrancyLock bool StalenessSeconds uint32 GasAfterPaymentCalculation uint32 - MinimumSubscriptionBalance *big.Int - FallbackWeiPerUnitLink *big.Int } -type GetSubscription struct { - Balance *big.Int - Owner common.Address - Consumers []common.Address +type SFeeConfig struct { + FulfillmentFlatFeeLinkPPMTier1 uint32 + FulfillmentFlatFeeLinkPPMTier2 uint32 + FulfillmentFlatFeeLinkPPMTier3 uint32 + FulfillmentFlatFeeLinkPPMTier4 uint32 + FulfillmentFlatFeeLinkPPMTier5 uint32 + ReqsForTier2 *big.Int + ReqsForTier3 *big.Int + ReqsForTier4 *big.Int + ReqsForTier5 *big.Int } func (_VRFCoordinatorV2 *VRFCoordinatorV2) ParseLog(log types.Log) (generated.AbigenLog, error) { @@ -2851,7 +2950,7 @@ func (_VRFCoordinatorV2 *VRFCoordinatorV2) ParseLog(log types.Log) (generated.Ab } func (VRFCoordinatorV2ConfigSet) Topic() common.Hash { - return common.HexToHash("0x56583fc0e609f432152501e64e8a4aaf7ecc715b33697f1cacb307f4b562d2c4") + return common.HexToHash("0xc21e3bd2e0b339d2848f0dd956947a88966c242c0c0c582a33137a5c1ceb5cb2") } func (VRFCoordinatorV2FundsRecovered) Topic() common.Hash { @@ -2931,13 +3030,9 @@ type VRFCoordinatorV2Interface interface { MAXREQUESTCONFIRMATIONS(opts *bind.CallOpts) (uint16, error) - MINGASLIMIT(opts *bind.CallOpts) (*big.Int, error) - GetCommitment(opts *bind.CallOpts, requestId *big.Int) ([32]byte, error) - GetConfig(opts *bind.CallOpts) (GetConfig, - - error) + GetFeeTier(opts *bind.CallOpts, reqCount uint64) (uint32, error) GetSubscription(opts *bind.CallOpts, subId uint64) (GetSubscription, @@ -2947,6 +3042,18 @@ type VRFCoordinatorV2Interface interface { Owner(opts *bind.CallOpts) (common.Address, error) + PendingRequestExists(opts *bind.CallOpts, subId uint64) (bool, error) + + SConfig(opts *bind.CallOpts) (SConfig, + + error) + + SFeeConfig(opts *bind.CallOpts) (SFeeConfig, + + error) + + SProvingKeyHashes(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) + STotalBalance(opts *bind.CallOpts) (*big.Int, error) TypeAndVersion(opts *bind.CallOpts) (string, error) @@ -2961,8 +3068,6 @@ type VRFCoordinatorV2Interface interface { CreateSubscription(opts *bind.TransactOpts) (*types.Transaction, error) - DefundSubscription(opts *bind.TransactOpts, subId uint64, to common.Address, amount *big.Int) (*types.Transaction, error) - DeregisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) FulfillRandomWords(opts *bind.TransactOpts, proof VRFProof, rc VRFCoordinatorV2RequestCommitment) (*types.Transaction, error) @@ -2971,6 +3076,8 @@ type VRFCoordinatorV2Interface interface { OracleWithdraw(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) + OwnerCancelSubscription(opts *bind.TransactOpts, subId uint64) (*types.Transaction, error) + RecoverFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) RegisterProvingKey(opts *bind.TransactOpts, oracle common.Address, publicProvingKey [2]*big.Int) (*types.Transaction, error) @@ -2981,7 +3088,7 @@ type VRFCoordinatorV2Interface interface { RequestSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId uint64, newOwner common.Address) (*types.Transaction, error) - SetConfig(opts *bind.TransactOpts, minimumRequestConfirmations uint16, fulfillmentFlatFeeLinkPPM uint32, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, minimumSubscriptionBalance *big.Int, fallbackWeiPerUnitLink *big.Int) (*types.Transaction, error) + SetConfig(opts *bind.TransactOpts, minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig VRFCoordinatorV2FeeConfig) (*types.Transaction, error) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) diff --git a/core/internal/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/internal/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 726c096a3b9..8717696544b 100644 --- a/core/internal/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/internal/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -14,7 +14,7 @@ solidity_vrf_request_id: ../../../contracts/solc/v0.6/VRFRequestIDBaseTestHelper solidity_vrf_request_id_v08: ../../../contracts/solc/v0.8/VRFRequestIDBaseTestHelper.abi ../../../contracts/solc/v0.8/VRFRequestIDBaseTestHelper.bin cae4c8e73f9d64886b0bdc02c40acd8b252b70e9499ce248b2ed0f480ccfda73 solidity_vrf_v08_verifier_wrapper: ../../../contracts/solc/v0.8/VRFTestHelper.abi ../../../contracts/solc/v0.8/VRFTestHelper.bin c9f113405fc2e57371721c34f112aa3038b18facc25d8124ddeecfa573ea7943 solidity_vrf_verifier_wrapper: ../../../contracts/solc/v0.6/VRFTestHelper.abi ../../../contracts/solc/v0.6/VRFTestHelper.bin 44c2b67d8d2990ab580453deb29d63508c6147a3dc49908a1db563bef06e6474 -vrf_consumer_v2: ../../../contracts/solc/v0.8/VRFConsumerV2.abi ../../../contracts/solc/v0.8/VRFConsumerV2.bin 997f6d8d9ab8dc389d75d0945d6d2f897fd341d5ab187858622802607b6b9a1c -vrf_coordinator_v2: ../../../contracts/solc/v0.8/VRFCoordinatorV2.abi ../../../contracts/solc/v0.8/VRFCoordinatorV2.bin 3462a66c7881970359a37693c86212e0e67129b7dd2b7d88215a9a5da0a03307 +vrf_consumer_v2: ../../../contracts/solc/v0.8/VRFConsumerV2.abi ../../../contracts/solc/v0.8/VRFConsumerV2.bin 8835726d73bb93a6c2fb5464490f23a8de052f57165c09f195b29c25727dd793 +vrf_coordinator_v2: ../../../contracts/solc/v0.8/VRFCoordinatorV2.abi ../../../contracts/solc/v0.8/VRFCoordinatorV2.bin 715160332fea2f474c00231dda607f9e97352ffc41f1fd4887dfd4589d1c854c vrf_malicious_consumer_v2: ../../../contracts/solc/v0.8/VRFMaliciousConsumerV2.abi ../../../contracts/solc/v0.8/VRFMaliciousConsumerV2.bin b9fde692c1ebd6d029a21d79da528aa39f92f67ad51aa5c18f4c0605469f287b vrf_single_consumer_example: ../../../contracts/solc/v0.8/VRFSingleConsumerExample.abi ../../../contracts/solc/v0.8/VRFSingleConsumerExample.bin b2ed5ca2aa6dd9ae1a06e7ff7a269e7c9ce027483705ecbd476d4df546787a0c diff --git a/core/internal/testutils/configtest/general_config.go b/core/internal/testutils/configtest/general_config.go index 7396f985004..7aa157de976 100644 --- a/core/internal/testutils/configtest/general_config.go +++ b/core/internal/testutils/configtest/general_config.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/core/chains/evm/types" + p2ppeer "github.com/libp2p/go-libp2p-core/peer" "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/services/eth" @@ -96,6 +98,7 @@ type GeneralConfigOverrides struct { P2PPeerIDError error SecretGenerator config.SecretGenerator TriggerFallbackDBPollInterval *time.Duration + KeySpecific map[string]types.ChainCfg } // FIXME: This is a hack, the proper fix is here: https://app.clubhouse.io/chainlinklabs/story/15103/use-in-memory-event-broadcaster-instead-of-postgres-event-broadcaster-in-transactional-tests-so-it-actually-works diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index a024581c51a..d67fbf18c33 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -136,7 +136,7 @@ func (mo *MockORM) CreateChain(id utils.Big, config evmtypes.ChainCfg) (evmtypes } func (mo *MockORM) UpdateChain(id utils.Big, enabled bool, config evmtypes.ChainCfg) (evmtypes.Chain, error) { - panic("not implemented") + return evmtypes.Chain{}, nil } func (mo *MockORM) DeleteChain(id utils.Big) error { diff --git a/core/scripts/vrfv2/deploy-vrfv2/main.go b/core/scripts/vrfv2/deploy-vrfv2/main.go index dfbd8da9aa3..f7cec038402 100644 --- a/core/scripts/vrfv2/deploy-vrfv2/main.go +++ b/core/scripts/vrfv2/deploy-vrfv2/main.go @@ -83,17 +83,26 @@ func main() { time.Sleep(waitForMine) // Set coordinators config _, err = coordinatorContract.SetConfig(user, - uint16(1), // minRequestConfirmations - uint32(1000), // 0.0001 link flat fee + uint16(1), // minRequestConfirmations uint32(1000000), uint32(60*60*24), // stalenessSeconds uint32(vrf.GasAfterPaymentCalculation), // gasAfterPaymentCalculation big.NewInt(10000000000000000), // 0.01 eth per link fallbackLinkPrice - big.NewInt(1000000000000000000), // Minimum subscription balance 0.01 link + vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ + FulfillmentFlatFeeLinkPPMTier1: uint32(10000), + FulfillmentFlatFeeLinkPPMTier2: uint32(1000), + FulfillmentFlatFeeLinkPPMTier3: uint32(100), + FulfillmentFlatFeeLinkPPMTier4: uint32(10), + FulfillmentFlatFeeLinkPPMTier5: uint32(1), + ReqsForTier2: big.NewInt(10), + ReqsForTier3: big.NewInt(20), + ReqsForTier4: big.NewInt(30), + ReqsForTier5: big.NewInt(40), + }, ) panicErr(err) time.Sleep(waitForMine) - c, err := coordinatorContract.GetConfig(nil) + c, err := coordinatorContract.SConfig(nil) panicErr(err) fmt.Printf("Coordinator config %v %v %+v\n", coordinatorAddress, linkAddress, c) // Deploy consumer diff --git a/core/scripts/vrfv2/get-randomness/main.go b/core/scripts/vrfv2/get-randomness/main.go index 8681ca95b1b..9b0cfde8ff8 100644 --- a/core/scripts/vrfv2/get-randomness/main.go +++ b/core/scripts/vrfv2/get-randomness/main.go @@ -24,7 +24,7 @@ func main() { panicErr(err) sub, err := coordinator.GetSubscription(nil, 1) fmt.Println(sub, err) - c, err := coordinator.GetConfig(nil) + c, err := coordinator.SConfig(nil) fmt.Println(c, err) consumer, err := vrf_consumer_v2.NewVRFConsumerV2(common.HexToAddress(consumerAddress), ec) diff --git a/core/scripts/vrfv2/kovan/main.go b/core/scripts/vrfv2/kovan/main.go index 4bbba695f68..7a57cd17391 100644 --- a/core/scripts/vrfv2/kovan/main.go +++ b/core/scripts/vrfv2/kovan/main.go @@ -85,13 +85,22 @@ func main() { coordinator, err := vrf_coordinator_v2.NewVRFCoordinatorV2(common.HexToAddress(*setConfigAddress), ec) panicErr(err) _, err = coordinator.SetConfig(owner, - uint16(1), // minRequestConfirmations - uint32(1000), // 0.0001 link flat fee - uint32(1000000), // max gas limit + uint16(1), // minRequestConfirmations + uint32(1000000), uint32(60*60*24), // stalenessSeconds uint32(vrf.GasAfterPaymentCalculation), // gasAfterPaymentCalculation big.NewInt(10000000000000000), // 0.01 eth per link fallbackLinkPrice - big.NewInt(1000000000000000000), // Minimum subscription balance 0.01 link + vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ + FulfillmentFlatFeeLinkPPMTier1: uint32(10000), + FulfillmentFlatFeeLinkPPMTier2: uint32(1000), + FulfillmentFlatFeeLinkPPMTier3: uint32(100), + FulfillmentFlatFeeLinkPPMTier4: uint32(10), + FulfillmentFlatFeeLinkPPMTier5: uint32(1), + ReqsForTier2: big.NewInt(10), + ReqsForTier3: big.NewInt(20), + ReqsForTier4: big.NewInt(30), + ReqsForTier5: big.NewInt(40), + }, ) panicErr(err) case "coordinator-register-key": diff --git a/core/services/bulletprooftxmanager/models.go b/core/services/bulletprooftxmanager/models.go index afb7013f0cd..50b5d0a0e62 100644 --- a/core/services/bulletprooftxmanager/models.go +++ b/core/services/bulletprooftxmanager/models.go @@ -26,6 +26,9 @@ type EthTxMeta struct { JobID int32 RequestID common.Hash RequestTxHash common.Hash + // Used for the VRFv2 - max link this tx will bill + // should it get bumped + MaxLink string } func (EthTxMeta) GormDataType() string { diff --git a/core/services/fluxmonitorv2/integrations_test.go b/core/services/fluxmonitorv2/integrations_test.go index 72cc61cb5c4..f5f281b03a4 100644 --- a/core/services/fluxmonitorv2/integrations_test.go +++ b/core/services/fluxmonitorv2/integrations_test.go @@ -358,10 +358,14 @@ func submitAnswer(t *testing.T, p answerParams) { checkSubmission(t, p, cb.Int64(), 0) } -func awaitSubmission(t *testing.T, submissionReceived chan *faw.FluxAggregatorSubmissionReceived) ( +func awaitSubmission(t *testing.T, backend *backends.SimulatedBackend, submissionReceived chan *faw.FluxAggregatorSubmissionReceived) ( receiptBlock uint64, answer int64, ) { t.Helper() + + // Send blocks until we get a response + stopBlocks := utils.FiniteTicker(time.Second, func() { backend.Commit() }) + defer stopBlocks() select { // block until FluxAggregator contract acknowledges chainlink message case log := <-submissionReceived: return log.Raw.BlockNumber, log.Submission.Int64() @@ -417,8 +421,9 @@ func checkLogWasConsumed(t *testing.T, fa fluxAggregatorUniverse, db *gorm.DB, p require.NotNil(t, block) consumed, err := log.NewORM(db, fa.evmChainID).WasBroadcastConsumed(db, block.Hash(), 0, pipelineSpecID) require.NoError(t, err) + fa.backend.Commit() return consumed - }, cltest.DefaultWaitTimeout).Should(gomega.BeTrue()) + }, cltest.DefaultWaitTimeout, time.Second).Should(gomega.BeTrue()) } func TestFluxMonitor_Deviation(t *testing.T) { @@ -451,8 +456,11 @@ func TestFluxMonitor_Deviation(t *testing.T) { cfg.Overrides.GlobalEvmEIP1559DynamicFees = null.BoolFrom(test.eip1559) }) - type k struct{ latestAnswer, updatedAt string } - expectedMeta := map[k]int{} + type v struct { + count int + updatedAt string + } + expectedMeta := map[string]v{} var expMetaMu sync.Mutex reportPrice := atomic.NewInt64(100) @@ -464,9 +472,10 @@ func TestFluxMonitor_Deviation(t *testing.T) { var m bridges.BridgeMetaDataJSON require.NoError(t, json.Unmarshal(b, &m)) if m.Meta.LatestAnswer != nil && m.Meta.UpdatedAt != nil { - key := k{m.Meta.LatestAnswer.String(), m.Meta.UpdatedAt.String()} + k := m.Meta.LatestAnswer.String() expMetaMu.Lock() - expectedMeta[key] = expectedMeta[key] + 1 + curr := expectedMeta[k] + expectedMeta[k] = v{curr.count + 1, m.Meta.UpdatedAt.String()} expMetaMu.Unlock() } }, @@ -527,7 +536,7 @@ func TestFluxMonitor_Deviation(t *testing.T) { }, cltest.DefaultWaitTimeout, 200*time.Millisecond).Should(gomega.BeNumerically(">=", 1)) // Initial Poll - receiptBlock, answer := awaitSubmission(t, submissionReceived) + receiptBlock, answer := awaitSubmission(t, fa.backend, submissionReceived) lggr := logger.TestLogger(t) lggr.Infof("Detected submission: %v in block %v", answer, receiptBlock) @@ -549,19 +558,15 @@ func TestFluxMonitor_Deviation(t *testing.T) { ) assertPipelineRunCreated(t, app.GetDB(), 1, float64(100)) - // make sure the log is sent from LogBroadcaster - fa.backend.Commit() - fa.backend.Commit() - // Need to wait until NewRound log is consumed - otherwise there is a chance // it will arrive after the next answer is submitted, and cause // DeleteFluxMonitorRoundsBackThrough to delete previous stats - checkLogWasConsumed(t, fa, app.GetDB(), int32(jobId), 5) + checkLogWasConsumed(t, fa, app.GetDB(), int32(jobId), receiptBlock) lggr.Info("Updating price to 103") // Change reported price to a value outside the deviation reportPrice.Store(103) - receiptBlock, answer = awaitSubmission(t, submissionReceived) + receiptBlock, answer = awaitSubmission(t, fa.backend, submissionReceived) lggr.Infof("Detected submission: %v in block %v", answer, receiptBlock) @@ -582,13 +587,10 @@ func TestFluxMonitor_Deviation(t *testing.T) { ) assertPipelineRunCreated(t, app.GetDB(), 2, float64(103)) - stopMining := cltest.Mine(fa.backend, time.Second) - defer stopMining() - // Need to wait until NewRound log is consumed - otherwise there is a chance // it will arrive after the next answer is submitted, and cause // DeleteFluxMonitorRoundsBackThrough to delete previous stats - checkLogWasConsumed(t, fa, app.GetDB(), int32(jobId), 8) + checkLogWasConsumed(t, fa, app.GetDB(), int32(jobId), receiptBlock) // Should not received a submission as it is inside the deviation reportPrice.Store(104) @@ -597,8 +599,9 @@ func TestFluxMonitor_Deviation(t *testing.T) { expMetaMu.Lock() defer expMetaMu.Unlock() assert.Len(t, expectedMeta, 2, "expected metadata %v", expectedMeta) - assert.Greater(t, expectedMeta[k{"100", "50"}], 0, "Stored answer metadata does not contain 100 updated at 50, but contains: %v", expectedMeta) - assert.Greater(t, expectedMeta[k{"103", "80"}], 0, "Stored answer metadata does not contain 103 updated at 80, but contains: %v", expectedMeta) + assert.Greater(t, expectedMeta["100"].count, 0, "Stored answer metadata does not contain 100 but contains: %v", expectedMeta) + assert.Greater(t, expectedMeta["103"].count, 0, "Stored answer metadata does not contain 103 but contains: %v", expectedMeta) + assert.Greater(t, expectedMeta["103"].updatedAt, expectedMeta["100"].updatedAt) }) } } @@ -694,7 +697,7 @@ ds1 -> ds1_parse // Wait for the node's submission, and ensure it submits to the round // started by the fake node - receiptBlock, _ := awaitSubmission(t, submissionReceived) + receiptBlock, _ := awaitSubmission(t, fa.backend, submissionReceived) checkSubmission(t, answerParams{ fa: &fa, @@ -783,10 +786,10 @@ ds1 -> ds1_parse // lower global kill switch flag - should trigger job run fa.flagsContract.LowerFlags(fa.sergey, []common.Address{utils.ZeroAddress}) fa.backend.Commit() - awaitSubmission(t, submissionReceived) + awaitSubmission(t, fa.backend, submissionReceived) reportPrice.Store(2) // change in price should trigger run - awaitSubmission(t, submissionReceived) + awaitSubmission(t, fa.backend, submissionReceived) // lower contract's flag - should have no effect fa.flagsContract.LowerFlags(fa.sergey, []common.Address{fa.aggregatorContractAddress}) @@ -795,7 +798,7 @@ ds1 -> ds1_parse // change in price should trigger run reportPrice.Store(4) - awaitSubmission(t, submissionReceived) + awaitSubmission(t, fa.backend, submissionReceived) // raise both flags fa.flagsContract.RaiseFlag(fa.sergey, fa.aggregatorContractAddress) @@ -973,7 +976,7 @@ ds1 -> ds1_parse -> ds1_multiply cltest.CreateJobViaWeb2(t, app, string(requestBody)) - receiptBlock, answer := awaitSubmission(t, submissionReceived) + receiptBlock, answer := awaitSubmission(t, fa.backend, submissionReceived) assert.Equal(t, 100*reportPrice.Load(), answer, "failed to report correct price to contract") @@ -994,7 +997,7 @@ ds1 -> ds1_parse -> ds1_multiply // Triggers a new round, since price deviation exceeds threshold reportPrice.Store(answer + 1) - receiptBlock, _ = awaitSubmission(t, submissionReceived) + receiptBlock, _ = awaitSubmission(t, fa.backend, submissionReceived) newRound := roundId + 1 processedAnswer = 100 * reportPrice.Load() checkSubmission(t, @@ -1055,7 +1058,7 @@ ds1 -> ds1_parse -> ds1_multiply // Wait for the node's submission, and ensure it submits to the round // started by the fake node - awaitSubmission(t, submissionReceived) + awaitSubmission(t, fa.backend, submissionReceived) } // submitMaliciousAnswer simulates a call to fa's FluxAggregator contract from diff --git a/core/services/job/models.go b/core/services/job/models.go index 513495d32be..aacb894cdc9 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -356,10 +356,11 @@ type KeeperSpec struct { type VRFSpec struct { ID int32 - CoordinatorAddress ethkey.EIP55Address `toml:"coordinatorAddress"` - PublicKey secp256k1.PublicKey `toml:"publicKey"` - Confirmations uint32 `toml:"confirmations"` - EVMChainID *utils.Big `toml:"evmChainID" gorm:"column:evm_chain_id"` - CreatedAt time.Time `toml:"-"` - UpdatedAt time.Time `toml:"-"` + CoordinatorAddress ethkey.EIP55Address `toml:"coordinatorAddress"` + PublicKey secp256k1.PublicKey `toml:"publicKey"` + Confirmations uint32 `toml:"confirmations"` + EVMChainID *utils.Big `toml:"evmChainID" gorm:"column:evm_chain_id"` + FromAddress *ethkey.EIP55Address `toml:"fromAddress"` + CreatedAt time.Time `toml:"-"` + UpdatedAt time.Time `toml:"-"` } diff --git a/core/services/vrf/delegate.go b/core/services/vrf/delegate.go index 4d4a53bfd89..896659819fc 100644 --- a/core/services/vrf/delegate.go +++ b/core/services/vrf/delegate.go @@ -40,6 +40,8 @@ type GethKeyStore interface { type Config interface { MinIncomingConfirmations() uint32 EvmGasLimitDefault() uint64 + KeySpecificMaxGasPriceWei(addr common.Address) *big.Int + MinRequiredOutgoingConfirmations() uint64 } func NewDelegate( @@ -104,7 +106,6 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.Service, error) { l: lV2, ethClient: chain.Client(), logBroadcaster: chain.LogBroadcaster(), - headBroadcaster: chain.HeadBroadcaster(), db: d.db, abi: abiV2, coordinator: coordinatorV2, @@ -118,7 +119,6 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.Service, error) { reqLogs: utils.NewHighCapacityMailbox(), chStop: make(chan struct{}), waitOnStop: make(chan struct{}), - newHead: make(chan struct{}, 1), respCount: GetStartingResponseCountsV2(d.db, lV2), blockNumberToReqID: pairing.New(), reqAdded: func() {}, diff --git a/core/services/vrf/integration_v2_test.go b/core/services/vrf/integration_v2_test.go index 0e4ebb353e6..619a0fc96c1 100644 --- a/core/services/vrf/integration_v2_test.go +++ b/core/services/vrf/integration_v2_test.go @@ -2,12 +2,24 @@ package vrf_test import ( "context" + "encoding/json" + "fmt" "math/big" "strconv" "strings" "testing" "time" + gethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/smartcontractkit/chainlink/core/services/job" + "github.com/smartcontractkit/chainlink/core/services/keystore/keys/vrfkey" + + "github.com/smartcontractkit/chainlink/core/assets" + "github.com/smartcontractkit/chainlink/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/core/services/pipeline" + "github.com/smartcontractkit/chainlink/core/store/models" + "github.com/smartcontractkit/chainlink/core/utils" + "github.com/smartcontractkit/chainlink/core/internal/gethwrappers/generated/vrf_malicious_consumer_v2" "github.com/smartcontractkit/chainlink/core/services/bulletprooftxmanager" @@ -24,7 +36,6 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - evmconfig "github.com/smartcontractkit/chainlink/core/chains/evm/config" "github.com/smartcontractkit/chainlink/core/internal/cltest" "github.com/smartcontractkit/chainlink/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/core/internal/gethwrappers/generated/link_token_interface" @@ -32,7 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/core/internal/gethwrappers/generated/vrf_consumer_v2" "github.com/smartcontractkit/chainlink/core/internal/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/core/services/pipeline" "github.com/smartcontractkit/chainlink/core/services/signatures/secp256k1" "github.com/smartcontractkit/chainlink/core/services/vrf" "github.com/smartcontractkit/chainlink/core/services/vrf/proof" @@ -75,11 +85,11 @@ func newVRFCoordinatorV2Universe(t *testing.T, key ethkey.KeyV2) coordinatorV2Un nallory = oracleTransactor ) genesisData := core.GenesisAlloc{ - sergey.From: {Balance: oneEth}, - neil.From: {Balance: oneEth}, - ned.From: {Balance: oneEth}, - carol.From: {Balance: oneEth}, - nallory.From: {Balance: oneEth}, + sergey.From: {Balance: assets.Ether(1000)}, + neil.From: {Balance: assets.Ether(1000)}, + ned.From: {Balance: assets.Ether(1000)}, + carol.From: {Balance: assets.Ether(1000)}, + nallory.From: {Balance: assets.Ether(1000)}, } gasLimit := ethconfig.Defaults.Miner.GasCeil consumerABI, err := abi.JSON(strings.NewReader( @@ -104,12 +114,12 @@ func newVRFCoordinatorV2Universe(t *testing.T, key ethkey.KeyV2) coordinatorV2Un neil, backend, linkAddress, common.Address{} /*blockHash store*/, linkEthFeed /* linkEth*/) require.NoError(t, err, "failed to deploy VRFCoordinator contract to simulated ethereum blockchain") backend.Commit() - // Deploy consumer it has 1 LINK + // Deploy consumer it has 10 LINK consumerContractAddress, _, consumerContract, err := vrf_consumer_v2.DeployVRFConsumerV2( carol, backend, coordinatorAddress, linkAddress) require.NoError(t, err, "failed to deploy VRFConsumer contract to simulated ethereum blockchain") - _, err = linkContract.Transfer(sergey, consumerContractAddress, oneEth) // Actually, LINK + _, err = linkContract.Transfer(sergey, consumerContractAddress, assets.Ether(10)) // Actually, LINK require.NoError(t, err, "failed to send LINK to VRFConsumer contract on simulated ethereum blockchain") backend.Commit() @@ -118,19 +128,28 @@ func newVRFCoordinatorV2Universe(t *testing.T, key ethkey.KeyV2) coordinatorV2Un vrf_malicious_consumer_v2.DeployVRFMaliciousConsumerV2( carol, backend, coordinatorAddress, linkAddress) require.NoError(t, err, "failed to deploy VRFMaliciousConsumer contract to simulated ethereum blockchain") - _, err = linkContract.Transfer(sergey, maliciousConsumerContractAddress, oneEth) // Actually, LINK + _, err = linkContract.Transfer(sergey, maliciousConsumerContractAddress, assets.Ether(1)) // Actually, LINK require.NoError(t, err, "failed to send LINK to VRFMaliciousConsumer contract on simulated ethereum blockchain") backend.Commit() // Set the configuration on the coordinator. _, err = coordinatorContract.SetConfig(neil, - uint16(1), // minRequestConfirmations - uint32(1000), // 0.0001 link flat fee - uint32(1000000), + uint16(1), // minRequestConfirmations + uint32(1000000), // gas limit uint32(60*60*24), // stalenessSeconds uint32(vrf.GasAfterPaymentCalculation), // gasAfterPaymentCalculation big.NewInt(10000000000000000), // 0.01 eth per link fallbackLinkPrice - big.NewInt(1000000000000000000), // Minimum subscription balance 0.01 link + vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ + FulfillmentFlatFeeLinkPPMTier1: uint32(1000), + FulfillmentFlatFeeLinkPPMTier2: uint32(1000), + FulfillmentFlatFeeLinkPPMTier3: uint32(100), + FulfillmentFlatFeeLinkPPMTier4: uint32(10), + FulfillmentFlatFeeLinkPPMTier5: uint32(1), + ReqsForTier2: big.NewInt(10), + ReqsForTier3: big.NewInt(20), + ReqsForTier4: big.NewInt(30), + ReqsForTier5: big.NewInt(40), + }, ) require.NoError(t, err, "failed to set coordinator configuration") backend.Commit() @@ -155,17 +174,231 @@ func newVRFCoordinatorV2Universe(t *testing.T, key ethkey.KeyV2) coordinatorV2Un } } +// Send eth from prefunded account. +// Amount is number of ETH not wei. +func sendEth(t *testing.T, key ethkey.KeyV2, ec *backends.SimulatedBackend, to common.Address, eth int) { + nonce, err := ec.PendingNonceAt(context.Background(), key.Address.Address()) + require.NoError(t, err) + tx := gethtypes.NewTx(&gethtypes.DynamicFeeTx{ + ChainID: big.NewInt(1337), + Nonce: nonce, + GasTipCap: big.NewInt(1), + GasFeeCap: big.NewInt(10000000000), // block base fee in sim + Gas: uint64(21000), + To: &to, + Value: big.NewInt(0).Mul(big.NewInt(int64(eth)), big.NewInt(1000000000000000000)), + Data: nil, + }) + signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(big.NewInt(1337)), key.ToEcdsaPrivKey()) + require.NoError(t, err) + err = ec.SendTransaction(context.Background(), signedTx) + require.NoError(t, err) + ec.Commit() +} + +func TestIntegrationVRFV2_OffchainSimulation(t *testing.T) { + config, _, _ := heavyweight.FullTestDB(t, "vrf_v2_integration_sim", true, true) + ownerKey := cltest.MustGenerateRandomKey(t) + uni := newVRFCoordinatorV2Universe(t, ownerKey) + app := cltest.NewApplicationWithConfigAndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey) + config.Overrides.GlobalEvmGasLimitDefault = null.NewInt(0, false) + config.Overrides.GlobalMinIncomingConfirmations = null.IntFrom(2) + + // Lets create 2 gas lanes + key1, err := app.KeyStore.Eth().Create(big.NewInt(1337)) + require.NoError(t, err) + sendEth(t, ownerKey, uni.backend, key1.Address.Address(), 10) + key2, err := app.KeyStore.Eth().Create(big.NewInt(1337)) + require.NoError(t, err) + sendEth(t, ownerKey, uni.backend, key2.Address.Address(), 10) + + gasPrice := decimal.NewFromBigInt(big.NewInt(10000000000), 0) // Default is 10 gwei + configureSimChain(app, map[string]types.ChainCfg{ + key1.Address.String(): { + EvmMaxGasPriceWei: utils.NewBig(big.NewInt(10000000000)), // 10 gwei + }, + key2.Address.String(): { + EvmMaxGasPriceWei: utils.NewBig(big.NewInt(100000000000)), // 100 gwei + }, + }, gasPrice.BigInt()) + require.NoError(t, app.Start()) + + var jbs []job.Job + // Create separate jobs for each gas lane and register their keys + for i, key := range []ethkey.KeyV2{key1, key2} { + vrfkey, err := app.GetKeyStore().VRF().Create() + require.NoError(t, err) + + jid := uuid.NewV4() + incomingConfs := 2 + s := testspecs.GenerateVRFSpec(testspecs.VRFSpecParams{ + JobID: jid.String(), + Name: fmt.Sprintf("vrf-primary-%d", i), + CoordinatorAddress: uni.rootContractAddress.String(), + Confirmations: incomingConfs, + PublicKey: vrfkey.PublicKey.String(), + FromAddress: key.Address.String(), + V2: true, + }).Toml() + jb, err := vrf.ValidatedVRFSpec(s) + t.Log(jb.VRFSpec.PublicKey.MustHash(), vrfkey.PublicKey.MustHash()) + require.NoError(t, err) + jb, err = app.JobSpawner().CreateJob(context.Background(), jb, jb.Name) + require.NoError(t, err) + registerProvingKeyHelper(t, uni, vrfkey) + jbs = append(jbs, jb) + } + // Wait until all jobs are active and listening for logs + gomega.NewGomegaWithT(t).Eventually(func() bool { + jbs := app.JobSpawner().ActiveJobs() + return len(jbs) == 2 + }, 5*time.Second, 100*time.Millisecond).Should(gomega.BeTrue()) + // Unfortunately the lb needs heads to be able to backfill logs to new subscribers. + // To avoid confirming + // TODO: it could just backfill immediately upon receiving a new subscriber? (though would + // only be useful for tests, probably a more robust way is to have the job spawner accept a signal that a + // job is fully up and running and not add it to the active jobs list before then) + time.Sleep(2 * time.Second) + + subFunding := decimal.RequireFromString("1000000000000000000") + _, err = uni.consumerContract.TestCreateSubscriptionAndFund(uni.carol, + subFunding.BigInt()) + require.NoError(t, err) + uni.backend.Commit() + sub, err := uni.rootContract.GetSubscription(nil, uint64(1)) + require.NoError(t, err) + t.Log("Sub balance", sub.Balance) + for i := 0; i < 5; i++ { + // Request 20 words (all get saved) so we use the full 300k + _, err := uni.consumerContract.TestRequestRandomness(uni.carol, jbs[0].VRFSpec.PublicKey.MustHash(), uint64(1), uint16(2), uint32(300000), uint32(20)) + require.NoError(t, err) + } + // Send a requests to the high gas price max keyhash, should remain queued until + // a significant topup + for i := 0; i < 1; i++ { + _, err := uni.consumerContract.TestRequestRandomness(uni.carol, jbs[1].VRFSpec.PublicKey.MustHash(), uint64(1), uint16(2), uint32(300000), uint32(20)) + require.NoError(t, err) + } + // Confirm all those requests + for i := 0; i < 3; i++ { + uni.backend.Commit() + } + // Now we should see ONLY 2 requests enqueued to the bptxm + // since we only have 2 requests worth of link at the max keyhash + // gas price. + var runs []pipeline.Run + gomega.NewGomegaWithT(t).Eventually(func() bool { + runs, err = app.PipelineORM().GetAllRuns() + require.NoError(t, err) + t.Log("runs", len(runs)) + return len(runs) == 2 + }, 5*time.Second, 1*time.Second).Should(gomega.BeTrue()) + // As we send new blocks, we should observe the fulfllments goes through the balance + // be reduced. + gomega.NewGomegaWithT(t).Consistently(func() bool { + runs, err = app.PipelineORM().GetAllRuns() + require.NoError(t, err) + uni.backend.Commit() + return len(runs) == 2 + }, 5*time.Second, 100*time.Millisecond).Should(gomega.BeTrue()) + sub, err = uni.rootContract.GetSubscription(nil, uint64(1)) + require.NoError(t, err) + t.Log("Sub balance should be near zero", sub.Balance) + etxes, n, err := app.BPTXMORM().EthTransactionsWithAttempts(0, 1000) + require.Equal(t, 2, n) // Only sent 2 transactions + // Should have max link set + require.NotNil(t, etxes[0].Meta) + require.NotNil(t, etxes[1].Meta) + md := bulletprooftxmanager.EthTxMeta{} + require.NoError(t, json.Unmarshal(*etxes[0].Meta, &md)) + require.NotEqual(t, "", md.MaxLink) + // Now lets top up and see the next batch go through + _, err = uni.consumerContract.TopUpSubscription(uni.carol, assets.Ether(1)) + require.NoError(t, err) + gomega.NewGomegaWithT(t).Eventually(func() bool { + runs, err = app.PipelineORM().GetAllRuns() + require.NoError(t, err) + t.Log("runs", len(runs)) + uni.backend.Commit() + return len(runs) == 4 + }, 10*time.Second, 1*time.Second).Should(gomega.BeTrue()) + // One more time for the final tx + _, err = uni.consumerContract.TopUpSubscription(uni.carol, assets.Ether(1)) + require.NoError(t, err) + gomega.NewGomegaWithT(t).Eventually(func() bool { + runs, err = app.PipelineORM().GetAllRuns() + require.NoError(t, err) + t.Log("runs", len(runs)) + uni.backend.Commit() + return len(runs) == 5 + }, 10*time.Second, 1*time.Second).Should(gomega.BeTrue()) + + // Send a huge topup and observe the high max gwei go through. + _, err = uni.consumerContract.TopUpSubscription(uni.carol, assets.Ether(7)) + require.NoError(t, err) + gomega.NewGomegaWithT(t).Eventually(func() bool { + runs, err = app.PipelineORM().GetAllRuns() + require.NoError(t, err) + t.Log("runs", len(runs)) + uni.backend.Commit() + return len(runs) == 6 + }, 10*time.Second, 1*time.Second).Should(gomega.BeTrue()) +} + +func configureSimChain(app *cltest.TestApplication, ks map[string]types.ChainCfg, defaultGasPrice *big.Int) { + zero := models.MustMakeDuration(0 * time.Millisecond) + reaperThreshold := models.MustMakeDuration(100 * time.Millisecond) + app.ChainSet.Configure( + big.NewInt(1337), + true, + types.ChainCfg{ + GasEstimatorMode: null.StringFrom("FixedPrice"), + EvmGasPriceDefault: utils.NewBig(defaultGasPrice), + EvmHeadTrackerMaxBufferSize: null.IntFrom(100), + EvmHeadTrackerSamplingInterval: &zero, // Head sampling disabled + EthTxResendAfterThreshold: &zero, + EvmFinalityDepth: null.IntFrom(15), + EthTxReaperThreshold: &reaperThreshold, + MinIncomingConfirmations: null.IntFrom(1), + MinRequiredOutgoingConfirmations: null.IntFrom(1), + MinimumContractPayment: assets.NewLinkFromJuels(100), + EvmGasLimitDefault: null.NewInt(2000000, true), + KeySpecific: ks, + }, + ) +} + +func registerProvingKeyHelper(t *testing.T, uni coordinatorV2Universe, vrfkey vrfkey.KeyV2) { + // Register a proving key associated with the VRF job. + p, err := vrfkey.PublicKey.Point() + require.NoError(t, err) + _, err = uni.rootContract.RegisterProvingKey( + uni.neil, uni.nallory.From, pair(secp256k1.Coordinates(p))) + require.NoError(t, err) + uni.backend.Commit() +} + func TestIntegrationVRFV2(t *testing.T) { config, _, _ := heavyweight.FullTestDB(t, "vrf_v2_integration", true, true) key := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2Universe(t, key) - config.Overrides.GlobalEvmGasLimitDefault = null.IntFrom(2000000) - - gasPrice := decimal.NewFromBigInt(evmconfig.DefaultGasPrice, 0) app := cltest.NewApplicationWithConfigAndKeyOnSimulatedBlockchain(t, config, uni.backend, key) - require.NoError(t, app.Start()) + config.Overrides.GlobalEvmGasLimitDefault = null.NewInt(0, false) + config.Overrides.GlobalMinIncomingConfirmations = null.IntFrom(2) + keys, err := app.KeyStore.Eth().SendingKeys() + + // Reconfigure the sim chain with a default gas price of 1 gwei, + // max gas limit of 2M and a key specific max 10 gwei price. + // Keep the prices low so we can operate with small link balance subscriptions. + gasPrice := decimal.NewFromBigInt(big.NewInt(1000000000), 0) + configureSimChain(app, map[string]types.ChainCfg{ + keys[0].Address.String(): { + EvmMaxGasPriceWei: utils.NewBig(big.NewInt(10000000000)), + }, + }, gasPrice.BigInt()) + require.NoError(t, app.Start()) vrfkey, err := app.GetKeyStore().VRF().Create() require.NoError(t, err) @@ -177,6 +410,7 @@ func TestIntegrationVRFV2(t *testing.T) { CoordinatorAddress: uni.rootContractAddress.String(), Confirmations: incomingConfs, PublicKey: vrfkey.PublicKey.String(), + FromAddress: keys[0].Address.String(), V2: true, }).Toml() jb, err := vrf.ValidatedVRFSpec(s) @@ -184,13 +418,7 @@ func TestIntegrationVRFV2(t *testing.T) { jb, err = app.JobSpawner().CreateJob(context.Background(), jb, jb.Name) require.NoError(t, err) - // Register a proving key associated with the VRF job. - p, err := vrfkey.PublicKey.Point() - require.NoError(t, err) - _, err = uni.rootContract.RegisterProvingKey( - uni.neil, uni.nallory.From, pair(secp256k1.Coordinates(p))) - require.NoError(t, err) - uni.backend.Commit() + registerProvingKeyHelper(t, uni, vrfkey) // Create and fund a subscription. // We should see that our subscription has 1 link. @@ -198,8 +426,8 @@ func TestIntegrationVRFV2(t *testing.T) { uni.consumerContractAddress, uni.rootContractAddress, }, []*big.Int{ - big.NewInt(1000000000000000000), // 1 link - big.NewInt(0), // 0 link + assets.Ether(10), // 10 link + big.NewInt(0), // 0 link }) subFunding := decimal.RequireFromString("1000000000000000000") _, err = uni.consumerContract.TestCreateSubscriptionAndFund(uni.carol, @@ -211,8 +439,8 @@ func TestIntegrationVRFV2(t *testing.T) { uni.rootContractAddress, uni.nallory.From, // Oracle's own address should have nothing }, []*big.Int{ - big.NewInt(0), - big.NewInt(1000000000000000000), + assets.Ether(9), + assets.Ether(1), big.NewInt(0), }) subId, err := uni.consumerContract.SSubId(nil) @@ -249,18 +477,19 @@ func TestIntegrationVRFV2(t *testing.T) { // keep blocks coming in for the lb to send the backfilled logs. uni.backend.Commit() return len(runs) == 1 && runs[0].State == pipeline.RunStatusCompleted - }, 5*time.Second, 1*time.Second).Should(gomega.BeTrue()) + }, 10*time.Second, 1*time.Second).Should(gomega.BeTrue()) // Wait for the request to be fulfilled on-chain. var rf []*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled gomega.NewGomegaWithT(t).Eventually(func() bool { rfIterator, err2 := uni.rootContract.FilterRandomWordsFulfilled(nil, nil) require.NoError(t, err2, "failed to logs") + uni.backend.Commit() for rfIterator.Next() { rf = append(rf, rfIterator.Event) } return len(rf) == 1 - }, 5*time.Second, 500*time.Millisecond).Should(gomega.BeTrue()) + }, 10*time.Second, 500*time.Millisecond).Should(gomega.BeTrue()) assert.True(t, rf[0].Success, "expected callback to succeed") fulfillReceipt, err := uni.backend.TransactionReceipt(context.Background(), rf[0].Raw.TxHash) require.NoError(t, err) @@ -297,8 +526,8 @@ func TestIntegrationVRFV2(t *testing.T) { ) t.Log("end balance", end) linkWeiCharged := start.Sub(end) - // Remove flat fee of 0.0001 to get fee for just gas. - linkCharged := linkWeiCharged.Sub(decimal.RequireFromString("100000000000000")).Div(wei) + // Remove flat fee of 0.001 to get fee for just gas. + linkCharged := linkWeiCharged.Sub(decimal.RequireFromString("1000000000000000")).Div(wei) t.Logf("subscription charged %s with gas prices of %s gwei and %s ETH per LINK\n", linkCharged, gasPrice.Div(gwei), weiPerUnitLink.Div(wei)) expected := decimal.RequireFromString(strconv.Itoa(int(fulfillReceipt.GasUsed))).Mul(gasPrice).Div(weiPerUnitLink) t.Logf("expected sub charge gas use %v %v off by %v", fulfillReceipt.GasUsed, expected, expected.Sub(linkCharged)) @@ -321,7 +550,7 @@ func TestIntegrationVRFV2(t *testing.T) { uni.rootContractAddress, uni.nallory.From, // Oracle's own address should have nothing }, []*big.Int{ - big.NewInt(0), + assets.Ether(9), subFunding.Sub(linkWeiCharged).BigInt(), linkWeiCharged.BigInt(), }) @@ -337,6 +566,8 @@ func TestMaliciousConsumer(t *testing.T) { key := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2Universe(t, key) config.Overrides.GlobalEvmGasLimitDefault = null.IntFrom(2000000) + config.Overrides.GlobalEvmMaxGasPriceWei = big.NewInt(1000000000) // 1 gwei + config.Overrides.GlobalEvmGasPriceDefault = big.NewInt(1000000000) // 1 gwei app := cltest.NewApplicationWithConfigAndKeyOnSimulatedBlockchain(t, config, uni.backend, key) require.NoError(t, app.Start()) @@ -393,9 +624,10 @@ func TestMaliciousConsumer(t *testing.T) { // before the job spawner has started the vrf services, which is fine // the lb will backfill the logs. However we need to // keep blocks coming in for the lb to send the backfilled logs. + t.Log("attempts", attempts) uni.backend.Commit() return len(attempts) == 1 && attempts[0].EthTx.State == bulletprooftxmanager.EthTxConfirmed - }, 5*time.Second, 1*time.Second).Should(gomega.BeTrue()) + }, 10*time.Second, 1*time.Second).Should(gomega.BeTrue()) // The fulfillment tx should succeed ch, err := app.GetChainSet().Default() @@ -546,7 +778,7 @@ func TestFulfillmentCost(t *testing.T) { "fulfillRandomWords", proof, rc) t.Log("estimate", estimate) // Establish very rough bounds on fulfillment cost - assert.Greater(t, estimate, uint64(130000)) + assert.Greater(t, estimate, uint64(120000)) assert.Less(t, estimate, uint64(500000)) } diff --git a/core/services/vrf/listener_v2.go b/core/services/vrf/listener_v2.go deleted file mode 100644 index 77f216c8b7a..00000000000 --- a/core/services/vrf/listener_v2.go +++ /dev/null @@ -1,403 +0,0 @@ -package vrf - -import ( - "context" - "fmt" - "sync" - - heaps "github.com/theodesp/go-heaps" - "github.com/theodesp/go-heaps/pairing" - - "github.com/smartcontractkit/chainlink/core/gracefulpanic" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink/core/internal/gethwrappers/generated/vrf_coordinator_v2" - "github.com/smartcontractkit/chainlink/core/logger" - "github.com/smartcontractkit/chainlink/core/services/bulletprooftxmanager" - "github.com/smartcontractkit/chainlink/core/services/eth" - httypes "github.com/smartcontractkit/chainlink/core/services/headtracker/types" - "github.com/smartcontractkit/chainlink/core/services/job" - "github.com/smartcontractkit/chainlink/core/services/keystore" - "github.com/smartcontractkit/chainlink/core/services/log" - "github.com/smartcontractkit/chainlink/core/services/pipeline" - "github.com/smartcontractkit/chainlink/core/services/postgres" - "github.com/smartcontractkit/chainlink/core/utils" - "gorm.io/gorm" -) - -const ( - // Gas to be used - GasAfterPaymentCalculation = 5000 + // subID balance update - 2100 + // cold subscription balance read - 20000 + // first time oracle balance update, note first time will be 20k, but 5k subsequently - 2*2100 - // cold read oracle address and oracle balance - 4800 + // request delete refund, note pre-london fork was 15k - 21000 + // base cost of the transaction - 6874 // Static costs of argument encoding etc. note that it varies by +/- x*12 for every x bytes of non-zero data in the proof. -) - -var ( - _ log.Listener = &listenerV2{} - _ job.Service = &listenerV2{} -) - -type pendingRequest struct { - confirmedAtBlock uint64 - req *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested - lb log.Broadcast -} - -type listenerV2 struct { - utils.StartStopOnce - cfg Config - l logger.Logger - abi abi.ABI - ethClient eth.Client - logBroadcaster log.Broadcaster - txm bulletprooftxmanager.TxManager - headBroadcaster httypes.HeadBroadcasterRegistry - coordinator *vrf_coordinator_v2.VRFCoordinatorV2 - pipelineRunner pipeline.Runner - pipelineORM pipeline.ORM - vorm keystore.VRFORM - job job.Job - db *gorm.DB - vrfks keystore.VRF - gethks keystore.Eth - reqLogs *utils.Mailbox - chStop chan struct{} - waitOnStop chan struct{} - newHead chan struct{} - latestHead uint64 - latestHeadMu sync.RWMutex - // We can keep these pending logs in memory because we - // only mark them confirmed once we send a corresponding fulfillment transaction. - // So on node restart in the middle of processing, the lb will resend them. - reqsMu sync.Mutex // Both goroutines write to reqs - reqs []pendingRequest - reqAdded func() // A simple debug helper - - // Data structures for reorg attack protection - // We want a map so we can do an O(1) count update every fulfillment log we get. - respCountMu sync.Mutex - respCount map[string]uint64 - // This auxiliary heap is to used when we need to purge the - // respCount map - we repeatedly want remove the minimum log. - // You could use a sorted list if the completed logs arrive in order, but they may not. - blockNumberToReqID *pairing.PairHeap -} - -func (lsn *listenerV2) Start() error { - return lsn.StartOnce("VRFListenerV2", func() error { - // Take the larger of the global vs specific. - // Note that the v2 vrf requests specify their own confirmation requirements. - // We wait for max(minConfs, request required confs) to be safe. - minConfs := lsn.cfg.MinIncomingConfirmations() - if lsn.job.VRFSpec.Confirmations > lsn.cfg.MinIncomingConfirmations() { - minConfs = lsn.job.VRFSpec.Confirmations - } - unsubscribeLogs := lsn.logBroadcaster.Register(lsn, log.ListenerOpts{ - Contract: lsn.coordinator.Address(), - ParseLog: lsn.coordinator.ParseLog, - LogsWithTopics: map[common.Hash][][]log.Topic{ - vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested{}.Topic(): { - { - log.Topic(lsn.job.VRFSpec.PublicKey.MustHash()), - }, - }, - }, - // Do not specify min confirmations, as it varies from request to request. - }) - - // Subscribe to the head broadcaster for handling - // per request conf requirements. - latestHead, unsubscribeHeadBroadcaster := lsn.headBroadcaster.Subscribe(lsn) - if latestHead != nil { - lsn.setLatestHead(*latestHead) - } - - go gracefulpanic.WrapRecover(func() { - lsn.runLogListener([]func(){unsubscribeLogs}, minConfs) - }) - go gracefulpanic.WrapRecover(func() { - lsn.runHeadListener(unsubscribeHeadBroadcaster) - }) - return nil - }) -} - -func (lsn *listenerV2) Connect(head *eth.Head) error { - lsn.latestHead = uint64(head.Number) - return nil -} - -// Removes and returns all the confirmed logs from -// the pending queue. -func (lsn *listenerV2) extractConfirmedLogs() []pendingRequest { - lsn.reqsMu.Lock() - defer lsn.reqsMu.Unlock() - var toProcess, toKeep []pendingRequest - for i := 0; i < len(lsn.reqs); i++ { - if lsn.reqs[i].confirmedAtBlock <= lsn.getLatestHead() { - toProcess = append(toProcess, lsn.reqs[i]) - } else { - toKeep = append(toKeep, lsn.reqs[i]) - } - } - lsn.reqs = toKeep - return toProcess -} - -// Note that we have 2 seconds to do this processing -func (lsn *listenerV2) OnNewLongestChain(_ context.Context, head eth.Head) { - lsn.setLatestHead(head) - select { - case lsn.newHead <- struct{}{}: - default: - } -} - -func (lsn *listenerV2) setLatestHead(h eth.Head) { - lsn.latestHeadMu.Lock() - defer lsn.latestHeadMu.Unlock() - num := uint64(h.Number) - if num > lsn.latestHead { - lsn.latestHead = num - } -} - -func (lsn *listenerV2) getLatestHead() uint64 { - lsn.latestHeadMu.RLock() - defer lsn.latestHeadMu.RUnlock() - return lsn.latestHead -} - -type fulfilledReqV2 struct { - blockNumber uint64 - reqID string -} - -func (a fulfilledReqV2) Compare(b heaps.Item) int { - a1 := a - a2 := b.(fulfilledReqV2) - switch { - case a1.blockNumber > a2.blockNumber: - return 1 - case a1.blockNumber < a2.blockNumber: - return -1 - default: - return 0 - } -} - -// Remove all entries 10000 blocks or older -// to avoid a memory leak. -func (lsn *listenerV2) pruneConfirmedRequestCounts() { - lsn.respCountMu.Lock() - defer lsn.respCountMu.Unlock() - min := lsn.blockNumberToReqID.FindMin() - for min != nil { - m := min.(fulfilledReqV2) - if m.blockNumber > (lsn.getLatestHead() - 10000) { - break - } - delete(lsn.respCount, m.reqID) - lsn.blockNumberToReqID.DeleteMin() - min = lsn.blockNumberToReqID.FindMin() - } -} - -// Listen for new heads -func (lsn *listenerV2) runHeadListener(unsubscribe func()) { - for { - select { - case <-lsn.chStop: - unsubscribe() - lsn.waitOnStop <- struct{}{} - return - case <-lsn.newHead: - toProcess := lsn.extractConfirmedLogs() - for _, r := range toProcess { - lsn.ProcessV2VRFRequest(r.req, r.lb) - } - lsn.pruneConfirmedRequestCounts() - } - } -} - -func (lsn *listenerV2) runLogListener(unsubscribes []func(), minConfs uint32) { - lsn.l.Infow("Listening for run requests", - "minConfs", minConfs) - for { - select { - case <-lsn.chStop: - for _, f := range unsubscribes { - f() - } - lsn.waitOnStop <- struct{}{} - return - case <-lsn.reqLogs.Notify(): - // Process all the logs in the queue if one is added - for { - i, exists := lsn.reqLogs.Retrieve() - if !exists { - break - } - lb, ok := i.(log.Broadcast) - if !ok { - panic(fmt.Sprintf("VRFListenerV2: invariant violated, expected log.Broadcast got %T", i)) - } - lsn.handleLog(lb, minConfs) - } - } - } -} - -func (lsn *listenerV2) shouldProcessLog(lb log.Broadcast) bool { - ctx, cancel := postgres.DefaultQueryCtx() - defer cancel() - consumed, err := lsn.logBroadcaster.WasAlreadyConsumed(lsn.db.WithContext(ctx), lb) - if err != nil { - lsn.l.Errorw("Could not determine if log was already consumed", "error", err, "txHash", lb.RawLog().TxHash) - // Do not process, let lb resend it as a retry mechanism. - return false - } - return !consumed -} - -func (lsn *listenerV2) getConfirmedAt(req *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, minConfs uint32) uint64 { - lsn.respCountMu.Lock() - defer lsn.respCountMu.Unlock() - newConfs := uint64(minConfs) * (1 << lsn.respCount[req.RequestId.String()]) - // We cap this at 200 because solidity only supports the most recent 256 blocks - // in the contract so if it was older than that, fulfillments would start failing - // without the blockhash store feeder. We use 200 to give the node plenty of time - // to fulfill even on fast chains. - if newConfs > 200 { - newConfs = 200 - } - if lsn.respCount[req.RequestId.String()] > 0 { - lsn.l.Warnw("Duplicate request found after fulfillment, doubling incoming confirmations", - "txHash", req.Raw.TxHash, - "blockNumber", req.Raw.BlockNumber, - "blockHash", req.Raw.BlockHash, - "reqID", req.RequestId.String(), - "newConfs", newConfs) - } - return req.Raw.BlockNumber + newConfs -} - -func (lsn *listenerV2) handleLog(lb log.Broadcast, minConfs uint32) { - if v, ok := lb.DecodedLog().(*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled); ok { - lsn.l.Infow("Received fulfilled log", "reqID", v.RequestId, "success", v.Success) - if !lsn.shouldProcessLog(lb) { - return - } - lsn.respCountMu.Lock() - lsn.respCount[v.RequestId.String()]++ - lsn.respCountMu.Unlock() - lsn.blockNumberToReqID.Insert(fulfilledReqV2{ - blockNumber: v.Raw.BlockNumber, - reqID: v.RequestId.String(), - }) - lsn.markLogAsConsumed(lb) - return - } - - req, err := lsn.coordinator.ParseRandomWordsRequested(lb.RawLog()) - if err != nil { - lsn.l.Errorw("Failed to parse log", "err", err, "txHash", lb.RawLog().TxHash) - if !lsn.shouldProcessLog(lb) { - return - } - lsn.markLogAsConsumed(lb) - return - } - - confirmedAt := lsn.getConfirmedAt(req, minConfs) - lsn.reqsMu.Lock() - lsn.reqs = append(lsn.reqs, pendingRequest{ - confirmedAtBlock: confirmedAt, - req: req, - lb: lb, - }) - lsn.reqAdded() - lsn.reqsMu.Unlock() -} - -func (lsn *listenerV2) markLogAsConsumed(lb log.Broadcast) { - ctx, cancel := postgres.DefaultQueryCtx() - defer cancel() - err := lsn.logBroadcaster.MarkConsumed(lsn.db.WithContext(ctx), lb) - lsn.l.ErrorIf(err, fmt.Sprintf("Unable to mark log %v as consumed", lb.String())) -} - -func (lsn *listenerV2) ProcessV2VRFRequest(req *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, lb log.Broadcast) { - // Check if the vrf req has already been fulfilled - callback, err := lsn.coordinator.GetCommitment(nil, req.RequestId) - if err != nil { - lsn.l.Errorw("Unable to check if already fulfilled, processing anyways", "err", err, "txHash", req.Raw.TxHash) - } else if utils.IsEmpty(callback[:]) { - // If seedAndBlockNumber is zero then the response has been fulfilled - // and we should skip it - lsn.l.Infow("Request already fulfilled", "txHash", req.Raw.TxHash, "subID", req.SubId, "callback", callback) - lsn.markLogAsConsumed(lb) - return - } - - lsn.l.Infow("Received log request", - "log", lb.String(), - "reqID", req.RequestId.String(), - "txHash", req.Raw.TxHash, - "blockNumber", req.Raw.BlockNumber, - "blockHash", req.Raw.BlockHash, - "seed", req.PreSeed) - - vars := pipeline.NewVarsFrom(map[string]interface{}{ - "jobSpec": map[string]interface{}{ - "databaseID": lsn.job.ID, - "externalJobID": lsn.job.ExternalJobID, - "name": lsn.job.Name.ValueOrZero(), - "publicKey": lsn.job.VRFSpec.PublicKey[:], - }, - "jobRun": map[string]interface{}{ - "logBlockHash": req.Raw.BlockHash[:], - "logBlockNumber": req.Raw.BlockNumber, - "logTxHash": req.Raw.TxHash, - "logTopics": req.Raw.Topics, - "logData": req.Raw.Data, - }, - }) - run := pipeline.NewRun(*lsn.job.PipelineSpec, vars) - if _, err = lsn.pipelineRunner.Run(context.Background(), &run, lsn.l, true, func(tx *gorm.DB) error { - // Always mark consumed regardless of whether the proof failed or not. - if err = lsn.logBroadcaster.MarkConsumed(tx, lb); err != nil { - lsn.l.Errorw("Failed mark consumed", "err", err) - } - return nil - }); err != nil { - lsn.l.Errorw("Failed executing run", "err", err) - } -} - -// Close complies with job.Service -func (lsn *listenerV2) Close() error { - return lsn.StopOnce("VRFListenerV2", func() error { - close(lsn.chStop) - <-lsn.waitOnStop - return nil - }) -} - -func (lsn *listenerV2) HandleLog(lb log.Broadcast) { - wasOverCapacity := lsn.reqLogs.Deliver(lb) - if wasOverCapacity { - lsn.l.Error("Log mailbox is over capacity - dropped the oldest log") - } -} - -// Job complies with log.Listener -func (lsn *listenerV2) JobID() int32 { - return lsn.job.ID -} diff --git a/core/services/vrf/listener_v3.go b/core/services/vrf/listener_v3.go new file mode 100644 index 00000000000..b64eae232f4 --- /dev/null +++ b/core/services/vrf/listener_v3.go @@ -0,0 +1,553 @@ +package vrf + +import ( + "context" + "fmt" + "math/big" + "sync" + "time" + + heaps "github.com/theodesp/go-heaps" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink/core/gracefulpanic" + "github.com/smartcontractkit/chainlink/core/internal/gethwrappers/generated/vrf_coordinator_v2" + "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/null" + "github.com/smartcontractkit/chainlink/core/services/bulletprooftxmanager" + "github.com/smartcontractkit/chainlink/core/services/eth" + "github.com/smartcontractkit/chainlink/core/services/job" + "github.com/smartcontractkit/chainlink/core/services/keystore" + "github.com/smartcontractkit/chainlink/core/services/log" + "github.com/smartcontractkit/chainlink/core/services/pipeline" + "github.com/smartcontractkit/chainlink/core/services/postgres" + "github.com/smartcontractkit/chainlink/core/utils" + "github.com/theodesp/go-heaps/pairing" + "gorm.io/gorm" +) + +var ( + _ log.Listener = &listenerV2{} + _ job.Service = &listenerV2{} +) + +const ( + // Gas used after computing the payment + GasAfterPaymentCalculation = 21000 + // base cost of the transaction + 100 + 5000 + // warm subscription balance read and update. See https://eips.ethereum.org/EIPS/eip-2929 + 2*2100 + 20000 - // cold read oracle address and oracle balance and first time oracle balance update, note first time will be 20k, but 5k subsequently + 4800 + // request delete refund (refunds happen after execution), note pre-london fork was 15k. See https://eips.ethereum.org/EIPS/eip-3529 + 4605 // Ppositive static costs of argument encoding etc. note that it varies by +/- x*12 for every x bytes of non-zero data in the proof. +) + +type pendingRequest struct { + confirmedAtBlock uint64 + req *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested + lb log.Broadcast +} + +type listenerV2 struct { + utils.StartStopOnce + cfg Config + l logger.Logger + abi abi.ABI + ethClient eth.Client + logBroadcaster log.Broadcaster + txm bulletprooftxmanager.TxManager + coordinator *vrf_coordinator_v2.VRFCoordinatorV2 + pipelineRunner pipeline.Runner + pipelineORM pipeline.ORM + vorm keystore.VRFORM + job job.Job + db *gorm.DB + vrfks keystore.VRF + gethks keystore.Eth + reqLogs *utils.Mailbox + chStop chan struct{} + waitOnStop chan struct{} + // We can keep these pending logs in memory because we + // only mark them confirmed once we send a corresponding fulfillment transaction. + // So on node restart in the middle of processing, the lb will resend them. + reqsMu sync.Mutex // Both goroutines write to reqs + reqs []pendingRequest + reqAdded func() // A simple debug helper + + // Data structures for reorg attack protection + // We want a map so we can do an O(1) count update every fulfillment log we get. + respCountMu sync.Mutex + respCount map[string]uint64 + // This auxiliary heap is to used when we need to purge the + // respCount map - we repeatedly want remove the minimum log. + // You could use a sorted list if the completed logs arrive in order, but they may not. + blockNumberToReqID *pairing.PairHeap +} + +func (lsn *listenerV2) Start() error { + return lsn.StartOnce("VRFListenerV2", func() error { + // Take the larger of the global vs specific. + // Note that the v2 vrf requests specify their own confirmation requirements. + // We wait for max(minConfs, request required confs) to be safe. + minConfs := lsn.cfg.MinIncomingConfirmations() + if lsn.job.VRFSpec.Confirmations > lsn.cfg.MinIncomingConfirmations() { + minConfs = lsn.job.VRFSpec.Confirmations + } + unsubscribeLogs := lsn.logBroadcaster.Register(lsn, log.ListenerOpts{ + Contract: lsn.coordinator.Address(), + ParseLog: lsn.coordinator.ParseLog, + LogsWithTopics: map[common.Hash][][]log.Topic{ + vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested{}.Topic(): { + { + log.Topic(lsn.job.VRFSpec.PublicKey.MustHash()), + }, + }, + }, + // Do not specify min confirmations, as it varies from request to request. + }) + + // Log listener gathers request logs + go gracefulpanic.WrapRecover(func() { + lsn.runLogListener([]func(){unsubscribeLogs}, minConfs) + }) + // Request handler periodically computes a set of logs which can be fulfilled. + go gracefulpanic.WrapRecover(func() { + lsn.runRequestHandler() + }) + return nil + }) +} + +// Returns all the confirmed logs from +// the pending queue by subscription +func (lsn *listenerV2) getConfirmedLogsBySub(latestHead uint64) map[uint64][]pendingRequest { + lsn.reqsMu.Lock() + defer lsn.reqsMu.Unlock() + var toProcess = make(map[uint64][]pendingRequest) + for i := 0; i < len(lsn.reqs); i++ { + if r := lsn.reqs[i]; r.confirmedAtBlock <= latestHead { + toProcess[r.req.SubId] = append(toProcess[r.req.SubId], r) + } + } + return toProcess +} + +// TODO: on second thought, I think it is more efficient to use the HB +func (lsn *listenerV2) getLatestHead() uint64 { + latestHead, err := lsn.ethClient.HeaderByNumber(context.Background(), nil) + if err != nil { + logger.Errorw("VRFListenerV2: unable to read latest head", "err", err) + return 0 + } + return latestHead.Number.Uint64() +} + +// Remove all entries 10000 blocks or older +// to avoid a memory leak. +func (lsn *listenerV2) pruneConfirmedRequestCounts() { + lsn.respCountMu.Lock() + defer lsn.respCountMu.Unlock() + min := lsn.blockNumberToReqID.FindMin() + for min != nil { + m := min.(fulfilledReqV2) + if m.blockNumber > (lsn.getLatestHead() - 10000) { + break + } + delete(lsn.respCount, m.reqID) + lsn.blockNumberToReqID.DeleteMin() + min = lsn.blockNumberToReqID.FindMin() + } +} + +// Determine a set of logs that are confirmed +// and the subscription has sufficient balance to fulfill, +// given a eth call with the max gas price. +// Note we have to consider the pending reqs already in the bptxm as already "spent" link, +// using a max link consumed in their metadata. +// A user will need a minBalance capable of fulfilling a single req at the max gas price or nothing will happen. +// This is acceptable as users can choose different keyhashes which have different max gas prices. +// Other variables which can change the bill amount between our eth call simulation and tx execution: +// - Link/eth price fluctation +// - Falling back to BHS +// However the likelihood is vanishingly small as +// 1) the window between simulation and tx execution is tiny. +// 2) the max gas price provides a very large buffer most of the time. +// Its easier to optimistically assume it will go though and in the rare case of a reversion +// we simply retry TODO: follow up where if we see a fulfillment revert, return log to the queue. +func (lsn *listenerV2) processPendingVRFRequests() { + latestHead, err := lsn.ethClient.HeaderByNumber(context.Background(), nil) + if err != nil { + logger.Errorw("VRFListenerV2: unable to read latest head", "err", err) + return + } + confirmed := lsn.getConfirmedLogsBySub(latestHead.Number.Uint64()) + // TODO: also probably want to order these by request time so we service oldest first + // Get subscription balance. Note that outside of this request handler, this can only decrease while there + // are no pending requests + if len(confirmed) == 0 { + logger.Infow("VRFListenerV2: no pending requests") + return + } + for subID, reqs := range confirmed { + sub, err := lsn.coordinator.GetSubscription(nil, subID) + if err != nil { + logger.Errorw("VRFListenerV2: unable to read subscription balance", "err", err) + return + } + keys, err := lsn.gethks.SendingKeys() + if err != nil { + logger.Errorw("VRFListenerV2: unable to read sending keys", "err", err) + continue + } + fromAddress := keys[0].Address + if lsn.job.VRFSpec.FromAddress != nil { + fromAddress = *lsn.job.VRFSpec.FromAddress + } + maxGasPrice := lsn.cfg.KeySpecificMaxGasPriceWei(fromAddress.Address()) + startBalance := sub.Balance + lsn.processRequestsPerSub(fromAddress.Address(), startBalance, maxGasPrice, reqs) + } + lsn.pruneConfirmedRequestCounts() +} + +func MaybeSubtractReservedLink(l logger.Logger, db *gorm.DB, fromAddress common.Address, startBalance *big.Int) (*big.Int, error) { + var reservedLink string + err := db.Raw(`SELECT SUM(CAST(meta->>'MaxLink' AS NUMERIC(78, 0))) + FROM eth_txes + WHERE meta->>'MaxLink' IS NOT NULL + AND (state <> 'fatal_error' AND state <> 'confirmed' AND state <> 'confirmed_missing_receipt') + GROUP BY from_address = ?`, fromAddress).Scan(&reservedLink).Error + if err != nil { + l.Errorw("VRFListenerV2: could not get reserved link", "err", err) + return startBalance, err + } + + if reservedLink != "" { + reservedLinkInt, success := big.NewInt(0).SetString(reservedLink, 10) + if !success { + l.Errorw("VRFListenerV2: error converting reserved link", "reservedLink", reservedLink) + return startBalance, errors.New("unable to convert returned link") + } + // Subtract the reserved link + return startBalance.Sub(startBalance, reservedLinkInt), nil + } + return startBalance, nil +} + +type fulfilledReqV2 struct { + blockNumber uint64 + reqID string +} + +func (a fulfilledReqV2) Compare(b heaps.Item) int { + a1 := a + a2 := b.(fulfilledReqV2) + switch { + case a1.blockNumber > a2.blockNumber: + return 1 + case a1.blockNumber < a2.blockNumber: + return -1 + default: + return 0 + } +} + +func (lsn *listenerV2) processRequestsPerSub(fromAddress common.Address, startBalance *big.Int, maxGasPrice *big.Int, reqs []pendingRequest) { + var err1 error + startBalance, err1 = MaybeSubtractReservedLink(lsn.l, lsn.db, fromAddress, startBalance) + if err1 != nil { + return + } + logger.Infow("VRFListenerV2: processing requests", + "sub", lsn.reqs[0].req.SubId, + "maxGasPrice", maxGasPrice.String(), + "reqs", len(reqs), + "startBalance", startBalance.String(), + ) + // Attempt to process every request, break if we run out of balance + var processed = make(map[string]struct{}) + for _, req := range reqs { + // This check to see if the log was consumed needs to be in the same + // goroutine as the mark consumed to avoid processing duplicates. + if !lsn.shouldProcessLog(req.lb) { + continue + } + // Check if the vrf req has already been fulfilled + // If so we just mark it completed + callback, err := lsn.coordinator.GetCommitment(nil, req.req.RequestId) + if err != nil { + lsn.l.Errorw("VRFListenerV2: unable to check if already fulfilled, processing anyways", "err", err, "txHash", req.req.Raw.TxHash) + } else if utils.IsEmpty(callback[:]) { + // If seedAndBlockNumber is zero then the response has been fulfilled + // and we should skip it + lsn.l.Infow("VRFListenerV2: request already fulfilled", "txHash", req.req.Raw.TxHash, "subID", req.req.SubId, "callback", callback) + lsn.markLogAsConsumed(req.lb) + processed[req.req.RequestId.String()] = struct{}{} + continue + } + // Run the pipeline to determine the max link that could be billed at maxGasPrice. + // The ethcall will error if there is currently insufficient balance onchain. + bi, run, payload, gaslimit, err := lsn.getMaxLinkForFulfillment(maxGasPrice, req) + if err != nil { + continue + } + if startBalance.Cmp(bi) < 0 { + // Insufficient funds, have to wait for a user top up + // leave it unprocessed for now + lsn.l.Infow("VRFListenerV2: insufficient link balance to fulfill a request, breaking", "balance", startBalance, "maxLink", bi) + break + } + lsn.l.Infow("VRFListenerV2: enqueuing fulfillment", "balance", startBalance, "reqID", req.req.RequestId) + // We have enough balance to service it, lets enqueue for bptxm + err = postgres.NewGormTransactionManager(lsn.db).Transact(func(ctx context.Context) error { + tx := postgres.TxFromContext(ctx, lsn.db) + if _, err = lsn.pipelineRunner.InsertFinishedRun(postgres.UnwrapGorm(tx), run, true); err != nil { + return err + } + if err = lsn.logBroadcaster.MarkConsumed(tx, req.lb); err != nil { + return err + } + _, err = lsn.txm.CreateEthTransaction(tx, bulletprooftxmanager.NewTx{ + FromAddress: fromAddress, + ToAddress: lsn.coordinator.Address(), + EncodedPayload: hexutil.MustDecode(payload), + GasLimit: gaslimit, + Meta: &bulletprooftxmanager.EthTxMeta{ + RequestID: common.BytesToHash(req.req.RequestId.Bytes()), + MaxLink: bi.String(), + }, + MinConfirmations: null.Uint32From(uint32(lsn.cfg.MinRequiredOutgoingConfirmations())), + Strategy: bulletprooftxmanager.NewSendEveryStrategy(false), // We already simd + }) + return err + }) + if err != nil { + lsn.l.Errorw("VRFListenerV2: error enqueuing fulfillment, requeuing request", + "err", err, + "reqID", req.req.RequestId, + "txHash", req.req.Raw.TxHash) + continue + } + // If we successfully enqueued for the bptxm, subtract that balance + // And loop to attempt to enqueue another fulfillment + startBalance = startBalance.Sub(startBalance, bi) + processed[req.req.RequestId.String()] = struct{}{} + } + // Remove all the confirmed logs + var toKeep []pendingRequest + for _, req := range reqs { + if _, ok := processed[req.req.RequestId.String()]; !ok { + toKeep = append(toKeep, req) + } + } + lsn.reqsMu.Lock() + lsn.reqs = toKeep + lsn.reqsMu.Unlock() + lsn.l.Infow("VRFListenerV2: finished processing for sub", + "sub", reqs[0].req.SubId, + "total reqs", len(reqs), + "total processed", len(processed), + "total remaining", len(toKeep)) + +} + +// Here we use the pipeline to parse the log, generate a vrf response +// then simulate the transaction at the max gas price to determine its maximum link cost. +func (lsn *listenerV2) getMaxLinkForFulfillment(maxGasPrice *big.Int, req pendingRequest) (*big.Int, pipeline.Run, string, uint64, error) { + var ( + maxLink *big.Int + payload string + gaslimit uint64 + ) + vars := pipeline.NewVarsFrom(map[string]interface{}{ + "jobSpec": map[string]interface{}{ + "databaseID": lsn.job.ID, + "externalJobID": lsn.job.ExternalJobID, + "name": lsn.job.Name.ValueOrZero(), + "publicKey": lsn.job.VRFSpec.PublicKey[:], + "maxGasPrice": maxGasPrice.String(), + }, + "jobRun": map[string]interface{}{ + "logBlockHash": req.req.Raw.BlockHash[:], + "logBlockNumber": req.req.Raw.BlockNumber, + "logTxHash": req.req.Raw.TxHash, + "logTopics": req.req.Raw.Topics, + "logData": req.req.Raw.Data, + }, + }) + run, trrs, err := lsn.pipelineRunner.ExecuteRun(context.Background(), *lsn.job.PipelineSpec, vars, lsn.l) + if err != nil { + logger.Errorw("VRFListenerV2: failed executing run", "err", err) + return maxLink, run, payload, gaslimit, err + } + // The call task will fail if there are insufficient funds + if run.AllErrors.HasError() { + logger.Warnw("VRFListenerV2: simulation errored, possibly insufficient funds. Request will remain unprocessed until funds are available", "err", err, "max gas price", maxGasPrice) + return maxLink, run, payload, gaslimit, errors.New("run errored") + } + if len(trrs.FinalResult().Values) != 1 { + logger.Errorw("VRFListenerV2: unexpected number of outputs", "err", err) + return maxLink, run, payload, gaslimit, errors.New("unexpected number of outputs") + } + // Run succeeded, we expect a byte array representing the billing amount + b, ok := trrs.FinalResult().Values[0].([]uint8) + if !ok { + logger.Errorw("VRFListenerV2: unexpected type") + return maxLink, run, payload, gaslimit, errors.New("expected []uint8 final result") + } + maxLink = utils.HexToBig(hexutil.Encode(b)[2:]) + for _, trr := range trrs { + if trr.Task.Type() == pipeline.TaskTypeVRFV2 { + m := trr.Result.Value.(map[string]interface{}) + payload = m["output"].(string) + } + if trr.Task.Type() == pipeline.TaskTypeEstimateGasLimit { + gaslimit = trr.Result.Value.(uint64) + } + } + return maxLink, run, payload, gaslimit, nil +} + +func (lsn *listenerV2) runRequestHandler() { + // TODO: Probably would have to be a configuration parameter per job so chains could have faster ones + tick := time.NewTicker(5 * time.Second) + defer tick.Stop() + for { + select { + case <-lsn.chStop: + lsn.waitOnStop <- struct{}{} + return + case <-tick.C: + lsn.processPendingVRFRequests() + } + } +} + +func (lsn *listenerV2) runLogListener(unsubscribes []func(), minConfs uint32) { + lsn.l.Infow("VRFListenerV2: listening for run requests", + "minConfs", minConfs) + for { + select { + case <-lsn.chStop: + for _, f := range unsubscribes { + f() + } + lsn.waitOnStop <- struct{}{} + return + case <-lsn.reqLogs.Notify(): + // Process all the logs in the queue if one is added + for { + i, exists := lsn.reqLogs.Retrieve() + if !exists { + break + } + lb, ok := i.(log.Broadcast) + if !ok { + panic(fmt.Sprintf("VRFListenerV2: invariant violated, expected log.Broadcast got %T", i)) + } + lsn.handleLog(lb, minConfs) + } + } + } +} + +func (lsn *listenerV2) shouldProcessLog(lb log.Broadcast) bool { + ctx, cancel := postgres.DefaultQueryCtx() + defer cancel() + consumed, err := lsn.logBroadcaster.WasAlreadyConsumed(lsn.db.WithContext(ctx), lb) + if err != nil { + lsn.l.Errorw("VRFListenerV2: could not determine if log was already consumed", "error", err, "txHash", lb.RawLog().TxHash) + // Do not process, let lb resend it as a retry mechanism. + return false + } + return !consumed +} + +func (lsn *listenerV2) getConfirmedAt(req *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, minConfs uint32) uint64 { + lsn.respCountMu.Lock() + defer lsn.respCountMu.Unlock() + newConfs := uint64(minConfs) * (1 << lsn.respCount[req.RequestId.String()]) + // We cap this at 200 because solidity only supports the most recent 256 blocks + // in the contract so if it was older than that, fulfillments would start failing + // without the blockhash store feeder. We use 200 to give the node plenty of time + // to fulfill even on fast chains. + if newConfs > 200 { + newConfs = 200 + } + if lsn.respCount[req.RequestId.String()] > 0 { + lsn.l.Warnw("VRFListenerV2: duplicate request found after fulfillment, doubling incoming confirmations", + "txHash", req.Raw.TxHash, + "blockNumber", req.Raw.BlockNumber, + "blockHash", req.Raw.BlockHash, + "reqID", req.RequestId.String(), + "newConfs", newConfs) + } + return req.Raw.BlockNumber + newConfs +} + +func (lsn *listenerV2) handleLog(lb log.Broadcast, minConfs uint32) { + if v, ok := lb.DecodedLog().(*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled); ok { + lsn.l.Infow("Received fulfilled log", "reqID", v.RequestId, "success", v.Success) + if !lsn.shouldProcessLog(lb) { + return + } + lsn.respCountMu.Lock() + lsn.respCount[v.RequestId.String()]++ + lsn.respCountMu.Unlock() + lsn.blockNumberToReqID.Insert(fulfilledReqV2{ + blockNumber: v.Raw.BlockNumber, + reqID: v.RequestId.String(), + }) + lsn.markLogAsConsumed(lb) + return + } + + req, err := lsn.coordinator.ParseRandomWordsRequested(lb.RawLog()) + if err != nil { + lsn.l.Errorw("VRFListenerV2: failed to parse log", "err", err, "txHash", lb.RawLog().TxHash) + if !lsn.shouldProcessLog(lb) { + return + } + lsn.markLogAsConsumed(lb) + return + } + + confirmedAt := lsn.getConfirmedAt(req, minConfs) + lsn.reqsMu.Lock() + lsn.reqs = append(lsn.reqs, pendingRequest{ + confirmedAtBlock: confirmedAt, + req: req, + lb: lb, + }) + lsn.reqAdded() + lsn.reqsMu.Unlock() +} + +func (lsn *listenerV2) markLogAsConsumed(lb log.Broadcast) { + ctx, cancel := postgres.DefaultQueryCtx() + defer cancel() + err := lsn.logBroadcaster.MarkConsumed(lsn.db.WithContext(ctx), lb) + lsn.l.ErrorIf(err, fmt.Sprintf("VRFListenerV2: unable to mark log %v as consumed", lb.String())) +} + +// Close complies with job.Service +func (lsn *listenerV2) Close() error { + return lsn.StopOnce("VRFListenerV2", func() error { + close(lsn.chStop) + <-lsn.waitOnStop + return nil + }) +} + +func (lsn *listenerV2) HandleLog(lb log.Broadcast) { + wasOverCapacity := lsn.reqLogs.Deliver(lb) + if wasOverCapacity { + logger.Error("VRFListenerV2: log mailbox is over capacity - dropped the oldest log") + } +} + +// Job complies with log.Listener +func (lsn *listenerV2) JobID() int32 { + return lsn.job.ID +} diff --git a/core/services/vrf/listener_v3_test.go b/core/services/vrf/listener_v3_test.go new file mode 100644 index 00000000000..ed05b57f33d --- /dev/null +++ b/core/services/vrf/listener_v3_test.go @@ -0,0 +1,88 @@ +package vrf + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + uuid "github.com/satori/go.uuid" + "github.com/smartcontractkit/chainlink/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/core/logger" + "github.com/smartcontractkit/chainlink/core/services/bulletprooftxmanager" + "github.com/smartcontractkit/chainlink/core/services/keystore" + "github.com/smartcontractkit/chainlink/core/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gorm.io/gorm" +) + +func addEthTx(t *testing.T, db *gorm.DB, from common.Address, state bulletprooftxmanager.EthTxState, maxLink string) { + err := db.Exec(`INSERT INTO eth_txes (from_address, to_address, encoded_payload, value, gas_limit, state, created_at, meta, subject, evm_chain_id, min_confirmations, pipeline_task_run_id, simulate) + VALUES ( + ?,?,?,?,?,?,NOW(),?,?,?,?,?,? + ) + RETURNING "eth_txes".*`, + from, // from + from, // to + []byte(`blah`), // payload + 0, // value + 0, // limit + state, + bulletprooftxmanager.EthTxMeta{ + MaxLink: maxLink, + }, + uuid.NullUUID{}, + 1337, + 0, // confs + nil, + false).Error + require.NoError(t, err) +} + +func addConfirmedEthTx(t *testing.T, db *gorm.DB, from common.Address, maxLink string) { + err := db.Exec(`INSERT INTO eth_txes (nonce, broadcast_at, error, from_address, to_address, encoded_payload, value, gas_limit, state, created_at, meta, subject, evm_chain_id, min_confirmations, pipeline_task_run_id, simulate) + VALUES ( + 10, NOW(), NULL, ?,?,?,?,?,'confirmed',NOW(),?,?,?,?,?,? + ) + RETURNING "eth_txes".*`, + from, // from + from, // to + []byte(`blah`), // payload + 0, // value + 0, // limit + bulletprooftxmanager.EthTxMeta{ + MaxLink: maxLink, + }, + uuid.NullUUID{}, + 1337, + 0, // confs + nil, + false).Error + require.NoError(t, err) +} + +func TestMaybeSubtractReservedLink(t *testing.T) { + db := pgtest.NewGormDB(t) + ks := keystore.New(db, utils.FastScryptParams, logger.Default) + require.NoError(t, ks.Unlock("blah")) + k, err := ks.Eth().Create(big.NewInt(1337)) + require.NoError(t, err) + + // Insert an unstarted eth tx with link metadata + addEthTx(t, db, k.Address.Address(), bulletprooftxmanager.EthTxUnstarted, "10000") + start, err := MaybeSubtractReservedLink(logger.Default, db, k.Address.Address(), big.NewInt(100000)) + require.NoError(t, err) + assert.Equal(t, "90000", start.String()) + + // A confirmed tx should not affect the starting balance + addConfirmedEthTx(t, db, k.Address.Address(), "10000") + start, err = MaybeSubtractReservedLink(logger.Default, db, k.Address.Address(), big.NewInt(100000)) + require.NoError(t, err) + assert.Equal(t, "90000", start.String()) + + // Another unstarted should + addEthTx(t, db, k.Address.Address(), bulletprooftxmanager.EthTxUnstarted, "10000") + start, err = MaybeSubtractReservedLink(logger.Default, db, k.Address.Address(), big.NewInt(100000)) + require.NoError(t, err) + assert.Equal(t, "80000", start.String()) +} diff --git a/core/store/migrate/migrations/0028_vrf_v2.sql b/core/store/migrate/migrations/0028_vrf_v2.sql index 31dbe8b2e05..46466ea7b15 100644 --- a/core/store/migrate/migrations/0028_vrf_v2.sql +++ b/core/store/migrate/migrations/0028_vrf_v2.sql @@ -4,6 +4,7 @@ CREATE TABLE vrf_specs ( public_key text NOT NULL, coordinator_address bytea NOT NULL, confirmations bigint NOT NULL, + from_address bytea, created_at timestamp with time zone NOT NULL, updated_at timestamp with time zone NOT NULL CONSTRAINT coordinator_address_len_chk CHECK (octet_length(coordinator_address) = 20) diff --git a/core/testdata/testspecs/v2_specs.go b/core/testdata/testspecs/v2_specs.go index 1d486d68710..3bf2ca3dae2 100644 --- a/core/testdata/testspecs/v2_specs.go +++ b/core/testdata/testspecs/v2_specs.go @@ -142,6 +142,7 @@ type VRFSpecParams struct { Name string CoordinatorAddress string Confirmations int + FromAddress string PublicKey string ObservationSource string V2 bool @@ -211,16 +212,18 @@ estimate_gas [type=estimategaslimit to="%s" multiplier="1.1" data="$(vrf.output)"] -submit_tx [type=ethtx to="%s" - data="$(vrf.output)" - gasLimit="$(estimate_gas)" - minConfirmations="0" - txMeta="{\\"requestTxHash\\": $(jobRun.logTxHash),\\"requestID\\": $(vrf.requestID),\\"jobID\\": $(jobSpec.databaseID)}"] -decode_log->vrf->estimate_gas->submit_tx -`, coordinatorAddress, coordinatorAddress) +simulate [type=ethcall + to="%s" + gas="$(estimate_gas)" + gasPrice="$(jobSpec.maxGasPrice)" + extractRevertReason=true + contract="%s" + data="$(vrf.output)"] +decode_log->vrf->estimate_gas->simulate +`, coordinatorAddress, coordinatorAddress, coordinatorAddress) } if params.ObservationSource != "" { - publicKey = params.ObservationSource + observationSource = params.ObservationSource } template := ` externalJobID = "%s" @@ -234,6 +237,11 @@ observationSource = """ %s """ ` + toml := fmt.Sprintf(template, jobID, name, coordinatorAddress, confirmations, publicKey, observationSource) + if params.FromAddress != "" { + toml = toml + "\n" + fmt.Sprintf(`fromAddress = "%s"`, params.FromAddress) + } + return VRFSpec{VRFSpecParams: VRFSpecParams{ JobID: jobID, Name: name, @@ -241,7 +249,7 @@ observationSource = """ Confirmations: confirmations, PublicKey: publicKey, ObservationSource: observationSource, - }, toml: fmt.Sprintf(template, jobID, name, coordinatorAddress, confirmations, publicKey, observationSource)} + }, toml: toml} } type OCRSpecParams struct { diff --git a/core/utils/finite_ticker.go b/core/utils/finite_ticker.go new file mode 100644 index 00000000000..448c6787d64 --- /dev/null +++ b/core/utils/finite_ticker.go @@ -0,0 +1,25 @@ +package utils + +import "time" + +func FiniteTicker(period time.Duration, onTick func()) func() { + tick := time.NewTicker(period) + chStop := make(chan struct{}) + go func() { + for { + select { + case <-tick.C: + onTick() + case <-chStop: + return + } + } + }() + + // NOTE: tick.Stop does not close the ticker channel, + // so we still need another way of returning (chStop). + return func() { + tick.Stop() + close(chStop) + } +} diff --git a/core/web/presenters/job.go b/core/web/presenters/job.go index 8d3be094bcf..9d053117ac8 100644 --- a/core/web/presenters/job.go +++ b/core/web/presenters/job.go @@ -208,17 +208,19 @@ func NewCronSpec(spec *job.CronSpec) *CronSpec { } type VRFSpec struct { - CoordinatorAddress ethkey.EIP55Address `json:"coordinatorAddress"` - PublicKey secp256k1.PublicKey `json:"publicKey"` - Confirmations uint32 `json:"confirmations"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + CoordinatorAddress ethkey.EIP55Address `json:"coordinatorAddress"` + PublicKey secp256k1.PublicKey `json:"publicKey"` + FromAddress *ethkey.EIP55Address `json:"fromAddress"` + Confirmations uint32 `json:"confirmations"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } func NewVRFSpec(spec *job.VRFSpec) *VRFSpec { return &VRFSpec{ CoordinatorAddress: spec.CoordinatorAddress, PublicKey: spec.PublicKey, + FromAddress: spec.FromAddress, Confirmations: spec.Confirmations, CreatedAt: spec.CreatedAt, UpdatedAt: spec.UpdatedAt,