Skip to content

Commit

Permalink
feature: eth-vrf (#9646)
Browse files Browse the repository at this point in the history
* feature: ethereum vrf

* fix: compilation

* chore: remove fee tiers

* feat: generalize

* fix: tests

* fix: prettier

* chore: more prettier

* chore: bump migration version

* chore: fix compilation

* fix: tests

* fix: validation tests

* chore: use new task to differentiate v2 versions

* feat: refactor to support native

* fix: lint

* chore: rename V2.5 => V2Plus

* chore: more renames

* add gethwrapper for subscription api

* Revert "add gethwrapper for subscription api"

This reverts commit 9fd32de.

* chore: revert pnpm lock change

* fix: review comments

* chore: fix comments

* fix: name clash

* fix: lint and foundry build

* fix: nit comments

* fix: mistake

* Remove passing costs onto end-users.

* Fix prettier

* Fix import cycle

* Merge branch 'develop' into feature/eth-vrf

* Fix merge conflicts

* Generate fixes

* Feature/eth vrf integration tests (#9708)

* integration tests for VRF V2 Plus. WIP

* support vrfv2 plus in bhs and bhf

* small fixes

* fix failing force fulfill test cases

* make changes to vrfv2 consumer contract to make batching tests pass

* fix import

* add ci profile

* fix v1 test

* prettier

* Update contracts/src/v0.8/vrf/testhelpers/VRFV2PlusExternalSubOwnerExample.sol

Co-authored-by: Makram <makramkd@users.noreply.github.com>

* Update contracts/src/v0.8/vrf/testhelpers/VRFV2PlusRevertingExample.sol

Co-authored-by: Makram <makramkd@users.noreply.github.com>

* Update contracts/src/v0.8/vrf/testhelpers/VRFV2PlusSingleConsumerExample.sol

Co-authored-by: Makram <makramkd@users.noreply.github.com>

* address comments

---------

Co-authored-by: Makram <makramkd@users.noreply.github.com>

* Update contracts/src/v0.8/vrf/VRFCoordinatorV2Plus.sol

Co-authored-by: Makram <makramkd@users.noreply.github.com>

* minor changes

* fix: bump gethwrappers

---------

Co-authored-by: jinhoonbang <jin.bang@smartcontract.com>
Co-authored-by: Chris <104409744+vreff@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 14, 2023
1 parent a14faaf commit 7b57ad4
Show file tree
Hide file tree
Showing 100 changed files with 15,237 additions and 2,821 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/solidity-foundry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
if: ${{ needs.changes.outputs.changes == 'true' }}
run: |
forge --version
forge build --sizes
forge build
id: build
working-directory: contracts

Expand Down
3 changes: 3 additions & 0 deletions common/txmgr/types/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ type TxMeta[ADDR types.Hashable, TX_HASH types.Hashable] struct {
// Used for the VRFv2 - the subscription ID of the
// requester of the VRF.
SubID *uint64 `json:"SubId,omitempty"`
// Used for VRFv2Plus - max eth this tx will bill
// should it get bumped
MaxEth *string `json:"MaxEth,omitempty"`

// Used for keepers
UpkeepID *string `json:"UpkeepID,omitempty"`
Expand Down
4 changes: 2 additions & 2 deletions contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ src = 'src/v0.8/functions'
test = 'src/v0.8/functions/tests'

[profile.vrf]
optimizer_runs = 10000
optimizer_runs = 1000
src = 'src/v0.8/vrf'
test = 'src/v0.8/vrf' # skips tests for no VRF foundry tests
solc_version = '0.8.6'
Expand All @@ -33,4 +33,4 @@ src = 'src/v0.8/automation'
test = 'src/v0.8/automation/test'
solc_version = '0.8.6'

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
13 changes: 12 additions & 1 deletion contracts/scripts/native_solc_compile_all
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,26 @@ $SCRIPTPATH/native_solc8_6_compile mocks/VRFCoordinatorMock.sol
$SCRIPTPATH/native_solc8_6_compile vrf/VRFConsumerBaseV2.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFConsumerV2.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFMaliciousConsumerV2.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFMaliciousConsumerV2Plus.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFTestHelper.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFV2RevertingExample.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFV2PlusRevertingExample.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFV2ProxyAdmin.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFV2TransparentUpgradeableProxy.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFConsumerV2UpgradeableExample.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFConsumerV2PlusUpgradeableExample.sol
$SCRIPTPATH/native_solc8_6_compile vrf/BatchBlockhashStore.sol
$SCRIPTPATH/native_solc8_6_compile vrf/BatchVRFCoordinatorV2.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFCoordinatorV2TestHelper.sol
$SCRIPTPATH/native_solc8_6_compile vrf/VRFCoordinatorV2.sol 10000
$SCRIPTPATH/native_solc8_6_compile vrf/VRFOwner.sol

# VRF V2Plus
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFV2PlusConsumerExample.sol
$SCRIPTPATH/native_solc8_6_compile vrf/VRFCoordinatorV2Plus.sol 1000
$SCRIPTPATH/native_solc8_6_compile vrf/BatchVRFCoordinatorV2Plus.sol
$SCRIPTPATH/native_solc8_6_compile vrf/VRFV2PlusSubscriptionManager.sol

# VRF V2 Wrapper
$SCRIPTPATH/native_solc8_6_compile vrf/VRFV2Wrapper.sol
$SCRIPTPATH/native_solc8_6_compile interfaces/VRFV2WrapperInterface.sol
Expand All @@ -102,7 +111,9 @@ $SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFV2WrapperConsumerExample.s

# VRF Consumers and Mocks
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFExternalSubOwnerExample.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFV2PlusExternalSubOwnerExample.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFSingleConsumerExample.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFV2PlusSingleConsumerExample.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFOwnerlessConsumerExample.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFLoadTestOwnerlessConsumer.sol
$SCRIPTPATH/native_solc8_6_compile vrf/testhelpers/VRFLoadTestExternalSubOwner.sol
Expand All @@ -120,7 +131,7 @@ $SCRIPTPATH/native_solc8_6_compile tests/LogEmitter.sol
# Chainlink Functions
$SCRIPTPATH/native_solc_compile_all_functions

## llo-feeds
# llo-feeds
$SCRIPTPATH/native_solc_compile_all_llo

# Mocks
Expand Down
121 changes: 121 additions & 0 deletions contracts/src/v0.8/interfaces/IVRFCoordinatorV2Plus.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IVRFCoordinatorV2Plus {
/**
* @notice Get configuration relevant for making requests
* @return minimumRequestConfirmations global min for request confirmations
* @return maxGasLimit global max for request gas limit
* @return s_provingKeyHashes list of registered key hashes
*/
function getRequestConfig() external view returns (uint16, uint32, bytes32[] memory);

/**
* @notice Request a set of random words.
* @param keyHash - Corresponds to a particular oracle job which uses
* that key for generating the VRF proof. Different keyHash's have different gas price
* ceilings, so you can select a specific one to bound your maximum per request cost.
* @param subId - The ID of the VRF subscription. Must be funded
* with the minimum subscription balance required for the selected keyHash.
* @param minimumRequestConfirmations - How many blocks you'd like the
* oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
* for why you may want to request more. The acceptable range is
* [minimumRequestBlockConfirmations, 200].
* @param callbackGasLimit - How much gas you'd like to receive in your
* fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords
* 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
* [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.
* @param nativePayment - Whether payment should be made in ETH or LINK.
* @return requestId - A unique identifier of the request. Can be used to match
* a request to a response in fulfillRandomWords.
*/
function requestRandomWords(
bytes32 keyHash,
uint64 subId,
uint16 minimumRequestConfirmations,
uint32 callbackGasLimit,
uint32 numWords,
bool nativePayment
) external returns (uint256 requestId);

/**
* @notice Create a VRF subscription.
* @return subId - A unique subscription id.
* @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
* @dev Note to fund the subscription, use transferAndCall. For example
* @dev LINKTOKEN.transferAndCall(
* @dev address(COORDINATOR),
* @dev amount,
* @dev abi.encode(subId));
*/
function createSubscription() external returns (uint64 subId);

/**
* @notice Get a VRF subscription.
* @param subId - ID of the subscription
* @return balance - LINK balance of the subscription in juels.
* @return ethBalance - ETH balance of the subscription in wei.
* @return owner - owner of the subscription.
* @return consumers - list of consumer address which are able to use this subscription.
*/
function getSubscription(
uint64 subId
) external view returns (uint96 balance, uint96 ethBalance, address owner, address[] memory consumers);

/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @param newOwner - proposed new owner of the subscription
*/
function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external;

/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @dev will revert if original owner of subId has
* not requested that msg.sender become the new owner.
*/
function acceptSubscriptionOwnerTransfer(uint64 subId) external;

/**
* @notice Add a consumer to a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - New consumer which can use the subscription
*/
function addConsumer(uint64 subId, address consumer) external;

/**
* @notice Remove a consumer from a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - Consumer to remove from the subscription
*/
function removeConsumer(uint64 subId, address consumer) external;

/**
* @notice Cancel a subscription
* @param subId - ID of the subscription
* @param to - Where to send the remaining LINK to
*/
function cancelSubscription(uint64 subId, address to) external;

/*
* @notice Check to see if there exists a request commitment consumers
* for all consumers and keyhashes for a given sub.
* @param subId - ID of the subscription
* @return true if there exists at least one unfulfilled request for the subscription, false
* otherwise.
*/
function pendingRequestExists(uint64 subId) external view returns (bool);

/*
* @notice Check to see the payment made for the provided request id.
* @param requestId - ID of the request
* @return amountPaid - amount paid for the request
*/
function s_requestPayments(uint256 requestId) external view returns (uint96);
}
11 changes: 11 additions & 0 deletions contracts/src/v0.8/interfaces/IVRFMigratableConsumerV2Plus.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice The IVRFMigratableConsumerV2Plus interface defines the
/// @notice method required to be implemented by all V2Plus consumers.
/// @dev This interface is designed to be used in VRFConsumerBaseV2Plus.
interface IVRFMigratableConsumerV2Plus {
/// @notice Set the VRF Coordinator address for the consumer.
/// @notice This method is should only be callable by the subscription admin.
function setVRFCoordinator(address vrfCoordinator) external;
}
76 changes: 76 additions & 0 deletions contracts/src/v0.8/interfaces/IVRFSubscriptionV2Plus.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice The IVRFSubscriptionV2Plus interface defines the subscription
/// @notice related methods implemented by the V2Plus coordinator.
interface IVRFSubscriptionV2Plus {
/**
* @notice Add a consumer to a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - New consumer which can use the subscription
*/
function addConsumer(uint64 subId, address consumer) external;

/**
* @notice Remove a consumer from a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - Consumer to remove from the subscription
*/
function removeConsumer(uint64 subId, address consumer) external;

/**
* @notice Cancel a subscription
* @param subId - ID of the subscription
* @param to - Where to send the remaining LINK to
*/
function cancelSubscription(uint64 subId, address to) external;

/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @dev will revert if original owner of subId has
* not requested that msg.sender become the new owner.
*/
function acceptSubscriptionOwnerTransfer(uint64 subId) external;

/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @param newOwner - proposed new owner of the subscription
*/
function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external;

/**
* @notice Create a VRF subscription.
* @return subId - A unique subscription id.
* @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
* @dev Note to fund the subscription with LINK, use transferAndCall. For example
* @dev LINKTOKEN.transferAndCall(
* @dev address(COORDINATOR),
* @dev amount,
* @dev abi.encode(subId));
* @dev Note to fund the subscription with ETH, use fundSubscriptionWithEth. Be sure
* @dev to send ETH with the call, for example:
* @dev COORDINATOR.fundSubscriptionWithEth{value: amount}(subId);
*/
function createSubscription() external returns (uint64 subId);

/**
* @notice Get a VRF subscription.
* @param subId - ID of the subscription
* @return balance - LINK balance of the subscription in juels.
* @return ethBalance - ETH balance of the subscription in wei.
* @return owner - owner of the subscription.
* @return consumers - list of consumer address which are able to use this subscription.
*/
function getSubscription(
uint64 subId
) external view returns (uint96 balance, uint96 ethBalance, address owner, address[] memory consumers);

/**
* @notice Fund a subscription with ETH.
* @param subId - ID of the subscription
* @notice This method expects msg.value to be greater than 0.
*/
function fundSubscriptionWithEth(uint64 subId) external payable;
}
64 changes: 64 additions & 0 deletions contracts/src/v0.8/vrf/BatchVRFCoordinatorV2Plus.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.6;

import "./VRFTypes.sol";

/**
* @title BatchVRFCoordinatorV2Plus
* @notice The BatchVRFCoordinatorV2Plus contract acts as a proxy to write many random responses to the
* @notice provided VRFCoordinatorV2Plus contract efficiently in a single transaction.
*/
contract BatchVRFCoordinatorV2Plus {
IVRFCoordinatorV2Plus public immutable COORDINATOR;

event ErrorReturned(uint256 indexed requestId, string reason);
event RawErrorReturned(uint256 indexed requestId, bytes lowLevelData);

constructor(address coordinatorAddr) {
COORDINATOR = IVRFCoordinatorV2Plus(coordinatorAddr);
}

/**
* @notice fulfills multiple randomness requests with the provided proofs and commitments.
* @param proofs the randomness proofs generated by the VRF provider.
* @param rcs the request commitments corresponding to the randomness proofs.
*/
function fulfillRandomWords(VRFTypes.Proof[] memory proofs, VRFTypes.RequestCommitmentV2Plus[] memory rcs) external {
require(proofs.length == rcs.length, "input array arg lengths mismatch");
for (uint256 i = 0; i < proofs.length; i++) {
try COORDINATOR.fulfillRandomWords(proofs[i], rcs[i]) returns (uint96 /* payment */) {
continue;
} catch Error(string memory reason) {
uint256 requestId = getRequestIdFromProof(proofs[i]);
emit ErrorReturned(requestId, reason);
} catch (bytes memory lowLevelData) {
uint256 requestId = getRequestIdFromProof(proofs[i]);
emit RawErrorReturned(requestId, lowLevelData);
}
}
}

/**
* @notice Returns the proving key hash associated with this public key.
* @param publicKey the key to return the hash of.
*/
function hashOfKey(uint256[2] memory publicKey) internal pure returns (bytes32) {
return keccak256(abi.encode(publicKey));
}

/**
* @notice Returns the request ID of the request associated with the given proof.
* @param proof the VRF proof provided by the VRF oracle.
*/
function getRequestIdFromProof(VRFTypes.Proof memory proof) internal pure returns (uint256) {
bytes32 keyHash = hashOfKey(proof.pk);
return uint256(keccak256(abi.encode(keyHash, proof.seed)));
}
}

interface IVRFCoordinatorV2Plus {
function fulfillRandomWords(
VRFTypes.Proof memory proof,
VRFTypes.RequestCommitmentV2Plus memory rc
) external returns (uint96);
}
Loading

0 comments on commit 7b57ad4

Please sign in to comment.