Skip to content

Commit

Permalink
Add gas overhead per word to wrapper (#12669)
Browse files Browse the repository at this point in the history
* Update randomWords from memory to calldata

* Add per word overhead

* Generate geth wrappers

* Update go code

* remove wrapper gas overhead per word

* minor fix

* revert foundry dependency upgrade

* fix integration test

* add changesets

---------

Co-authored-by: jinhoonbang <jin.bang@smartcontract.com>
  • Loading branch information
leeyikjiun and jinhoonbang committed Apr 16, 2024
1 parent fe8fac7 commit 3134ce8
Show file tree
Hide file tree
Showing 33 changed files with 176 additions and 107 deletions.
5 changes: 5 additions & 0 deletions .changeset/tender-crews-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

vrfv2plus - account for num words in coordinator gas overhead in v2plus wrapper
5 changes: 5 additions & 0 deletions contracts/.changeset/new-crews-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chainlink/contracts": patch
---

vrfv2plus - account for num words in coordinator gas overhead in v2plus wrapper
4 changes: 2 additions & 2 deletions contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,12 @@ abstract contract VRFConsumerBaseV2Plus is IVRFMigratableConsumerV2Plus, Confirm
* @param randomWords the VRF output expanded to the requested number of words
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual;
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal virtual;

// rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
// proof. rawFulfillRandomness then calls fulfillRandomness, after validating
// the origin of the call
function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external {
function rawFulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) external {
if (msg.sender != address(s_vrfCoordinator)) {
revert OnlyCoordinatorCanFulfill(msg.sender, address(s_vrfCoordinator));
}
Expand Down
48 changes: 35 additions & 13 deletions contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
// in the pricing for wrapped requests. This includes the gas costs of proof verification and
// payment calculation in the coordinator.
uint32 private s_coordinatorGasOverhead;
uint16 private s_coordinatorGasOverheadPerWord;

// s_fulfillmentFlatFeeLinkPPM is the flat fee in millionths of native that VRFCoordinatorV2
// charges for native payment.
Expand All @@ -119,7 +120,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
// Wrapper has no premium. This premium is for VRFCoordinator.
uint8 private s_coordinatorLinkPremiumPercentage;

// 6 bytes left
// 4 bytes left
/* Storage Slot 5: END */

struct Callback {
Expand Down Expand Up @@ -202,6 +203,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
function setConfig(
uint32 _wrapperGasOverhead,
uint32 _coordinatorGasOverhead,
uint16 _coordinatorGasOverheadPerWord,
uint8 _coordinatorNativePremiumPercentage,
uint8 _coordinatorLinkPremiumPercentage,
bytes32 _keyHash,
Expand All @@ -223,6 +225,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume

s_wrapperGasOverhead = _wrapperGasOverhead;
s_coordinatorGasOverhead = _coordinatorGasOverhead;
s_coordinatorGasOverheadPerWord = _coordinatorGasOverheadPerWord;
s_coordinatorNativePremiumPercentage = _coordinatorNativePremiumPercentage;
s_coordinatorLinkPremiumPercentage = _coordinatorLinkPremiumPercentage;
s_keyHash = _keyHash;
Expand All @@ -238,6 +241,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
emit ConfigSet(
_wrapperGasOverhead,
_coordinatorGasOverhead,
_coordinatorGasOverheadPerWord,
_coordinatorNativePremiumPercentage,
_coordinatorLinkPremiumPercentage,
_keyHash,
Expand Down Expand Up @@ -270,6 +274,9 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
* @return coordinatorGasOverhead reflects the gas overhead of the coordinator's
* fulfillRandomWords function.
*
* @return coordinatorGasOverheadPerWord reflects the gas overhead per word of the coordinator's
* fulfillRandomWords function.
*
* @return wrapperNativePremiumPercentage is the premium ratio in percentage for native payment. For example, a value of 0
* indicates no premium. A value of 15 indicates a 15 percent premium.
*
Expand All @@ -292,6 +299,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
uint32 fulfillmentFlatFeeLinkDiscountPPM,
uint32 wrapperGasOverhead,
uint32 coordinatorGasOverhead,
uint16 coordinatorGasOverheadPerWord,
uint8 wrapperNativePremiumPercentage,
uint8 wrapperLinkPremiumPercentage,
bytes32 keyHash,
Expand All @@ -305,6 +313,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
s_fulfillmentFlatFeeLinkDiscountPPM,
s_wrapperGasOverhead,
s_coordinatorGasOverhead,
s_coordinatorGasOverheadPerWord,
s_coordinatorNativePremiumPercentage,
s_coordinatorLinkPremiumPercentage,
s_keyHash,
Expand All @@ -322,16 +331,18 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
* @param _callbackGasLimit is the gas limit used to estimate the price.
*/
function calculateRequestPrice(
uint32 _callbackGasLimit
uint32 _callbackGasLimit,
uint32 _numWords
) external view override onlyConfiguredNotDisabled returns (uint256) {
(int256 weiPerUnitLink, ) = _getFeedData();
return _calculateRequestPrice(_callbackGasLimit, tx.gasprice, weiPerUnitLink);
return _calculateRequestPrice(_callbackGasLimit, _numWords, tx.gasprice, weiPerUnitLink);
}

function calculateRequestPriceNative(
uint32 _callbackGasLimit
uint32 _callbackGasLimit,
uint32 _numWords
) external view override onlyConfiguredNotDisabled returns (uint256) {
return _calculateRequestPriceNative(_callbackGasLimit, tx.gasprice);
return _calculateRequestPriceNative(_callbackGasLimit, _numWords, tx.gasprice);
}

/**
Expand All @@ -345,28 +356,34 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
*/
function estimateRequestPrice(
uint32 _callbackGasLimit,
uint32 _numWords,
uint256 _requestGasPriceWei
) external view override onlyConfiguredNotDisabled returns (uint256) {
(int256 weiPerUnitLink, ) = _getFeedData();
return _calculateRequestPrice(_callbackGasLimit, _requestGasPriceWei, weiPerUnitLink);
return _calculateRequestPrice(_callbackGasLimit, _numWords, _requestGasPriceWei, weiPerUnitLink);
}

function estimateRequestPriceNative(
uint32 _callbackGasLimit,
uint32 _numWords,
uint256 _requestGasPriceWei
) external view override onlyConfiguredNotDisabled returns (uint256) {
return _calculateRequestPriceNative(_callbackGasLimit, _requestGasPriceWei);
return _calculateRequestPriceNative(_callbackGasLimit, _numWords, _requestGasPriceWei);
}

function _calculateRequestPriceNative(uint256 _gas, uint256 _requestGasPrice) internal view returns (uint256) {
function _calculateRequestPriceNative(
uint256 _gas,
uint32 _numWords,
uint256 _requestGasPrice
) internal view returns (uint256) {
// costWei is the base fee denominated in wei (native)
// (wei/gas) * gas
uint256 wrapperCostWei = _requestGasPrice * s_wrapperGasOverhead;

// coordinatorCostWei takes into account the L1 posting costs of the VRF fulfillment transaction, if we are on an L2.
// (wei/gas) * gas + l1wei
uint256 coordinatorCostWei = _requestGasPrice *
(_gas + s_coordinatorGasOverhead) +
(_gas + _getCoordinatorGasOverhead(_numWords)) +
ChainSpecificUtil._getL1CalldataGasCost(s_fulfillmentTxSizeBytes);

// coordinatorCostWithPremiumAndFlatFeeWei is the coordinator cost with the percentage premium and flat fee applied
Expand All @@ -379,6 +396,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume

function _calculateRequestPrice(
uint256 _gas,
uint32 _numWords,
uint256 _requestGasPrice,
int256 _weiPerUnitLink
) internal view returns (uint256) {
Expand All @@ -389,7 +407,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
// coordinatorCostWei takes into account the L1 posting costs of the VRF fulfillment transaction, if we are on an L2.
// (wei/gas) * gas + l1wei
uint256 coordinatorCostWei = _requestGasPrice *
(_gas + s_coordinatorGasOverhead) +
(_gas + _getCoordinatorGasOverhead(_numWords)) +
ChainSpecificUtil._getL1CalldataGasCost(s_fulfillmentTxSizeBytes);

// coordinatorCostWithPremiumAndFlatFeeWei is the coordinator cost with the percentage premium and flat fee applied
Expand Down Expand Up @@ -427,7 +445,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
checkPaymentMode(extraArgs, true);
uint32 eip150Overhead = _getEIP150Overhead(callbackGasLimit);
(int256 weiPerUnitLink, bool isFeedStale) = _getFeedData();
uint256 price = _calculateRequestPrice(callbackGasLimit, tx.gasprice, weiPerUnitLink);
uint256 price = _calculateRequestPrice(callbackGasLimit, numWords, tx.gasprice, weiPerUnitLink);
// solhint-disable-next-line gas-custom-errors
require(_amount >= price, "fee too low");
// solhint-disable-next-line gas-custom-errors
Expand Down Expand Up @@ -486,7 +504,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
checkPaymentMode(extraArgs, false);

uint32 eip150Overhead = _getEIP150Overhead(_callbackGasLimit);
uint256 price = _calculateRequestPriceNative(_callbackGasLimit, tx.gasprice);
uint256 price = _calculateRequestPriceNative(_callbackGasLimit, _numWords, tx.gasprice);
// solhint-disable-next-line gas-custom-errors
require(msg.value >= price, "fee too low");
// solhint-disable-next-line gas-custom-errors
Expand Down Expand Up @@ -557,7 +575,7 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
}

// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override {
function fulfillRandomWords(uint256 _requestId, uint256[] calldata _randomWords) internal override {
Callback memory callback = s_callbacks[_requestId];
delete s_callbacks[_requestId];

Expand Down Expand Up @@ -603,6 +621,10 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
return gas / 63 + 1;
}

function _getCoordinatorGasOverhead(uint32 numWords) internal view returns (uint32) {
return s_coordinatorGasOverhead + numWords * s_coordinatorGasOverheadPerWord;
}

/**
* @dev calls target address with exactly gasAmount gas and data as calldata
* or reverts if at least gasAmount gas is not available.
Expand Down
4 changes: 2 additions & 2 deletions contracts/src/v0.8/vrf/dev/VRFV2PlusWrapperConsumerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ abstract contract VRFV2PlusWrapperConsumerBase {
uint32 _numWords,
bytes memory extraArgs
) internal returns (uint256 requestId, uint256 reqPrice) {
reqPrice = i_vrfV2PlusWrapper.calculateRequestPrice(_callbackGasLimit);
reqPrice = i_vrfV2PlusWrapper.calculateRequestPrice(_callbackGasLimit, _numWords);
i_linkToken.transferAndCall(
address(i_vrfV2PlusWrapper),
reqPrice,
Expand All @@ -79,7 +79,7 @@ abstract contract VRFV2PlusWrapperConsumerBase {
uint32 _numWords,
bytes memory extraArgs
) internal returns (uint256 requestId, uint256 requestPrice) {
requestPrice = i_vrfV2PlusWrapper.calculateRequestPriceNative(_callbackGasLimit);
requestPrice = i_vrfV2PlusWrapper.calculateRequestPriceNative(_callbackGasLimit, _numWords);
return (
i_vrfV2PlusWrapper.requestRandomWordsInNative{value: requestPrice}(
_callbackGasLimit,
Expand Down
18 changes: 14 additions & 4 deletions contracts/src/v0.8/vrf/dev/interfaces/IVRFV2PlusWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface IVRFV2PlusWrapper {
event ConfigSet(
uint32 wrapperGasOverhead,
uint32 coordinatorGasOverhead,
uint16 coordinatorGasOverheadPerWord,
uint8 coordinatorNativePremiumPercentage,
uint8 coordinatorLinkPremiumPercentage,
bytes32 keyHash,
Expand Down Expand Up @@ -35,8 +36,9 @@ interface IVRFV2PlusWrapper {
* @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function.
*
* @param _callbackGasLimit is the gas limit used to estimate the price.
* @param _numWords is the number of words to request.
*/
function calculateRequestPrice(uint32 _callbackGasLimit) external view returns (uint256);
function calculateRequestPrice(uint32 _callbackGasLimit, uint32 _numWords) external view returns (uint256);

/**
* @notice Calculates the price of a VRF request in native with the given callbackGasLimit at the current
Expand All @@ -46,8 +48,9 @@ interface IVRFV2PlusWrapper {
* @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function.
*
* @param _callbackGasLimit is the gas limit used to estimate the price.
* @param _numWords is the number of words to request.
*/
function calculateRequestPriceNative(uint32 _callbackGasLimit) external view returns (uint256);
function calculateRequestPriceNative(uint32 _callbackGasLimit, uint32 _numWords) external view returns (uint256);

/**
* @notice Estimates the price of a VRF request with a specific gas limit and gas price.
Expand All @@ -56,9 +59,14 @@ interface IVRFV2PlusWrapper {
* @dev pricing.
*
* @param _callbackGasLimit is the gas limit used to estimate the price.
* @param _numWords is the number of words to request.
* @param _requestGasPriceWei is the gas price in wei used for the estimation.
*/
function estimateRequestPrice(uint32 _callbackGasLimit, uint256 _requestGasPriceWei) external view returns (uint256);
function estimateRequestPrice(
uint32 _callbackGasLimit,
uint32 _numWords,
uint256 _requestGasPriceWei
) external view returns (uint256);

/**
* @notice Estimates the price of a VRF request in native with a specific gas limit and gas price.
Expand All @@ -67,10 +75,12 @@ interface IVRFV2PlusWrapper {
* @dev pricing.
*
* @param _callbackGasLimit is the gas limit used to estimate the price.
* @param _numWords is the number of words to request.
* @param _requestGasPriceWei is the gas price in wei used for the estimation.
*/
function estimateRequestPriceNative(
uint32 _callbackGasLimit,
uint32 _numWords,
uint256 _requestGasPriceWei
) external view returns (uint256);

Expand All @@ -85,7 +95,7 @@ interface IVRFV2PlusWrapper {
uint32 _callbackGasLimit,
uint16 _requestConfirmations,
uint32 _numWords,
bytes memory extraArgs
bytes calldata extraArgs
) external payable returns (uint256 requestId);

function link() external view returns (address);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract VRFMaliciousConsumerV2Plus is VRFConsumerBaseV2Plus {
}

// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
s_gasAvailable = gasleft();
s_randomWords = randomWords;
s_requestId = requestId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ contract VRFV2PlusConsumerExample is ConfirmedOwner, VRFConsumerBaseV2Plus {
}

// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
// solhint-disable-next-line gas-custom-errors
require(requestId == s_recentRequestId, "request ID is incorrect");
s_requests[requestId].randomWords = randomWords;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract VRFV2PlusExternalSubOwnerExample is VRFConsumerBaseV2Plus {
}

// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
// solhint-disable-next-line gas-custom-errors
require(requestId == s_requestId, "request ID is incorrect");
s_randomWords = randomWords;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ contract VRFV2PlusLoadTestWithMetrics is VRFConsumerBaseV2Plus {
constructor(address _vrfCoordinator) VRFConsumerBaseV2Plus(_vrfCoordinator) {}

// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override {
function fulfillRandomWords(uint256 _requestId, uint256[] calldata _randomWords) internal override {
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
s_requests[_requestId].fulfilmentTimestamp = block.timestamp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract VRFV2PlusRevertingExample is VRFConsumerBaseV2Plus {
}

// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256, uint256[] memory) internal pure override {
function fulfillRandomWords(uint256, uint256[] calldata) internal pure override {
// solhint-disable-next-line gas-custom-errors, reason-string
revert();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ contract VRFV2PlusSingleConsumerExample is VRFConsumerBaseV2Plus {
}

// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
// solhint-disable-next-line gas-custom-errors
require(requestId == s_requestId, "request ID is incorrect");
s_randomWords = randomWords;
Expand Down
Loading

0 comments on commit 3134ce8

Please sign in to comment.