0

While trying to test the script it is giving me the following error:

Error: VM Exception while processing transaction: reverted with custom error 'InvalidConsumer()'

Here is the GitHub repo of my project for reference: https://github.com/Jimit1512/Nft.git

Here is the testing code:

        beforeEach(async () => {
              accounts = await ethers.getSigners();
              deployer = accounts[0];
              await deployments.fixture(["mocks","randomIpfs"]);
              const randomIpfsNftDeployer = await deployments.get("RandomIpfsNft");
              randomIpfsNft = await ethers.getContractAt("RandomIpfsNft", randomIpfsNftDeployer.address);
              const vrfCoordinatorV2MockDeployer = await deployments.get("VRFCoordinatorV2Mock");
              vrfCoordinatorV2Mock = await ethers.getContractAt("VRFCoordinatorV2Mock", vrfCoordinatorV2MockDeployer.address);
            
                const createSubTx = await vrfCoordinatorV2Mock.createSubscription();
                const createSubReceipt = await createSubTx.wait(1);
                const subId = createSubReceipt.logs[0].args.subId;
                await vrfCoordinatorV2Mock.addConsumer(subId, randomIpfsNftDeployer.address);
          });



        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"
                  );
              });

Here is the contract:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

import "@chainlink/contracts/src/v0.8/vrf/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/vrf/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{

    enum Breed {
        PUG,
        SHIB_INU,
        ST_BERNARD
    }

    VRFCoordinatorV2Interface private  immutable i_vrfCoordinator;
    uint64 private immutable i_subscriptionId;
    bytes32 private immutable i_gasLane;
    uint32 private immutable i_callbackGasLimit;
    uint16 private constant REQUEST_CONFIRMATION = 3;
    uint32 private constant NUM_WORDS = 1;

    mapping (uint256 => address) public s_requestIdToSender;


    uint256 public s_tokenCounter;
    uint256 internal constant MAX_CHANCE_VALUE = 100;
    string[3] internal s_dogTokenUris;
    uint256 internal i_mintFee;

    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") Ownable(msg.sender) {
        i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
        i_subscriptionId = subscriptionId;
        i_gasLane = gasLane;
        i_callbackGasLimit = callbackGasLimit;
        s_dogTokenUris = dogTokenUris;
        i_mintFee = mintFee;
    }

    function requestNft() public payable returns(uint256 requestId){
        if(msg.value < i_mintFee){
            revert RandomIpfsNft__NeedMoreETHSent();
        }
        requestId;
        requestId = i_vrfCoordinator.requestRandomWords(i_gasLane, i_subscriptionId, REQUEST_CONFIRMATION, 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;

        uint256 moddedRng = randomWords[0] % MAX_CHANCE_VALUE;
        s_tokenCounter++;
        Breed dogBreed=getBreedFromModdedRng(moddedRng);
        _safeMint(dogOwner, newTokenId);
        _setTokenURI(newTokenId, s_dogTokenUris[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 pure returns(Breed){
        uint256 sum = 0;
        uint256[3] memory chanceArray = getChanceArray();
        for(uint256 i = 0; i< chanceArray.length; i++){
            if(moddedRng >= sum && moddedRng < sum + chanceArray[i]){
                return Breed(i);
            }
            sum += 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_dogTokenUris[index];
    }
    function getTokenCounters() public view returns(uint256){
        return s_tokenCounter;
    }
}
2
  • Can you share your code via GitHub repo, so I can try debugging the same on my end? Commented Jul 15 at 13:53
  • Added in the question
    – Jimit
    Commented Jul 16 at 12:59

2 Answers 2

0

For your project's repo, I've created a PR by doing the fixes, and here's the description for the same.

In the randomIpfsNft.test.js file:

  • Instead of creating a new subscription, you should use the existing subscription having the subId value as 1n, because that's what being created and used while deploying the RandomIpfsNft contract. As, in the test, you're not redeploying it, but using it from the deployments fixture. So, in beforeEach, these lines are replaced:

    const createSubTx = await vrfCoordinatorV2Mock.createSubscription();
    const createSubReceipt = await createSubTx.wait(1);
    const subId = createSubReceipt.events[0].args.subId;
    

    with:

    const subId = 1n;
    
  • In the mints NFT after random number is returned test of fulfillRandomWords:

    await vrfCoordinatorV2Mock.fulfillRandomWords(
         requestNftReceipt.events[1].args.requestId,
         randomIpfsNft.address
    );
    

    is replaced with:

    await vrfCoordinatorV2Mock.fulfillRandomWords(
         requestNftReceipt.logs[1].args.requestId,
         await randomIpfsNft.getAddress()
    );
    
  • And, the logic of your getBreedFromModdedRng function isn't working as expected for the values of moddedRng that are greater than 99. So, you need to reconstruct that logic. Meanwhile, in the corresponding tests section, I've changed the last 2 tests as:

    it("should return st. bernard if moddedRng is between 40 - 139", async function () {
        const expectedValue = await randomIpfsNft.getBreedFromModdedRng(77);
        assert.equal(2, expectedValue);
    });
    
    it("should revert if moddedRng > 139", async function () {
        await expect(randomIpfsNft.getBreedFromModdedRng(140)).to.be.revertedWithCustomError(randomIpfsNft, "RandomIpfsNft__RangeOutOfBounds");
    });
    
3
  • Can you please provide the code to check subId because in my other project same problem is happening and while giving the subId as 1n still giving me the same error.
    – Jimit
    Commented Jul 21 at 13:23
  • If you’re calling createSubscription and fundSunscription in your deploy script. Then, you’ve to console log the subId while executing your deploy script, and note it down so as to use the same in your test scripts. Commented Jul 21 at 13:58
  • Otherwise, you can execute them in your test script itself. Commented Jul 21 at 13:58
0

The InvalidConsumer() error is mostly thrown when there is some problem with the vrfCoordinatorV2Mock.addConsumer().Most probably the param subId might be not correct set.

Can you try making the const subId = createSubReceipt.logs[0].args.subId; to const subId = createSubReceipt.events[0].args.subId; cause like .createSubscription emits an event which contains the subId .

Hope it helps

1
  • now giving me following error TypeError: Cannot read properties of undefined (reading '0')
    – Jimit
    Commented Jul 15 at 13:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.