I'm working on testing a Chainlink VRF-based Raffle contract using Foundry, but I’m encountering a EvmError: Revert when running the test that checks whether the raffle correctly blocks entries while it's in the CALCULATING state.
Here’s the output when running the test:
Ran 1 test for test/uint/RaffleTest.t.sol:RaffleTest
[FAIL: EvmError: Revert] testDontAllowPlayersToEnterWhenRaffleIsCalculating() (gas: 102379)
Logs:
please update the subscriptionid in your HelperConfig.s.sol
uising vrfCoordinator: 0x34A1D3fff3958843C43aD80F30b94c510645C316
Adding consumer contract 0xDB8cFf278adCCF9E9b5da745B44E754fC4EE3C76
To vrfCoordinator 0x34A1D3fff3958843C43aD80F30b94c510645C316
Traces:
[102379] RaffleTest::testDontAllowPlayersToEnterWhenRaffleIsCalculating()
├─ [0] VM::prank(player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C])
│ └─ ← [Return]
├─ [48011] Raffle::enterRaffle{value: 10000000000000000}()
│ ├─ emit RaffleEntered(player: player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C])
│ └─ ← [Stop]
├─ [0] VM::warp(32)
│ └─ ← [Return]
├─ [0] VM::roll(2)
│ └─ ← [Return]
├─ [30682] Raffle::performUpkeep(0x)
│ ├─ [270] VRFCoordinatorV2Mock::requestRandomWords(RandomWordsRequest({ keyHash: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae, subId: 1, requestConfirmations: 3, callbackGasLimit: 500000 [5e5], numWords: 1, extraArgs: 0x92fd13380000000000000000000000000000000000000000000000000000000000000000 }))
│ │ └─ ← [Revert] EvmError: Revert
│ └─ ← [Revert] EvmError: Revert
└─ ← [Revert] EvmError: Revert
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 25.56ms (2.02ms CPU time)
Ran 1 test suite in 495.49ms (25.56ms CPU time): 0 tests passed, 1 failed, 0 skipped (1 total tests)
Failing tests:
Encountered 1 failing test in test/uint/RaffleTest.t.sol:RaffleTest
[FAIL: EvmError: Revert] testDontAllowPlayersToEnterWhenRaffleIsCalculating() (gas: 102379)
Encountered a total of 1 failing tests, 0 tests succeeded
testcode
function testDontAllowPlayersToEnterWhenRaffleIsCalculating() public {
vm.prank(PLAYER);
raffle.enterRaffle{value: _entranceFee}();
vm.warp(block.timestamp + _interval + 1);
vm.roll(block.number + 1);
raffle.performUpkeep("");
vm.expectRevert(Raffle.Raffle__RaffleNotOpened.selector);
vm.prank(PLAYER);
raffle.enterRaffle{value: _entranceFee}();
}
I have a DeployRaffle script that sets up everything including the VRF subscription:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {Script} from "forge-std/Script.sol";
import {Raffle} from "src/Raffle.sol";
import {HelperConfig} from "./HelperConfig.s.sol";
import {CreateSubscription, FundSubscription, AddConsumer} from "./interactions.s.sol";
contract DeployRaffle is Script {
function run() external {
deployContract();
}
function deployContract() public returns (Raffle, HelperConfig) {
HelperConfig helperConfig = new HelperConfig();
HelperConfig.NetworkConfig memory config = helperConfig.getConfig();
if (config.subscriptionId == 0) {
CreateSubscription createSubscription = new CreateSubscription();
(config.subscriptionId, config.vrfCoordinator) =
createSubscription.createSubscription(config.vrfCoordinator);
FundSubscription fundSubscription = new FundSubscription();
fundSubscription.fundSubscription(config.vrfCoordinator, config.subscriptionId, config.link);
}
vm.startBroadcast();
Raffle raffle = new Raffle(
config._entranceFee,
config._interval,
config.vrfCoordinator,
config.gasLane,
config.subscriptionId,
config.callBackGasData
);
vm.stopBroadcast();
AddConsumer addConsumer = new AddConsumer();
addConsumer.addConsumer(address(raffle), config.vrfCoordinator, config.subscriptionId);
return (raffle, helperConfig);
}
}
Here is the script from my interactions.s.sol script that creates, funds, and registers the consumer:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {HelperConfig, CodeConstants} from "./HelperConfig.s.sol";
import {VRFCoordinatorV2Mock} from "@chainlink/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2Mock.sol";
import {LinkToken} from "../test/mocks/LinkToken.sol";
import {DevOpsTools} from "../lib/foundry-devops/src/DevOpsTools.sol";
contract CreateSubscription is Script {
function createSubscriptionUsingConfig() public returns (uint256, address) {
HelperConfig helperConfig = new HelperConfig();
address vrfCoordinator = helperConfig.getConfig().vrfCoordinator;
(uint64 subId,) = createSubscription(vrfCoordinator);
return (subId, vrfCoordinator);
}
function createSubscription(address vrfCoordinator) public returns (uint64, address) {
console.log("creating subscription on chain: ", block.chainid);
vm.startBroadcast();
uint64 subId = VRFCoordinatorV2Mock(vrfCoordinator).createSubscription();
vm.stopBroadcast();
console.log("your subscription id is: ", subId);
console.log("please update the subscriptionid in your HelperConfig.s.sol");
return (subId, vrfCoordinator);
}
function run() public {
createSubscriptionUsingConfig();
}
}
contract FundSubscription is Script, CodeConstants {
uint256 public constant FUND_AMOUNT = 3 ether;
function fundSubscriptionUsingConfig() public {
HelperConfig helperConfig = new HelperConfig();
address vrfCoordinator = helperConfig.getConfig().vrfCoordinator;
uint256 subscriptionId = helperConfig.getConfig().subscriptionId;
address linkToken = helperConfig.getConfig().link;
fundSubscription(vrfCoordinator, subscriptionId, linkToken);
}
function fundSubscription(address vrfCoordinator, uint256 subscriptionId, address linkToken) public {
console.log("funding subscription ", subscriptionId);
console.log("uising vrfCoordinator: ", vrfCoordinator);
console.log("on Chain: ", block.chainid);
if (block.chainid == LOCAL_CHAIN_ID) {
vm.startBroadcast();
VRFCoordinatorV2Mock(vrfCoordinator).fundSubscription(uint64(subscriptionId), uint96(FUND_AMOUNT));
vm.stopBroadcast();
} else {
vm.startBroadcast();
LinkToken(linkToken).transferAndCall(vrfCoordinator, FUND_AMOUNT, abi.encode(subscriptionId));
vm.stopBroadcast();
}
}
function run() public {
fundSubscriptionUsingConfig();
}
}
contract AddConsumer is Script {
function addConsumerUsingConfig(address mostRecentlyDeployed) public {
HelperConfig helperConfig = new HelperConfig();
uint256 subId = helperConfig.getConfig().subscriptionId;
address vrfCoordinator = helperConfig.getConfig().vrfCoordinator;
addConsumer(mostRecentlyDeployed, vrfCoordinator, subId);
}
function addConsumer(address contractToAddToVrf, address vrfCoordinator, uint256 subId) public {
console.log("Adding consumer contract ", contractToAddToVrf);
console.log("To vrfCoordinator ", vrfCoordinator);
console.log("on ChainId: ", block.chainid);
vm.startBroadcast();
VRFCoordinatorV2Mock(vrfCoordinator).addConsumer(uint64(subId), contractToAddToVrf);
vm.stopBroadcast();
}
function run() public {
address mostRecentlyDeployed = DevOpsTools.get_most_recent_deployment("raffle", block.chainid);
addConsumerUsingConfig(mostRecentlyDeployed);
}
}