From 364e885bd0cf88a9e1eeb44dd352044a14c5b808 Mon Sep 17 00:00:00 2001 From: franfran20 Date: Tue, 5 Jul 2022 22:11:42 +0100 Subject: [PATCH 1/4] Add new section for explanation and examples of using chainlinkVRF to handle different function requests logic Author: franfran20 --- docs/vrf/v2/best-practices.md | 47 +++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/docs/vrf/v2/best-practices.md b/docs/vrf/v2/best-practices.md index 3dfbadc2612..37e5f6faa5c 100644 --- a/docs/vrf/v2/best-practices.md +++ b/docs/vrf/v2/best-practices.md @@ -96,3 +96,50 @@ function fulfillRandomWords( s_requestIndexToRandomWords[requestNumber] = randomWords; } ``` + +## Processing Different Functions VRF Requests + +If you want to have multiple VRF requests in different functions, create a mapping between `requestId` and the `functionId`. This `functionId` could be a uint256 of your choice or the hash of the function name like `keccak256("FunctionName")`. This way, you can have different functions making requests and different logic for each request. When handling different logic in your `fulfillRandomWords` function, keep in mind the your `callbackGasLimit` and appropriate `keyHash` set. + +```solidity +uint256 public variableA; +uint256 public variableB; +bytes32 public UPDATE_VARIABLE_A_ID = keccak256("updateVariableA"); +bytes32 public UPDATE_VARIABLE_B_ID = keccak256("updateVariableB"); +mapping(uint256 => bytes32) public requestIdToFunctionId; + +function updateVariableA() public { + uint256 requestId = COORDINATOR.requestRandomWords( + keyHash, + s_subscriptionId, + requestConfirmations, + callBackGasLimit, + numWords + ); + requestIdToFunctionId[requestId] = UPDATE_VARIABLE_A_ID; +} + +function updateVariableB() public { + uint256 requestId = COORDINATOR.requestRandomWords( + keyHash, + s_subscriptionId, + requestConfirmations, + callBackGasLimit, + numWords + ); + requestIdToFunctionId[requestId] = UPDATE_VARIABLE_B_ID; +} + +function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) + internal + override + { + if (requestIdToFunctionId[requestId] == UPDATE_VARIABLE_A_ID) { + variableA = randomWords[0]; + } + if (requestIdToFunctionId[requestId] == UPDATE_VARIABLE_B_ID) { + variableB = randomWords[0]; + } +} + +``` From d443ba3bac10ba4f12d96a373f290ffb982f0667 Mon Sep 17 00:00:00 2001 From: aelmanaa Date: Thu, 25 Aug 2022 10:36:12 +0200 Subject: [PATCH 2/4] Processing VRF responses through different execution paths --- docs/vrf/v2/best-practices.md | 45 +++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/docs/vrf/v2/best-practices.md b/docs/vrf/v2/best-practices.md index 37e5f6faa5c..66affc0a1fb 100644 --- a/docs/vrf/v2/best-practices.md +++ b/docs/vrf/v2/best-practices.md @@ -97,16 +97,18 @@ function fulfillRandomWords( } ``` -## Processing Different Functions VRF Requests +## Processing VRF responses through different execution paths -If you want to have multiple VRF requests in different functions, create a mapping between `requestId` and the `functionId`. This `functionId` could be a uint256 of your choice or the hash of the function name like `keccak256("FunctionName")`. This way, you can have different functions making requests and different logic for each request. When handling different logic in your `fulfillRandomWords` function, keep in mind the your `callbackGasLimit` and appropriate `keyHash` set. +If you want to process VRF responses depending on predetermined conditions, you can create an `enum`. When requesting for randomness, map each `requestId` to an enum. This way, you can handle different execution paths in `fulfillRandomWords`. ```solidity +enum Logic { A, B, C} + uint256 public variableA; uint256 public variableB; -bytes32 public UPDATE_VARIABLE_A_ID = keccak256("updateVariableA"); -bytes32 public UPDATE_VARIABLE_B_ID = keccak256("updateVariableB"); -mapping(uint256 => bytes32) public requestIdToFunctionId; +uint256 public variableC; + +mapping(uint256 => Logic) public requests; function updateVariableA() public { uint256 requestId = COORDINATOR.requestRandomWords( @@ -114,9 +116,9 @@ function updateVariableA() public { s_subscriptionId, requestConfirmations, callBackGasLimit, - numWords + 1 //numWords ); - requestIdToFunctionId[requestId] = UPDATE_VARIABLE_A_ID; + requests[requestId] = Logic.A; } function updateVariableB() public { @@ -125,21 +127,34 @@ function updateVariableB() public { s_subscriptionId, requestConfirmations, callBackGasLimit, - numWords + 1 //numWords + ); + requests[requestId] = Logic.B; +} + +function updateVariableC() public { + uint256 requestId = COORDINATOR.requestRandomWords( + keyHash, + s_subscriptionId, + requestConfirmations, + callBackGasLimit, + 1 //numWords ); - requestIdToFunctionId[requestId] = UPDATE_VARIABLE_B_ID; + requests[requestId] = Logic.C; } function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { - if (requestIdToFunctionId[requestId] == UPDATE_VARIABLE_A_ID) { - variableA = randomWords[0]; - } - if (requestIdToFunctionId[requestId] == UPDATE_VARIABLE_B_ID) { - variableB = randomWords[0]; - } + Logic logic = requests[requestId]; + if(logic == Logic.A){ + variableA = randomWords[0]; + }else if(logic == Logic.B){ + variableB = randomWords[0]; + }else if(logic == Logic.C){ + variableC = randomWords[0]; + } } ``` From 18b4d6dcbaabf5e5d26e079ce0b28a88fa9d3434 Mon Sep 17 00:00:00 2001 From: aelmanaa Date: Thu, 25 Aug 2022 19:54:26 +0200 Subject: [PATCH 3/4] Display better different execution paths --- _includes/samples/VRF/VRFv2MultiplePaths.sol | 107 +++++++++++++++++++ docs/vrf/v2/best-practices.md | 58 +--------- 2 files changed, 109 insertions(+), 56 deletions(-) create mode 100644 _includes/samples/VRF/VRFv2MultiplePaths.sol diff --git a/_includes/samples/VRF/VRFv2MultiplePaths.sol b/_includes/samples/VRF/VRFv2MultiplePaths.sol new file mode 100644 index 00000000000..991f12bd6c5 --- /dev/null +++ b/_includes/samples/VRF/VRFv2MultiplePaths.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +// An example of a consumer contract that relies on a subscription for funding. +// It shows how to setup multiple execution paths for handling a response. +pragma solidity ^0.8.7; + +import '@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol'; +import '@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol'; + +/** + * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. + * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. + * DO NOT USE THIS CODE IN PRODUCTION. + */ + +contract VRFv2MultiplePaths is VRFConsumerBaseV2 { + VRFCoordinatorV2Interface COORDINATOR; + + // Your subscription ID. + uint64 s_subscriptionId; + + // Goerli coordinator. For other networks, + // see https://docs.chain.link/docs/vrf/v2/supported-networks/#configurations + address vrfCoordinator = 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D; + + // The gas lane to use, which specifies the maximum gas price to bump to. + // For a list of available gas lanes on each network, + // see https://docs.chain.link/docs/vrf/v2/supported-networks/#configurations + bytes32 keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15; + + uint32 callbackGasLimit = 100_000; + + // The default is 3, but you can set this higher. + uint16 requestConfirmations = 3; + + // For this example, retrieve 1 random value in one request. + // Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS. + uint32 numWords = 1; + + enum Variable { + A, + B, + C + } + + uint256 public variableA; + uint256 public variableB; + uint256 public variableC; + + mapping(uint256 => Variable) public requests; + + // events + event FulfilledA(uint256 requestId, uint256 value); + event FulfilledB(uint256 requestId, uint256 value); + event FulfilledC(uint256 requestId, uint256 value); + + constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + s_subscriptionId = subscriptionId; + } + + function updateVariable(uint256 input) public { + uint256 requestId = COORDINATOR.requestRandomWords( + keyHash, + s_subscriptionId, + requestConfirmations, + callbackGasLimit, + numWords + ); + + if (input % 2 == 0) { + requests[requestId] = Variable.A; + } else if (input % 3 == 0) { + requests[requestId] = Variable.B; + } else { + requests[requestId] = Variable.C; + } + } + + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { + Variable variable = requests[requestId]; + if (variable == Variable.A) { + fulfillA(requestId, randomWords[0]); + } else if (variable == Variable.B) { + fulfillB(requestId, randomWords[0]); + } else if (variable == Variable.C) { + fulfillC(requestId, randomWords[0]); + } + } + + function fulfillA(uint256 requestId, uint256 randomWord) private { + // execution path A + variableA = randomWord; + emit FulfilledA(requestId, randomWord); + } + + function fulfillB(uint256 requestId, uint256 randomWord) private { + // execution path B + variableB = randomWord; + emit FulfilledB(requestId, randomWord); + } + + function fulfillC(uint256 requestId, uint256 randomWord) private { + // execution path C + variableC = randomWord; + emit FulfilledC(requestId, randomWord); + } +} diff --git a/docs/vrf/v2/best-practices.md b/docs/vrf/v2/best-practices.md index 66affc0a1fb..eeefb1ca3f4 100644 --- a/docs/vrf/v2/best-practices.md +++ b/docs/vrf/v2/best-practices.md @@ -99,62 +99,8 @@ function fulfillRandomWords( ## Processing VRF responses through different execution paths -If you want to process VRF responses depending on predetermined conditions, you can create an `enum`. When requesting for randomness, map each `requestId` to an enum. This way, you can handle different execution paths in `fulfillRandomWords`. +If you want to process VRF responses depending on predetermined conditions, you can create an `enum`. When requesting for randomness, map each `requestId` to an enum. This way, you can handle different execution paths in `fulfillRandomWords`. See the following example: ```solidity -enum Logic { A, B, C} - -uint256 public variableA; -uint256 public variableB; -uint256 public variableC; - -mapping(uint256 => Logic) public requests; - -function updateVariableA() public { - uint256 requestId = COORDINATOR.requestRandomWords( - keyHash, - s_subscriptionId, - requestConfirmations, - callBackGasLimit, - 1 //numWords - ); - requests[requestId] = Logic.A; -} - -function updateVariableB() public { - uint256 requestId = COORDINATOR.requestRandomWords( - keyHash, - s_subscriptionId, - requestConfirmations, - callBackGasLimit, - 1 //numWords - ); - requests[requestId] = Logic.B; -} - -function updateVariableC() public { - uint256 requestId = COORDINATOR.requestRandomWords( - keyHash, - s_subscriptionId, - requestConfirmations, - callBackGasLimit, - 1 //numWords - ); - requests[requestId] = Logic.C; -} - -function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) - internal - override - { - Logic logic = requests[requestId]; - if(logic == Logic.A){ - variableA = randomWords[0]; - }else if(logic == Logic.B){ - variableB = randomWords[0]; - }else if(logic == Logic.C){ - variableC = randomWords[0]; - } -} - +{% include 'samples/VRF/VRFv2MultiplePaths.sol' %} ``` From b28010f6790cbc315d4802243eb0e11d23885ba6 Mon Sep 17 00:00:00 2001 From: aelmanaa Date: Thu, 25 Aug 2022 20:00:52 +0200 Subject: [PATCH 4/4] add remix callout --- docs/vrf/v2/best-practices.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/vrf/v2/best-practices.md b/docs/vrf/v2/best-practices.md index eeefb1ca3f4..f80d9fd58bb 100644 --- a/docs/vrf/v2/best-practices.md +++ b/docs/vrf/v2/best-practices.md @@ -104,3 +104,8 @@ If you want to process VRF responses depending on predetermined conditions, you ```solidity {% include 'samples/VRF/VRFv2MultiplePaths.sol' %} ``` + +