Lesson 14: reverted with custom error 'InvalidConsumer()' #1375
-
Hello, > Error: VM Exception while processing transaction: reverted with custom error 'InvalidConsumer()'
> at VRFCoordinatorV2Mock.onlyValidConsumer (@chainlink/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol:72)
> at VRFCoordinatorV2Mock.requestRandomWords (@chainlink/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol:147)
> at RandomIpfsNft.requestNft (contracts/RandomIpfsNft.sol:72)
> at HardhatNode._mineBlockWithPendingTxs (/home/ibrahim/hh-fcc/hardhat-nft-fcc/node_modules/hardhat/src/internal/hardhat-network/provider/node.ts:1773:23)
> at HardhatNode.mineBlock (/home/ibrahim/hh-fcc/hardhat-nft-fcc/node_modules/hardhat/src/internal/hardhat-network/provider/node.ts:466:16)
> at EthModule._sendTransactionAndReturnHash (/home/ibrahim/hh-fcc/hardhat-nft-fcc/node_modules/hardhat/src/internal/hardhat-network/provider/modules/eth.ts:1504:18)
> at HardhatNetworkProvider.request (/home/ibrahim/hh-fcc/hardhat-nft-fcc/node_modules/hardhat/src/internal/hardhat-network/provider/provider.ts:118:18)
> at EthersProviderWrapper.send (/home/ibrahim/hh-fcc/hardhat-nft-fcc/node_modules/@nomiclabs/hardhat-ethers/src/internal/ethers-provider-wrapper.ts:13:20) I am attaching Solidity and test file below // SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
error RandomIpfsNft__RangeOutOfBounds();
error RandomIpfsNft__NeedMoreETHSent();
error RandomIpfsNft__TransferFailed();
contract RandomIpfsNft is VRFConsumerBaseV2, ERC721URIStorage, Ownable {
// When we mint an NFT, we will trigger a Chainlink VRF call to get us a random number
// using that number, we will get a random NFT
// Pug, Shiba Inu, St. Bernard
// Pug <- Super Rare
// Shiba Inu <- Sort of Rare
// St. Bernard <- Common
// Users have to pay to mint an NFT
// The owner of the contract can withdraw the ETH
// Type Declaration
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;
// VRF Helpers
mapping(uint256 => address) public s_requestIdToSender;
// NFT Variables
uint256 public s_tokenCounter;
uint256 internal constant MAX_CHANCE_VALUE = 100;
string[] internal s_dogTokeUris;
uint256 internal immutable i_mintFee;
// Events
event NftRequested(uint256 indexed requestId, address requester);
event NftMinted(Breed DogBreed, address minter);
constructor(
address vrfCoordinatorV2,
uint64 subscriptionId,
bytes32 gasLane,
uint32 callbackGasLimit,
string[3] memory dogTokenUris,
uint256 mintFee
) VRFConsumerBaseV2(vrfCoordinatorV2) ERC721("Random IPFS NFT", "RIN") {
i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
i_subscriptionId = subscriptionId;
i_gasLane = gasLane;
i_callbackGasLimit = callbackGasLimit;
s_dogTokeUris = dogTokenUris;
i_mintFee = mintFee;
}
function requestNft() public payable returns (uint256 requestId) {
if (msg.value < i_mintFee) {
revert RandomIpfsNft__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 newTokenId = s_tokenCounter;
// What does this token look like?
uint256 moddedRng = randomWords[0] % MAX_CHANCE_VALUE;
// Range of Random Numbers : 0 - 99
// Eg. 7 -> PUG
// 12 -> Shiba Inu
// 88 -> St. Bernard
Breed dogBreed = getBreedFromModdedRng(moddedRng);
s_tokenCounter += s_tokenCounter;
_safeMint(dogOwner, newTokenId);
_setTokenURI(newTokenId, s_dogTokeUris[uint256(dogBreed)]);
emit NftMinted(dogBreed, dogOwner);
}
function withdraw() public onlyOwner {
uint256 amount = address(this).balance;
(bool success, ) = payable(msg.sender).call{value: amount}("");
if (!success) {
revert RandomIpfsNft__TransferFailed();
}
}
function getBreedFromModdedRng(uint256 moddedRng) public view returns (Breed) {
uint256 cumulativeSum = 0;
uint256[3] memory chanceArray = getChanceArray();
// moddedRng = 25
// i = 0
// cumulativeSum = 0
for (uint256 i = 0; i < chanceArray.length; i++) {
if (moddedRng >= cumulativeSum && moddedRng < cumulativeSum + chanceArray[i]) {
return Breed(i);
}
cumulativeSum += chanceArray[i];
}
revert RandomIpfsNft__RangeOutOfBounds();
}
function getChanceArray() public pure returns (uint256[3] memory) {
return [10, 30, MAX_CHANCE_VALUE];
}
function getMintFee() public view returns (uint256) {
return i_mintFee;
}
function getDogTokenUris(uint256 index) public view returns (string memory) {
return s_dogTokeUris[index];
}
function getTokenCounter() public view returns (uint256) {
return s_tokenCounter;
}
}
Test.js file below: const { assert, expect } = require("chai")
const { network, deployments, ethers } = require("hardhat")
const { developmentChains, networkConfig } = require("../../helper-hardhat-config")
!developmentChains.includes(network.name)
? describe.skip
: describe("Random IPFS NFT Unit Tests", async function () {
let randomIpfsNft, deployer, vrfCoordinatorV2Mock
beforeEach(async () => {
accounts = await ethers.getSigners()
deployer = accounts[0]
await deployments.fixture(["mocks", "randomipfs"])
randomIpfsNft = await ethers.getContract("RandomIpfsNft")
vrfCoordinatorV2Mock = await ethers.getContract("VRFCoordinatorV2Mock")
})
describe("constructor", () => {
it("sets starting values correctly", async function () {
const dogTokenUriZero = await randomIpfsNft.getDogTokenUris(0)
assert(dogTokenUriZero.includes("ipfs://"))
})
})
describe("requestNft", () => {
it("fails if payment isn't sent with the request", async function () {
await expect(randomIpfsNft.requestNft()).to.be.revertedWith(
"RandomIpfsNft__NeedMoreETHSent"
)
})
it("emits an event and kicks off a random word request", async function () {
const fee = await randomIpfsNft.getMintFee()
await expect(randomIpfsNft.requestNft({ value: fee.toString() })).to.emit(
randomIpfsNft,
"NftRequested"
)
})
})
}) |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 25 replies
-
@IbrahimGhasia Its look like the issue from VRF side, please make sure you have added correct subscription id in helper file |
Beta Was this translation helpful? Give feedback.
-
Hey, IbrahimGhasia In a nearby discussion there is a solution to the problem.
|
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Hello I am facing the same issue:
But I have verified that consumer is registered before the Nft is minted . See lines in log
and
The contract is added as consumer But I am still getting the same error. Here's const {network,ethers} = require('hardhat')
const { developmentChains, networkConfig, DECIMALS } = require('../helper-config')
const { uploadImages, buildIPFSMetaDataJson, storeMetadataToIPFS } = require('../utils/pinata-helper')
const verify = require('../utils/verify')
const imagesDirPath = './images/random-nfts'
const tokenUris = [
...
]
const FUND_AMOUNT ="1000000000000000000000"
module.exports = async function({getNamedAccounts,deployments}){
const {deployer} = await getNamedAccounts()
const {deploy,log} = deployments
const {chainId} = network.config
let subscriptionId,vrfCoordinatorV2Address,vrfCoordinatorV2Mock
if(process.env.PINATA_ENABLED=="true"){
let tokenUris = await handleTokenUris()
console.log(tokenUris);
}
if(developmentChains.includes(network.name)){
//create a subscription
vrfCoordinatorV2Mock = await ethers.getContract("VRFCoordinatorV2Mock",deployer)
vrfCoordinatorV2Address = vrfCoordinatorV2Mock.address
const tx = await vrfCoordinatorV2Mock.createSubscription()
const txReceipt = await tx.wait(1)
subscriptionId = txReceipt.events[0].args.subId
let txn = await vrfCoordinatorV2Mock.fundSubscription(subscriptionId,FUND_AMOUNT)
await txn.wait(1)
}else{
subscriptionId = networkConfig[chainId].subscriptionId
vrfCoordinatorV2Address = networkConfig[chainId].vrfCoordinatorV2Address
}
const args = [vrfCoordinatorV2Address,networkConfig[chainId].gasLane,networkConfig[chainId].subscriptionId,networkConfig[chainId].callbackGasLimit,tokenUris, networkConfig[chainId].mintFee]
log("Deploying Contact RandomIpfsNft .. ",args)
const RandomIpfsNftContract = await deploy("RandomIpfsNft",{
from:deployer,
log:true,
args,
waitConfirmations:network.config.blockConfirmations || 1
})
log(" --- Contract Deployed --",RandomIpfsNftContract.address);
if(vrfCoordinatorV2Mock){
await vrfCoordinatorV2Mock.addConsumer(subscriptionId, RandomIpfsNftContract.address)
log("Added Consumer to mock ",RandomIpfsNftContract.address)
}
if(!developmentChains.includes(network.name)){
await verify(RandomIpfsNftContract.address,args)
}
}
async function handleTokenUris(){
let tokenUris=[]
let {ipfsUris,files} = await uploadImages(imagesDirPath)
console.log(ipfsUris);
for( idx in files){
let metadata = buildIPFSMetaDataJson(files[idx].replace('.png',''),`Nice ${files[idx].replace('.png','')}`,ipfsUris[idx],Math.floor(Math.random() * 100))
let IPFSHash = await storeMetadataToIPFS(metadata)
console.log("Uploaded NFTIMAGE ",IPFSHash);
tokenUris.push(`ipfs://${IPFSHash}`)
}
return tokenUris
}
module.exports.tags = ["all","randomipfs","main"] Here's // SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@chainlink/contracts/src/v0.8/VRFCoordinatorV2.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
error RandomIpfsNft__NeedMoreETHSent();
error RandomIpfsNft__RangeOutOfBounds();
error RandomIpfsNft__TransferFailed();
contract RandomIpfsNft is ERC721URIStorage, VRFConsumerBaseV2, Ownable{
enum VisionToken {
HEALTHCARE,
FINANCE,
GOVERN
}
event NftRequested(uint256 indexed requestId, address requester);
event NftMinted(VisionToken breed, address minter);
uint256 private s_tokenCounter;
VRFCoordinatorV2Interface private immutable i_vrfCoordinator;
bytes32 private immutable i_gasLane;
uint64 private immutable i_subId;
uint32 private immutable i_callbackGasLimit;
uint private immutable i_mintFee;
uint8 private constant REQUEST_CONFIRMATIONS = 3;
uint8 private constant NUM_WORDS = 1;
uint8 private constant MAX_CHANCE = 100;
mapping (uint => address) s_requestIdToOwner;
string[3] internal s_visionTokenUris;
constructor(address vrfCoordinatorAdr,bytes32 gasLane,uint64 subId,uint32 callbackGasLimit,string[3] memory tokenUris,uint mintFee) ERC721("RandomVision","RV") VRFConsumerBaseV2(vrfCoordinatorAdr) {
i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorAdr);
i_gasLane = gasLane;
i_subId = subId;
i_callbackGasLimit = callbackGasLimit;
i_mintFee = mintFee;
s_tokenCounter = 0;
s_visionTokenUris = tokenUris;
}
function mintNft()public payable returns (uint256 requestId){
if (msg.value < i_mintFee) {
revert RandomIpfsNft__NeedMoreETHSent();
}
requestId = i_vrfCoordinator.requestRandomWords(
i_gasLane,
i_subId,
REQUEST_CONFIRMATIONS,
i_callbackGasLimit,
NUM_WORDS
);
s_requestIdToOwner[requestId] = msg.sender;
emit NftRequested(requestId,msg.sender);
}
function fulfillRandomWords(uint256 requestId,uint[] memory randomWords) internal override {
uint randomWord = randomWords[0] % MAX_CHANCE;
uint8[3] memory chanceArray = getChanceArray();
VisionToken tokenType;
address ownerAdr = s_requestIdToOwner[requestId];
for(uint8 i=0;i< chanceArray.length;i++){
if(randomWord <= chanceArray[i]){
tokenType = VisionToken(i);
}
}
_safeMint(ownerAdr,s_tokenCounter);
_setTokenURI(s_tokenCounter,s_visionTokenUris[uint256(tokenType)]);
s_tokenCounter = s_tokenCounter +1;
emit NftMinted(tokenType,ownerAdr);
}
function withdraw() public onlyOwner{
uint256 amount = address(this).balance;
(bool success,) = payable(msg.sender).call{value:amount}("");
if(!success) {
revert RandomIpfsNft__TransferFailed();
}
}
function getChanceArray() private view returns (uint8[3] memory) {
return [20,50,MAX_CHANCE];
}
function getMintFee() public view returns (uint256) {
return i_mintFee;
}
function getVisionTokenUris(uint256 index) public view returns (string memory) {
return s_visionTokenUris[index];
}
function getTokenCounter() public view returns (uint256) {
return s_tokenCounter;
}
}
And const { ethers, network } = require("hardhat")
const { networkConfig } = require("../helper-config")
module.exports = async({getNamedAccounts})=>{
const {deployer} = await getNamedAccounts()
let simpleNft = await ethers.getContract("SimpleNft",deployer)
let txns = await simpleNft.mintNft()
await txns.wait(1)
console.log(`Basic NFT index 0 tokenURI: ${await simpleNft.tokenURI(0)}`)
const highValue = ethers.utils.parseEther("4000")
let dynamicSvgNft = await ethers.getContract("DynamicSvgNft",deployer)
let txnd = await dynamicSvgNft.mintNft(highValue)
await txnd.wait(1)
console.log(`dynamicSvgNft NFT index 0 tokenURI: ${await dynamicSvgNft.tokenURI(0)}`)
const randomIpfsNft = await ethers.getContract("RandomIpfsNft", deployer)
const mintFee = await randomIpfsNft.getMintFee()
console.log(`Got Min Fee ${mintFee}`);
console.log("Is this consumer address registered",randomIpfsNft.address);
const randomIpfsNftMintTx = await randomIpfsNft.mintNft({ value: mintFee.toString() })
const randomIpfsNftMintTxReceipt = await randomIpfsNftMintTx.wait(1)
console.log(`randomIpfsNft NFT index 0 tokenURI: ${await randomIpfsNft.tokenURI(0)}`)
await new Promise(async (resolve, reject) => {
setTimeout(() => reject("Timeout: 'NFTMinted' event did not fire"), 300000) // 5 minute timeout time
randomIpfsNft.once("NftMinted", async () => {
console.log(`Random IPFS NFT index 0 tokenURI: ${await randomIpfsNft.tokenURI(0)}`)
resolve()
})
if (chainId == 31337) {
const requestId = randomIpfsNftMintTxReceipt.events[1].args.requestId.toString()
const vrfCoordinatorV2Mock = await ethers.getContract("VRFCoordinatorV2Mock", deployer)
await vrfCoordinatorV2Mock.fulfillRandomWords(requestId, randomIpfsNft.address)
}
})
}
module.exports.tags= ["all","mint"] |
Beta Was this translation helpful? Give feedback.
You should add this line in your deploy script (02-deploy-random-ipfs-nft.js) after you deploy both vrfCoordinatorV2Mock and randomIpfsNft:
In code it will loke like this:
Its because you created subscription but you didn't add a consumers for this subscription, so VRF revert you with this message. VRF add this modifier to function requestRandomWords :