I am going through the Foundry Course by Patrick Collins (@Big Shoutout) and I have just completed the section on creating the Raffle.
I deployed the Raffle successfully on Eth Sepolia and created the VRF Subscription, Chainlink Automation and everything was working great! Happy days.
I then decided I wanted to try and deploy on Arbitrum Sepolia. So I created the Arbitrum helper config for the network:
function getArbSepEthConfig() public view returns (NetworkConfig memory) {
return
NetworkConfig({
entranceFee: 0.01 ether,
interval: 30,
vrfCoordinator: 0x50d47e4142598E3411aA864e08a44284e471AC6f,
gasLane: 0x027f94ff1465b3525f9fc03e9ff7d6d2c0953482246dd6ae07570c45d6631414,
subscriptionId: 211,
callbackGasLimit: 2500000,
link: 0xb1D4538B4571d411F07960EF2838Ce337FE1E80E,
deployerKey: vm.envUint("PRIVATE_KEY")
});
}
All of the parameters are correct as far as I can see.
Here is the DeployRaffle.s.sol contract that is using the config:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Script, console} from "forge-std/Script.sol";
import {Raffle} from "../src/Raffle.sol";
import {HelperConfig} from "./HelperConfig.s.sol";
import {VRFCoordinatorV2Mock} from "../test/mock/VRFCoordinatorV2Mock.sol";
import {CreateSubscription, FundSubscription, AddConsumer} from "./Interactions.s.sol";
contract DeployRaffle is Script {
function run() external returns (Raffle, HelperConfig) {
HelperConfig helperConfig = new HelperConfig();
(
uint256 entranceFee,
uint256 interval,
address vrfCoordinator,
bytes32 gasLane,
uint64 subscriptionId,
uint32 callbackGasLimit,
address link,
uint256 deployerKey
) = helperConfig.activeNetworkConfig();
if (subscriptionId == 0) {
CreateSubscription createSubscription = new CreateSubscription();
subscriptionId = createSubscription.createSubscription(
vrfCoordinator,
deployerKey
);
FundSubscription fundSubscription = new FundSubscription();
fundSubscription.fundSubscription(
vrfCoordinator,
link,
subscriptionId,
deployerKey
);
}
AddConsumer addComsumer = new AddConsumer();
if (block.chainid == 31337) {
vm.startBroadcast();
} else {
vm.startBroadcast(deployerKey);
console.log("Deploying Raffle on ChainId: ", block.chainid);
}
Raffle raffle = new Raffle(
entranceFee,
interval,
vrfCoordinator,
gasLane,
subscriptionId,
callbackGasLimit
);
vm.stopBroadcast();
addComsumer.addConsumer(
vrfCoordinator,
subscriptionId,
address(raffle),
deployerKey
);
return (raffle, helperConfig);
}
}
And finally, here is the Raffle.sol that I have deployed:
// Layout of Contract:
// version
// imports
// errors
// interfaces, libraries, contracts
// Type declarations
// State variables
// Events
// Modifiers
// Functions
// Layout of Functions:
// constructor
// receive function (if exists)
// fallback function (if exists)
// external
// public
// internal
// private
// internal & private view & pure functions
// external & public view & pure functions
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {VRFCoordinatorV2Interface} from "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
/**
* @title Raffle
* @author Charlie Mack
* @notice This contract is used for a simple raffle
* @dev Implements Chainlink VRF v2
*/
contract Raffle is VRFConsumerBaseV2 {
/** Errors */
error Raffel__NotEnoughETHSent();
error Raffel__TransferFailed();
error Raffle__RaffleNotOpen();
error Raffle__RaffleUpkeepNotNeeded(
uint256 balance,
uint256 playersLength,
RaffleState raffleState,
uint256 timeSinceLastRaffle
);
/** Interfaces */
VRFCoordinatorV2Interface private immutable i_vrfCoordinator;
/** Type Declarations */
enum RaffleState {
OPEN,
CALCULATING
}
/** State Variables */
uint16 private constant REQUEST_CONFIRMATIONS = 3;
uint32 private constant NUM_WORDS = 1;
uint256 private immutable i_entranceFee;
uint256 private immutable i_interval;
bytes32 private immutable i_gasLane;
uint64 private immutable i_subscriptionId;
uint32 private immutable i_callbackGasLimit;
address payable[] private s_players;
uint256 private s_lastTimeStamp;
address private s_recentWinner;
RaffleState private s_raffleState;
/** Events */
event EnteredRaffle(address indexed player);
event PickedWinner(address indexed winner);
event RequestedRaffleWinner(uint256 indexed requestId);
constructor(
uint256 entranceFee,
uint256 interval,
address vrfCoordinator,
bytes32 gasLane,
uint64 subscriptionId,
uint32 callbackGasLimit
) VRFConsumerBaseV2(vrfCoordinator) {
i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinator);
i_entranceFee = entranceFee;
i_interval = interval;
s_lastTimeStamp = block.timestamp;
i_gasLane = gasLane;
i_subscriptionId = subscriptionId;
i_callbackGasLimit = callbackGasLimit;
s_raffleState = RaffleState.OPEN;
}
function enterRaffle() external payable {
if (s_raffleState != RaffleState.OPEN) {
revert Raffle__RaffleNotOpen();
}
if (msg.value < i_entranceFee) {
revert Raffel__NotEnoughETHSent();
}
s_players.push(payable(msg.sender));
emit EnteredRaffle(msg.sender);
}
/**
* @dev This is the function that the Chainlink Automation nodes call
* to see if it's time to perform an upkeep.
* The following should be true for this to return true:
* 1. The raffle is in the OPEN state
* 2. The time since the last raffle is greater than the interval
* 3. The contract has ETH (aka, players)
* 4. (Implicit) The subscription is funded with LINK
*/
function checkUpkeep(
bytes memory /*checkData*/
) public view returns (bool upkeepNeeded, bytes memory /* performData */) {
bool timeHasPassed = (block.timestamp - s_lastTimeStamp) > i_interval;
bool isOpen = RaffleState.OPEN == s_raffleState;
bool hasBalance = address(this).balance > 0;
bool hasPlayers = s_players.length > 0;
upkeepNeeded = timeHasPassed && isOpen && hasBalance && hasPlayers;
return (upkeepNeeded, "0x0");
}
function performUpkeep(bytes calldata /* performData */) external {
(bool upkeepNeeded, ) = checkUpkeep("");
if (!upkeepNeeded) {
revert Raffle__RaffleUpkeepNotNeeded(
address(this).balance,
s_players.length,
s_raffleState,
block.timestamp - s_lastTimeStamp
);
}
s_raffleState = RaffleState.CALCULATING;
//1 Request the RNG
//2 Get the random Number
uint256 requestId = i_vrfCoordinator.requestRandomWords(
i_gasLane,
i_subscriptionId,
REQUEST_CONFIRMATIONS,
i_callbackGasLimit,
NUM_WORDS
);
emit RequestedRaffleWinner(requestId);
}
function fulfillRandomWords(
uint256 /* requestId */,
uint256[] memory randomWords
) internal override {
uint256 indexOfWinner = randomWords[0] % s_players.length;
address payable winner = s_players[indexOfWinner];
s_recentWinner = winner;
//reset the raffle
s_raffleState = RaffleState.OPEN;
s_players = new address payable[](0);
s_lastTimeStamp = block.timestamp;
emit PickedWinner(winner);
(bool success, ) = winner.call{value: address(this).balance}("");
if (!success) {
revert Raffel__TransferFailed();
}
}
/** Getter Functions */
function getEntranceFee() public view returns (uint256) {
return i_entranceFee;
}
function getInterval() public view returns (uint256) {
return i_interval;
}
function getGasLane() public view returns (bytes32) {
return i_gasLane;
}
function getSubscriptionId() public view returns (uint64) {
return i_subscriptionId;
}
function getCallbackGasLimit() public view returns (uint32) {
return i_callbackGasLimit;
}
function getPlayers() public view returns (address payable[] memory) {
return s_players;
}
function getPlayer(uint256 index) public view returns (address payable) {
return s_players[index];
}
function getLastTimeStamp() public view returns (uint256) {
return s_lastTimeStamp;
}
function getRecentWinner() public view returns (address) {
return s_recentWinner;
}
function getRaffleState() public view returns (RaffleState) {
return s_raffleState;
}
}
As you can see I seem to be using the correct vrf coordinator address as stated in the chainlink docs.
The contract was deployed here: 0xd4624f3A28905523AA30609052a20767f434DA28
When I try to call the performUpkeep function, I get the following error:
├─ [0] console::log("mostRecentlyDeployed: ", 0xd4624f3A28905523AA30609052a20767f434DA28) [staticcall]
│ └─ ← ()
├─ [0] VM::startBroadcast()
│ └─ ← ()
├─ [8797746687693881515] 0xd4624f3A28905523AA30609052a20767f434DA28::performUpkeep(0x)
│ ├─ [8797746687693851289] VRFCoordinatorV2::requestRandomWords(0x027f94ff1465b3525f9fc03e9ff7d6d2c0953482246dd6ae07570c45d6631414, 211, 3, 500000 [5e5], 1)
│ │ ├─ [8797746687693840135] 0x0000000000000000000000000000000000000064::arbBlockNumber() [staticcall]
│ │ │ └─ ← EvmError: InvalidFEOpcode
│ │ └─ ← EvmError: Revert
│ └─ ← EvmError: Revert
└─ ← EvmError: Revert
Does anyone have an idea on what is going on here? Help is much appreciated :)