Lesson 14 random ipfs test question #1732
-
If anyone could help me with this section of the unit test for random ipfs nft, i'd be very grateful. Why are we using two different try catches in this instance? Also in the second try catch where we call the fRWs on the mock contract is confusing to me as well, why are we retrieving the request Id and contract address in specific? The second try catch is confusing to me in general really, I dont understand why we are calling requestNft at all? describe("fulfillRandomWords", () => {
it("mints NFT after random number is returned", async function () {
await new Promise(async (resolve, reject) => {
randomIpfsNft.once("NftMinted", async () => {
try {
const tokenUri = await randomIpfsNft.tokenURI("0")
const tokenCounter = await randomIpfsNft.getTokenCounter()
assert.equal(tokenUri.toString().includes("ipfs://"), true)
assert.equal(tokenCounter.toString(), "1")
resolve()
} catch (e) {
console.log(e)
reject(e)
}
})
try {
const fee = await randomIpfsNft.getMintFee()
const requestNftResponse = await randomIpfsNft.requestNft({
value: fee.toString(),
})
const requestNftReceipt = await requestNftResponse.wait(1)
await vrfCoordinatorV2Mock.fulfillRandomWords(
requestNftReceipt.events[1].args.requestId,
randomIpfsNft.address
)
} catch (e) {
console.log(e)
reject(e)
}
})
})
}) The contract: // SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "hardhat/console.sol";
error AlreadyInitialized();
error NeedMoreETHSent();
error RangeOutOfBounds();
contract RandomIpfsNft is ERC721URIStorage, VRFConsumerBaseV2, Ownable {
// Types
enum Breed {
PUG,
SHIBA_INU,
ST_BERNARD
}
// Chainlink VRF Variables
VRFCoordinatorV2Interface private immutable i_vrfCoordinator;
uint64 private immutable i_subscriptionId;
bytes32 private immutable i_gasLane;
uint32 private immutable i_callbackGasLimit;
uint16 private constant REQUEST_CONFIRMATIONS = 3;
uint32 private constant NUM_WORDS = 1;
// NFT Variables
uint256 private i_mintFee;
uint256 public s_tokenCounter;
mapping(uint256 => Breed) private s_tokenIdToBreed;
uint256 internal constant MAX_CHANCE_VALUE = 100;
string[] internal s_dogTokenUris;
bool private s_initialized;
// VRF Helpers
mapping(uint256 => address) public s_requestIdToSender;
// Events
event NftRequested(uint256 indexed requestId, address requester);
event NftMinted(Breed breed, address minter);
constructor(
address vrfCoordinatorV2,
uint64 subscriptionId,
bytes32 gasLane, // keyHash
uint256 mintFee,
uint32 callbackGasLimit,
string[3] memory dogTokenUris
) VRFConsumerBaseV2(vrfCoordinatorV2) ERC721("Random IPFS NFT", "RIN") {
i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
i_gasLane = gasLane;
i_subscriptionId = subscriptionId;
i_mintFee = mintFee;
i_callbackGasLimit = callbackGasLimit;
_initializeContract(dogTokenUris);
}
function requestNft() public payable returns (uint256 requestId) {
if (msg.value < i_mintFee) {
revert NeedMoreETHSent();
}
requestId = i_vrfCoordinator.requestRandomWords(
i_gasLane,
i_subscriptionId,
REQUEST_CONFIRMATIONS,
i_callbackGasLimit,
NUM_WORDS
);
s_requestIdToSender[requestId] = msg.sender;
emit NftRequested(requestId, msg.sender);
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
address dogOwner = s_requestIdToSender[requestId];
uint256 newItemId = s_tokenCounter;
s_tokenCounter = s_tokenCounter + 1;
uint256 moddedRng = randomWords[0] % MAX_CHANCE_VALUE;
Breed dogBreed = getBreedFromModdedRng(moddedRng);
_safeMint(dogOwner, newItemId);
_setTokenURI(newItemId, s_dogTokenUris[uint256(dogBreed)]);
emit NftMinted(dogBreed, dogOwner);
}
function getChanceArray() public pure returns (uint256[3] memory) {
return [10, 30, MAX_CHANCE_VALUE];
}
function _initializeContract(string[3] memory dogTokenUris) private {
if (s_initialized) {
revert AlreadyInitialized();
}
s_dogTokenUris = dogTokenUris;
s_initialized = true;
}
function getBreedFromModdedRng(uint256 moddedRng) public pure returns (Breed) {
uint256 cumulativeSum = 0;
uint256[3] memory chanceArray = getChanceArray();
for (uint256 i = 0; i < chanceArray.length; i++) {
// if (moddedRng >= cumulativeSum && moddedRng < cumulativeSum + chanceArray[i]) {
if (moddedRng >= cumulativeSum && moddedRng < chanceArray[i]) {
return Breed(i);
}
// cumulativeSum = cumulativeSum + chanceArray[i];
cumulativeSum = chanceArray[i];
}
revert RangeOutOfBounds();
}
function withdraw() public onlyOwner {
uint256 amount = address(this).balance;
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "Transfer failed");
}
function getMintFee() public view returns (uint256) {
return i_mintFee;
}
function getDogTokenUris(uint256 index) public view returns (string memory) {
return s_dogTokenUris[index];
}
function getInitialized() public view returns (bool) {
return s_initialized;
}
function getTokenCounter() public view returns (uint256) {
return s_tokenCounter;
}
} Any help appreciated :) |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
@jacobhay2626 try-catch we use to handle any issues that occur during runtime. In this test we have two sections: One lower part that executes first will call the requestNFT function and then fulfulRandomWords. If any issue occurs here then it will reject the promise the console log the error. And your question we are requesting id is because that we have to pass to fulfullRandomWords function, here because we are running a unit test and we are pretending to be a Chainlink node that's why we are calling it but for the real scenario, Chainlink Nodes will call this function and we do not need to care about ids and all that stuff. The second try-catch above it will execute after successfully requesting and fulfilling the request of the random number. This part guarantees that our request hit successfully now we can match the results. Here we use try-catch in case we call any function and that will not be available inside the contract that can throw an error so we can get that error and know the reason. And similar issues can occur. |
Beta Was this translation helpful? Give feedback.
@jacobhay2626 try-catch we use to handle any issues that occur during runtime. In this test we have two sections:
One lower part that executes first will call the requestNFT function and then fulfulRandomWords. If any issue occurs here then it will reject the promise the console log the error. And your question we are requesting id is because that we have to pass to fulfullRandomWords function, here because we are running a unit test and we are pretending to be a Chainlink node that's why we are calling it but for the real scenario, Chainlink Nodes will call this function and we do not need to care about ids and all that stuff.
The second try-catch above it will execute after successfully…