generated from scaffold-eth/scaffold-eth-2
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b7778c9
commit 5b92d01
Showing
11 changed files
with
406 additions
and
1,012 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.7; | ||
|
||
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; | ||
import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol"; | ||
|
||
/** Simple contract that requests a random number and stores it as a state variable | ||
* | ||
* @dev the arguments for "requestRandomWords()" don't have to be immutable but it saves on gas | ||
*/ | ||
contract VRFConsumer is VRFConsumerBaseV2 { | ||
// State variables | ||
VRFCoordinatorV2Interface private immutable i_vrfCoordinator; | ||
uint64 private immutable i_subscriptionId; | ||
bytes32 private immutable i_keyHash; // gas lane ? | ||
uint32 private immutable i_callbackGasLimit; | ||
uint16 private constant REQUEST_CONFIRMATIONS = 3; // # of blocks to wait before the request can be fulfilled | ||
uint32 private constant NUM_WORDS = 1; // how many random numbers to request | ||
|
||
mapping(uint256 => address) public s_requestIdToSender; // tracks the sender of the request using the requestId | ||
mapping(address => uint256) public s_senderToResult; // stores the resulting random number for each requester | ||
|
||
// Events | ||
event RequestRandomNumber( | ||
uint256 indexed requestId, | ||
address indexed requester | ||
); | ||
event RandomNumberReceived( | ||
uint256 indexed requestId, | ||
uint256 indexed result | ||
); | ||
|
||
constructor( | ||
address vrfCoordinatorV2, | ||
bytes32 keyHash, | ||
uint64 subscriptionId, | ||
uint32 callbackGasLimit | ||
) VRFConsumerBaseV2(vrfCoordinatorV2) { | ||
i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2); | ||
i_keyHash = keyHash; | ||
i_subscriptionId = subscriptionId; | ||
i_callbackGasLimit = callbackGasLimit; | ||
} | ||
|
||
/** This function triggers the request to chainlink node that generates the random number | ||
* @return requestId each request has a unique ID | ||
*/ | ||
function requestRandomNumber() public returns (uint256 requestId) { | ||
// Will revert if subscription is not setup and funded | ||
requestId = i_vrfCoordinator.requestRandomWords( | ||
i_keyHash, | ||
i_subscriptionId, | ||
REQUEST_CONFIRMATIONS, | ||
i_callbackGasLimit, | ||
NUM_WORDS | ||
); | ||
|
||
// keep track of who sent the request | ||
s_requestIdToSender[requestId] = msg.sender; | ||
emit RequestRandomNumber(requestId, msg.sender); | ||
} | ||
|
||
/** Chainlink oracle calls this function to deliver the random number | ||
* @param requestId The ID of the request | ||
* @param randomWords Array containing the random number(s) | ||
* | ||
* @dev use the random number to change state of your contract here | ||
* @dev the random number is huge so you'll want to use modulo to constrain the range | ||
*/ | ||
function fulfillRandomWords( | ||
uint256 requestId, | ||
uint256[] memory randomWords | ||
) internal override { | ||
uint256 randomNumber = (randomWords[0] % 100) + 1; // 1 - 100 | ||
// update mapping to record who received which random number by using the requestId | ||
s_senderToResult[s_requestIdToSender[requestId]] = randomNumber; | ||
emit RandomNumberReceived(requestId, randomNumber); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { HardhatRuntimeEnvironment } from "hardhat/types"; | ||
import { DeployFunction } from "hardhat-deploy/types"; | ||
import { developmentChains, networkConfig } from "../helper-hardhat-config"; | ||
import { network, ethers } from "hardhat"; | ||
|
||
// only for vrfCoordinatorMock deployment | ||
const FUND_AMOUNT = ethers.utils.parseUnits("10", "ether"); | ||
|
||
/** Deploy the "VRFConsumer" contract | ||
* | ||
* @param hre HardhatRuntimeEnvironment object. | ||
*/ | ||
const deployVRFConsumer: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { | ||
const { deployer } = await hre.getNamedAccounts(); | ||
const { deploy, log } = hre.deployments; | ||
const { ethers } = hre; | ||
|
||
const chainId = await hre.ethers.provider.getNetwork().then(network => network.chainId); | ||
console.log(`The current chain ID is: ${chainId}`); | ||
|
||
log("------------------------------------"); | ||
// use values from hepler-hardhat-config.ts | ||
const { vrfCoordinatorV2 } = networkConfig[chainId]; | ||
|
||
let vrfCoordinatorV2Address, subscriptionId; | ||
|
||
if (developmentChains.includes(network.name)) { | ||
const vrfCoordinatorV2Mock = await ethers.getContract("VRFCoordinatorV2Mock"); | ||
vrfCoordinatorV2Address = vrfCoordinatorV2Mock.address; | ||
const tx = await vrfCoordinatorV2Mock.createSubscription(); | ||
const txReceipt = await tx.wait(1); | ||
subscriptionId = txReceipt.events[0].args.subId.toString(); // grab subId from the event logs of .createSubscription() | ||
await vrfCoordinatorV2Mock.fundSubscription(subscriptionId, FUND_AMOUNT); | ||
} else { | ||
vrfCoordinatorV2Address = vrfCoordinatorV2.address; | ||
subscriptionId = vrfCoordinatorV2.subscriptionId; | ||
} | ||
|
||
const { keyHash, callbackGasLimit } = vrfCoordinatorV2; | ||
|
||
const args = [vrfCoordinatorV2Address, keyHash, subscriptionId, callbackGasLimit]; | ||
|
||
const VRFConsumer = await deploy("VRFConsumer", { | ||
from: deployer, | ||
args: args, | ||
log: true, | ||
autoMine: true, | ||
}); | ||
|
||
// For testing on local network, add nft contract as a valid consumer of the VRFCoordinatorV2Mock contract | ||
if (developmentChains.includes(network.name)) { | ||
const vrfCoordinatorV2Mock = await ethers.getContract("VRFCoordinatorV2Mock"); | ||
await vrfCoordinatorV2Mock.addConsumer(subscriptionId, VRFConsumer.address); | ||
} | ||
|
||
// HOW TO VERIFY : https://docs.scaffoldeth.io/deploying/deploy-smart-contracts#4-verify-your-smart-contract | ||
}; | ||
|
||
export default deployVRFConsumer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,44 @@ | ||
interface NetworkConfigEntryTypes { | ||
name?: string; | ||
vrfCoordinatorV2?: string; | ||
subscriptionId?: string; | ||
gasLane?: string; | ||
callbackGasLimit?: string; | ||
interval?: string; | ||
mintFee?: string; | ||
priceFeeds?: { | ||
name: string; | ||
priceFeedAddress?: { | ||
BTC_USD: string; | ||
ETH_USD: string; | ||
LINK_USD: string; | ||
}; | ||
vrfCoordinatorV2: { | ||
address?: string; | ||
subscriptionId?: string; | ||
keyHash?: string; | ||
requestConfirmations?: number; | ||
callbackGasLimit?: string; | ||
numWords?: number; | ||
}; | ||
} | ||
|
||
const networkConfig: { [key: number]: NetworkConfigEntryTypes } = { | ||
31337: { | ||
name: "hardhat", | ||
gasLane: "0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c", | ||
interval: "30", | ||
callbackGasLimit: "500000", // 500,000 gas | ||
mintFee: "100000000000000000", // 0.1 ETH | ||
vrfCoordinatorV2: { | ||
keyHash: "0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c", // doesn't matter for local network | ||
callbackGasLimit: "500000", // doesn't matter for local network | ||
}, | ||
}, | ||
11155111: { | ||
name: "sepolia", | ||
vrfCoordinatorV2: "0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625", | ||
subscriptionId: "4707", // vrf.chain.link/sepolia/4707 | ||
gasLane: "0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c", | ||
callbackGasLimit: "500000", // 500,000 | ||
interval: "30", | ||
mintFee: "100000000000000000", // 0.1 ETH | ||
priceFeeds: { | ||
vrfCoordinatorV2: { | ||
address: "0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625", | ||
keyHash: "0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c", // gas lane ? | ||
subscriptionId: "4707", // replace with your subscription id | ||
callbackGasLimit: "500000", // 500,000 gas | ||
}, | ||
priceFeedAddress: { | ||
BTC_USD: "0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43", | ||
ETH_USD: "0x694AA1769357215DE4FAC081bf1f309aDC325306", | ||
LINK_USD: "0xc59E3633BAAC79493d908e63626716e204A45EdF", | ||
}, | ||
}, | ||
}; | ||
|
||
const developmentChains: string[] = ["hardhat", "localhost"]; | ||
const developmentChains: string[] = ["hardhat", "foundry", "localhost"]; | ||
|
||
export { networkConfig, developmentChains }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { InformationCircleIcon } from "@heroicons/react/24/outline"; | ||
import { useScaffoldContractWrite } from "~~/hooks/scaffold-eth/useScaffoldContractWrite"; | ||
|
||
// import { useScaffoldEventHistory } from "~~/hooks/scaffold-eth/useScaffoldEventHistory"; | ||
|
||
/** | ||
* TODO: Figure out how to show result using eventSubscriber (gotta handle state!) | ||
* - you gotta combine eventHistory with eventSubscriber to make it work right! | ||
*/ | ||
export const ActionPanel = () => { | ||
// useScaffoldEventSubscriber({ | ||
// contractName: "YourContract", | ||
// eventName: "GreetingChange", | ||
// // The listener function is called whenever a GreetingChange event is emitted by the contract. | ||
// // Parameters emitted by the event can be destructed using the below example | ||
// // for this example: event GreetingChange(address greetingSetter, string newGreeting, bool premium, uint256 value); | ||
// listener: (logs) => { | ||
// logs.map((log) => { | ||
// const { greetingSetter, value, premium, newGreeting } = log.args; | ||
// console.log( | ||
// "📡 GreetingChange event", | ||
// greetingSetter, | ||
// value, | ||
// premium, | ||
// newGreeting | ||
// ); | ||
// }); | ||
// }, | ||
// }); | ||
|
||
// const { data: events } = useScaffoldEventHistory({ | ||
// contractName: "VRFConsumer", | ||
// eventName: "RandomNumberReceived", | ||
// fromBlock: 4485346n, | ||
// }); | ||
|
||
const { writeAsync: triggerRequest } = useScaffoldContractWrite({ | ||
contractName: "VRFConsumer", | ||
functionName: "requestRandomNumber", | ||
blockConfirmations: 1, | ||
onBlockConfirmation: txnReceipt => { | ||
console.log("Transaction blockHash", txnReceipt.blockHash); | ||
}, | ||
}); | ||
|
||
// console.log("events", events); | ||
|
||
return ( | ||
<div className="bg-base-100 rounded-xl p-10 shadow-lg"> | ||
<div className="flex justify-center items-center mb-10 gap-2"> | ||
<h3 className="text-2xl md:text-3xl text-center font-bold">VRFConsumer</h3> | ||
<div className="tooltip tooltip-accent" data-tip={`put link to contract here`}> | ||
<button> | ||
<InformationCircleIcon className="h-7 w-7" /> | ||
</button> | ||
</div> | ||
</div> | ||
|
||
<div className="flex justify-center"> | ||
<button className="btn btn-accent text-base-200 text-lg" onClick={() => triggerRequest()}> | ||
Request Random | ||
</button> | ||
</div> | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.