I am following Patrick Collins course. At chapter 9, when we do a testnet test on rinkeby. The event is not firing. I am using chainlink Keeper and VRF to run the the code as detailed below.
Console error log
fulfillRandomWords
works with live Chainlink Keepers and Chainlink VRF, we get a random winner:
Error: Timeout of 500000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (D:\coding-learning\solidityjsbypatrick\hardhat-smartcontract-lottery\test\staging\Raffle.staging.test.ts)
As you can see, I am getting timeout after 500sec.
helper-network file
const networkConfig: networkConfigInfo = {
4: {
name: "rinkeby",
vrfCoordinatorV2: "0x6168499c0cffcacd319c818142124b7a15e857ab",
raffleEntranceFee: "10000000000000000", // 0.01 ETH
gasLane: "0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc", //30 gwei
subscriptionId: 7783,
callBackGasLimit: "500000", // 500,000 gas
keepersUpdateInterval: "30", // in seconds
},
.........
......
}
chain link VRF screenshot
Chainlink keeper screenshot
Staging Test file (TypeScript)
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { assert, expect } from "chai";
import { BigNumber } from "ethers";
import { ethers, network } from "hardhat";
import { developmentChains } from "../../helper-hardhat-config";
import { Raffle } from "../../typechain-types";
//run onlu on test/main net
developmentChains.includes(network.name)
? describe.skip
: describe("Raffle Staging Test", function () {
let player: SignerWithAddress;
let deployer: SignerWithAddress;
let raffle: Raffle;
let raffleEntranceFee: BigNumber;
beforeEach(async () => {
const accounts = await ethers.getSigners();
deployer = accounts[0];
player = accounts[1];
raffle = await ethers.getContract("Raffle", deployer); //connecting account[0]/deployer with contract. We can use player account to using .connect(player)
raffleEntranceFee = await raffle.getEnteranceFee();
});
describe("fulfillRandomWords", function () {
it("works with live Chainlink Keepers and Chainlink VRF, we get a random winner", async () => {
//enter the raffle
console.log("Setting up test...");
const startingTimeStamp = await raffle.getLastTimeStamp();
console.log("Setting up Listener...");
await new Promise<void>(async (resolve, reject) => {
// setup listener before we enter the raffle. Just in case the blockchain moves REALLY fast
raffle.once("WinnerPicked", async () => {
console.log("WinnerPicked event fired!");
try {
// get data after the winner is picked and event is fired.
const recentWinner = await raffle.getRecentWinner();
const raffleState = await raffle.getRaffleState();
const endingTimeStamp = await raffle.getLastTimeStamp();
const winnerFinalBalance = await deployer.getBalance(); //here account[0]/deployer is the only entrant also. So he is the winner.
//start assertation
assert.equal(raffleState, 0);
assert.equal(recentWinner.toString(), deployer.address);
assert(endingTimeStamp > startingTimeStamp);
await expect(raffle.getPlayer(0)).to.be.reverted;
assert.equal(
winnerFinalBalance.toString(),
winnerStartingBalance.add(raffleEntranceFee).toString()
);
resolve();
} catch (e) {
console.log(e);
reject(e);
}
});
//Entering the raffle
console.log("Entering Raffle...");
const tx = await raffle.enterRaffle({ value: raffleEntranceFee });
await tx.wait(1);
const winnerStartingBalance = await deployer.getBalance(); //here account[0]/deployer is the only entrant also. So he is the winner.
// And this code Wont compelte untill our listener (above) has finished listening.
console.log("Ok, time to wait...");
});
});
});
});
Raffle.sol file [Smart contract]
// 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 "@chainlink/contracts/src/v0.8/KeeperCompatible.sol";
error Raffle__SendMoreToEnterRaffle();
error Raffle__RaffleIsNotOpen();
error Raffle__TransferFailed();
error Raffle__UpkeepNotNeeded(uint256 currentBalance, uint256 numPlayers, uint256 raffleState);
contract Raffle is VRFConsumerBaseV2, KeeperCompatibleInterface {
enum RaffleState {
Open,
Calculating
}
uint256 private immutable i_enteranceFee;
address payable[] private s_players;
address private s_recentWinner;
uint256 private s_lastTimeStamp;
uint256 private immutable i_interval;
RaffleState private s_raffleState;
VRFCoordinatorV2Interface private immutable i_vrfCoordinator;
bytes32 private immutable i_gasLane;
uint64 private immutable i_subscriptionId;
uint16 private constant REQUEST_CONFIRMATION = 3;
uint32 private immutable i_callbackGasLimit;
uint32 private constant NUM_WORDS = 1;
event RaffleEnter(address indexed player);
event RequestRafflewinner(uint256 indexed requestId);
event WinnerPicked(address indexed winner);
constructor(
address vrfCoordinatorV2,
uint64 subscriptionId,
uint256 enteranceFee,
bytes32 gasLane,
uint32 callbackGasLimit,
uint256 interval
) VRFConsumerBaseV2(vrfCoordinatorV2) {
i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
i_subscriptionId = subscriptionId;
i_enteranceFee = enteranceFee;
i_gasLane = gasLane;
i_callbackGasLimit = callbackGasLimit;
i_interval = interval;
s_raffleState = RaffleState.Open;
s_lastTimeStamp = block.timestamp;
}
// To enter into lottery
function enterRaffle() public payable {
if (msg.value < i_enteranceFee) {
revert Raffle__SendMoreToEnterRaffle();
}
if (s_raffleState != RaffleState.Open) {
revert Raffle__RaffleIsNotOpen();
}
s_players.push(payable(msg.sender));
emit RaffleEnter(msg.sender);
}
function checkUpkeep(
bytes memory /* checkData */
)
public
view
override
returns (
bool upkeepNeeded,
bytes memory /* performData */
)
{
bool isOpen = (RaffleState.Open == s_raffleState);
bool timePassed = ((block.timestamp - s_lastTimeStamp) > i_interval);
bool hasPlayers = (s_players.length > 0);
bool hasBalance = (address(this).balance > 0);
upkeepNeeded = (isOpen && timePassed && hasPlayers && hasBalance);
return (upkeepNeeded, "");
}
function performUpkeep(
bytes calldata /* performData */
) external override {
(bool upkeepNeeded, ) = checkUpkeep("");
if (!upkeepNeeded) {
revert Raffle__UpkeepNotNeeded(
address(this).balance,
s_players.length,
uint256(s_raffleState)
);
}
s_raffleState = RaffleState.Calculating;
uint256 requestId = i_vrfCoordinator.requestRandomWords(
i_gasLane,
i_subscriptionId,
REQUEST_CONFIRMATION,
i_callbackGasLimit,
NUM_WORDS
);
emit RequestRafflewinner(requestId);
}
function fulfillRandomWords(
uint256, /*requestId*/
uint256[] memory randomWords
) internal override {
uint256 indexOfWinner = randomWords[0] % s_players.length;
address payable recentWinner = s_players[indexOfWinner];
s_recentWinner = recentWinner;
s_players = new address payable[](0);
s_raffleState = RaffleState.Open;
s_lastTimeStamp = block.timestamp;
(bool success, ) = recentWinner.call{value: address(this).balance}("");
if (!success) {
revert Raffle__TransferFailed();
}
emit WinnerPicked(recentWinner);
}
//Rest of the getter function..
}
Contract address on rinkeby
0xdc6a50411fb987123bE0D048Cde60c66135e2FDe