smartcontractkit / hardhat-starter-kit

A repo for boilerplate code for testing, deploying, and shipping chainlink solidity code.
MIT License
1.21k stars 491 forks source link

Predictable number for testing #96

Closed leovafme closed 2 years ago

leovafme commented 2 years ago

In the RandomNumberConsumerV2 unit test, the result of the number is validated with always being greater than zero. At first it's fine. but how is it applied for the moment in which it must be tested with a winning or specific number?

assert(firstRandomNumber.gt(ethers.constants.Zero))

Is there any way to provide the number as in the v1 mock?

it("Should successfully request a random number and get a result", async () => { const transaction = await randomNumberConsumer.getRandomNumber() const transactionReceipt = await transaction.wait(1) const requestId = transactionReceipt.events[0].topics[1] const randomValue = 777 await vrfCoordinatorMock.callBackWithRandomness( requestId, randomValue, randomNumberConsumer.address ) assert.equal((await randomNumberConsumer.randomResult()).toString(), randomValue) })

andrejrakic commented 2 years ago

Hey @leovafme, thanks for raising this issue. This repo use VRFCoordinatorV2Mock from @chainlink/contracts package. V1 mock expected uint to be passed to the fulfillment function, while V2 mock calculates it, you can see how from the implementation. You can either calculate those mock random numbers inside the test itself using ethers.js following the implementation from the VRFCoordinatorV2Mock, OR you can always create your own mock contract/function to test a "specific number" scenario. Maybe this repo from the "Testing with Hardhat" workshop at Chainlink Spring 2022 Hackathon can help you with the mocks alongside the hardhat-starter-kit repo, of course. Let me know if I can close this or if you maybe have additional questions? Thanks.

leovafme commented 2 years ago

Hi @andrejrakic. Thank you very much for quick response. I tried to do as you say but I can't find a solution to the dilemma. I have searched and I have not seen any place that has implemented this successfully.

Is there a repository or someone who has implemented it? Thanks.

andrejrakic commented 2 years ago

@leovafme, how about something like this? (Haven't had time to try it btw, this is just a pseudo-code)

  const vrfConsumerBaseV2Mock: MockContract = await waffle.deployMockContract(
    deployer,
    VRF_CONSUMER_BASE_V2_ABI
  );

  const randomWords = [BigNumber.from(`777`)];

  vrfConsumerBaseV2Mock.mock.rawFulfillRandomWords(requestId, randomWords).returns();
  // or
  vrfConsumerBaseV2Mock.mock["rawFulfillRandomWords(uint256, uint256[])"].withArgs(requestId, randomWords).returns();
leovafme commented 2 years ago

Hi @andrejrakic thanks for the code snippet. I tests it. the function rawFulfillRandomWords required OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator). I extended VRFCoordinatorV2Mock

pragma solidity ^0.8.0;

import "@chainlink/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol";

contract VRFCoordinatorV2MockHelper is VRFCoordinatorV2Mock {
    constructor(uint96 _baseFee, uint96 _gasPriceLink) VRFCoordinatorV2Mock(_baseFee, _gasPriceLink) {}

    receive() external payable {}
}

in test fund ether to VRFCoordinatorV2MockHelper and use hardhat_impersonateAccount for call vrfConsumerBaseV2Mock

deployer.sendTransaction({
  to: vrfCoordinatorV2Mock.address,
  value: ethers.utils.parseEther("5.0"),
});

await ethers.provider.send("hardhat_impersonateAccount", [vrfCoordinatorV2Mock.address]);
await ethers.provider.send("evm_mine")

const signer = await ethers.getSigner(vrfCoordinatorV2Mock.address);

const randomWords = [777, 778];

await randomNumberConsumerV2.connect(signer).rawFulfillRandomWords(requestId, randomWords);

this works for me, but is it the proper implementation? Thanks.