I'm deploying a smart contract on the Ethereum Sepolia testnet that integrates Chainlink VRF v2.5
using Foundry. However, I encountered an issue where Foundry provided an invalid Subscription ID that does not exist on the Chainlink VRF Subscription Manager.
Here is my Git Repo for this project: https://github.com/kartik01112004/smart-contract-lottery
What I did:
I wrote a Foundry script called CreateSubscription
as given in Interactions.s.sol to create a subscription and got the Subscription ID.
I deployed my Raffle contract using Foundry script called DeployRaffle
as given in DeployRaffle.s.sol, passing the Subscription ID (that I got in step 1) as one of the constructor
params.
After deployment, I checked the Sepolia Chainlink VRF Subscription Manager, but the Subscription ID provided by Foundry (that I got in step 1) does not exist on the VRF Dashboard.
My contract is now deployed with an incorrect Subscription ID, making it non-functional.
The contract relies on subscriptionId
in performUpkeep
, but since the ID is invalid, the contract fails when calling Chainlink VRF.
Minimal Steps To Reproduce:
Create a Foundry project.
Install the chainlink-brownie-contracts
library:
forge install smartcontractkit/chainlink-brownie-contracts
Inside the script
folder, create a file named CreateSubscription.s.sol
and paste the following content:
//SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {Script, console} from "forge-std/Script.sol";
import {VRFCoordinatorV2_5Mock} from
"lib/chainlink-brownie-contracts/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol";
contract CreateSubscription is Script {
/**
* vrf mock values
*/
uint96 constant MOCK_BASE_FEE = 0.25 ether;
uint96 constant MOCK_GAS_PRICE_LINK = 1e9;
//link eth price
int256 constant MOCK_WEI_PER_UINT_LINK = 4e15;
address constant DEFAULT_ANVIL_ACCOUNT = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
function createSubscription() public {
console.log("Creating subscription on chainID:", block.chainid);
vm.startBroadcast(DEFAULT_ANVIL_ACCOUNT);
uint256 subId =
new VRFCoordinatorV2_5Mock(MOCK_BASE_FEE, MOCK_GAS_PRICE_LINK, MOCK_WEI_PER_UINT_LINK).createSubscription();
vm.stopBroadcast();
console.log("Your Subscription ID is:", subId);
}
function run() public {
createSubscription();
}
}
Run the Foundry Anvil Node.
Execute the script using the following command:
forge script script/CreateSubscription.s.sol:CreateSubscription --rpc-url http://127.0.0.1:8545 -vvvvv
Questions:
Would appreciate any insights from anyone who has faced a similar issue!
The reason you're getting an invalid subscription ID is that you're creating the subscription using VRFCoordinatorV2_5Mock
, which is designed to create a mock subscription locally on Anvil rather than on-chain on Ethereum Sepolia.
If you want to create the subscription on-chain, you need to instantiate IVRFCoordinatorV2Plus
using the vrfCoordinator
address, like:
IVRFCoordinatorV2Plus(vrfCoordinator).createSubscription();
Here's your revised CreateSubscription
contract script:
//SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {HelperConfig, CodeConstants} from "script/HelperConfig.s.sol";
import {VRFCoordinatorV2_5Mock} from "@chainlink/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol";
import {IVRFCoordinatorV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/interfaces/IVRFCoordinatorV2Plus.sol";
import {Vm} from "forge-std/Vm.sol";
import {LinkToken} from "test/mocks/LinkToken.sol";
import {DevOpsTools} from "lib/foundry-devops/src/DevOpsTools.sol";
contract CreateSubscription is Script, CodeConstants {
function createSubscriptionUsingConfig() public returns (uint256, address) {
HelperConfig helperConfig = new HelperConfig();
address vrfCoordinator = helperConfig.getConfig().vrfCoordinator;
address account = helperConfig.getConfig().account;
(uint256 subId,) = createSubscription(vrfCoordinator, account);
return (subId, vrfCoordinator);
}
function createSubscription(address vrfCoordinator, address account) public returns (uint256, address) {
console.log("Creating subscription on chainID: ", block.chainid);
if (block.chainid == LOCAL_CHIAIN_ID) {
vm.startBroadcast(account);
uint256 subId = VRFCoordinatorV2_5Mock(vrfCoordinator).createSubscription();
vm.stopBroadcast();
console.log("your subscription id is: ", subId);
console.log("please update your sub id in HelperConfig.s.sol");
return (subId, vrfCoordinator);
} else {
vm.startBroadcast(account);
IVRFCoordinatorV2Plus(vrfCoordinator).createSubscription();
vm.stopBroadcast();
return (0, vrfCoordinator);
}
}
function run() public {
createSubscriptionUsingConfig();
}
}
NOTE: The subscription ID returned by VRFCoordinatorV2_5Mock(vrfCoordinator).createSubscription();
is not necessarily the actual subscription ID created on the VRF Dashboard. This is because the returned value comes from a simulated execution before the transaction is confirmed on-chain. Foundry simulates the return value before the transaction is mined, which can result in a discrepancy between the simulated value and the actual on-chain value.
Therefore, you must retrieve the actual value either from the transaction logs or directly from the VRF Dashboard.
As for your questions 3 and 4, there is no way to update the subscription ID in your already deployed Raffle contract. This is because the subscription ID is passed as a constructor
argument at deployment, and there is no function to update it. Therefore, you need to redeploy your Raffle contract with a new subscription ID created on Ethereum Sepolia.
Additionally, there is no way to recover the ETH from the Raffle contract, as the only outgoing ETH transaction occurs during winner selection inside the fulfillRandomWords
function.