0

I'm building a smart contract that generates 3 random numbers using Chainlink VRF. My contract (SlotMachine.sol) implements the VRFConsumerBaseV2 and the constructor has 2 parameters: _subscriptionId and _vrfCoordinator:

constructor(
        uint64 _subscriptionId,
        address _vrfCoordinator
    ) payable VRFConsumerBaseV2(_vrfCoordinator) 

I have a play() function which calls the requestRandonWords through the vrfCoordinator and I overrided the fulfillRandomWord function to use the generated random words.

I want to do 2 things: 1) create the unit tests in slotmachine.js and 2) test my contract in the hardhat network (chainId = 31337) through deploy.js

For this I'm using the VRFCoordinatorV2Mock which helps me mock an oracle's behavior (as I understand).

My unit tests in slotmachine.js are working well. The fulfillRandomWords is called and then my tests are passing. However when I add a similar logic to the deploy.js file (deploy the mock contract and then the slot machine contract) the fulfillRandomWords is not being called when I call the play() function (which has the requestRandomWords inside it) from my deployed contract.

slotmachine.js

const { ethers } = require("hardhat");
const { expect } = require("chai");
const { BigNumber } = require("ethers");

const provider = ethers.getDefaultProvider();

describe("Decentralized Slot Machine", async function () {
  let myContract;
  let hardhatVrfCoordinatorV2Mock;

  describe("Testing Decentralized Slot Machine", function () {
    //1. Contract deployment
    it("Should deploy Slot Machine Contract", async function () {
      const SlotMachine = await ethers.getContractFactory("SlotMachine");
      let vrfCoordinatorV2Mock = await ethers.getContractFactory(
        "VRFCoordinatorV2Mock"
      );

      hardhatVrfCoordinatorV2Mock = await vrfCoordinatorV2Mock.deploy(0, 0);

      await hardhatVrfCoordinatorV2Mock.createSubscription();

      await hardhatVrfCoordinatorV2Mock.fundSubscription(
        1,
        ethers.utils.parseEther("7")
      );

      myContract = await SlotMachine.deploy(
        1,
        hardhatVrfCoordinatorV2Mock.address,
        {
          value: ethers.utils.parseEther("100"),
        }
      );

      await hardhatVrfCoordinatorV2Mock.addConsumer(1, myContract.address);
    });

    //2. Play
    describe("First Play - First Player", function () {
      it("Contract should receive random numbers", async () => {
        const [account1, account2] = await ethers.getSigners();

        let tx = await myContract.play(ethers.constants.AddressZero, {
          value: ethers.utils.parseEther("1"),
        });
        let { events } = await tx.wait();

        let [reqId] = events.filter((x) => x.event === "RequestedRandomness")[0]
          .args;

        await expect(
          hardhatVrfCoordinatorV2Mock.fulfillRandomWords(
            reqId,
            myContract.address
          )
        ).to.emit(myContract, "ReceivedRandomness");

        let round = await myContract.rounds(reqId);

        expect(round.userAddress).to.be.equal(account1.address);
        expect(round.number1).to.be.equal(1);
        expect(round.number2).to.be.equal(9);
        expect(round.number3).to.be.equal(6);
      });       

    });
  });
});

deploy.js

const { ethers } = require("hardhat");

const localChainId = "31337";

module.exports = async ({ getNamedAccounts, deployments, getChainId }) => {
  const { deploy } = deployments;
  const { deployer } = await getNamedAccounts();
  const chainId = await getChainId();

  await deploy("VRFCoordinatorV2Mock", {
    from: deployer,
    args: [0, 0],
    log: true,
    waitConfirmations: 5,
  });

  const hardhatVrfCoordinatorV2Mock = await ethers.getContract(
    "VRFCoordinatorV2Mock",
    deployer
  );

  await hardhatVrfCoordinatorV2Mock.createSubscription();

  await hardhatVrfCoordinatorV2Mock.fundSubscription(
    1,
    ethers.utils.parseEther("7")
  );

  const myContract = await deploy("SlotMachine", {
    from: deployer,
    args: [1, hardhatVrfCoordinatorV2Mock.address],
    log: true,
    waitConfirmations: 5,
  });

  await hardhatVrfCoordinatorV2Mock.addConsumer(1, myContract.address);

  console.log("Contract address: ", myContract.address);
};
module.exports.tags = ["SlotMachine"];

Question

Can I deploy my contract in the hardhat network with the expected behavior of the VRFCoordinatorV2Mock which is to call automatically fulfillRandomWords? If so, how can I do this? What do I have to change in my code? If the answer is no, is there another alternative than deploying to a testnet?

2 Answers 2

0

You need to create a script which calls the ‘play()’ function on deployed contract locally in hardhat you can do it with ‘yarn hardhat run scripts/callPlay.js’ (considering you have callPlay.js inside your ‘scripts’ directory)

Also feel free to checkout https://github.com/technophile-04/Lottery-SmartContract-PureRandom it has mocks and and scripts to mock it locally(here I have used Chainlink keepers too so it might be a bit messy but you will get the idea) also checkout https://youtu.be/gyMwXuJrbJQ at timestamp 13:41:07 you will get the full idea how to do it.

0

I solved my problem.

Nope, the fulfillRandomWords cannot be called automatically because it is called by Chainlink's VRFCoordinator and it only works on a mainnet or a testnet. So I have to call it manually from my VRFCoordinatorV2Mock, after I call my play function which returns the requestId I have to use as a parameter.

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.