0

So I'm writing this lottery smart contract which is pretty straight forward, and since I want to test this on the goerli test net, I want to be able to add the contract as a subscriber to my VRF every time it's deployed.


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "node_modules/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "node_modules/@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "node_modules/@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";

contract Lottery is VRFConsumerBaseV2 {
    VRFCoordinatorV2Interface COORDINATOR;

    address owner;
    address[] buyers = [msg.sender];
    address winner;
    address vrfCoordinator = 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D;

    bytes32 keyHash;

    uint32 callbackGasLimit = 5000000;
    uint16 requestConfirmations = 3;
    uint32 numWords = 1;
    uint256[] public randomWords;
    uint256 public requestId;
    uint64 subscriptionId;
    uint256 usdEntryFee;
    uint256 startingTime;
    uint256 endTime;
    uint256 prizePool;
    bool available;

    AggregatorV3Interface public priceFeed;

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    function enterLottery() public payable returns (bool success) {
        buyers.push(msg.sender);
    }

    function getEntranceFee() public view returns (uint256) {
        (, int256 price, , , ) = priceFeed.latestRoundData();
        uint256 adjustedPrice = uint256(price) * 10**10; // 18 decimals
        // $50, $2,000 / ETH
        // 50/2,000
        // 50 * 100000 / 2000
        uint256 costToEnter = (usdEntryFee * 10**18) / adjustedPrice;
        return costToEnter;
    }

    constructor(address _priceFeed, uint64 _subscriptionId)
        VRFConsumerBaseV2(vrfCoordinator)
    {
        priceFeed = AggregatorV3Interface(_priceFeed);
        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        owner = msg.sender;
        usdEntryFee = 50 * (10**18);
        subscriptionId = _subscriptionId;
    }

    function requestRandomWords() public onlyOwner {
        requestId = COORDINATOR.requestRandomWords(
            keyHash,
            subscriptionId,
            requestConfirmations,
            callbackGasLimit,
            numWords
        );
    }

    function addSubscription() public {
        COORDINATOR.addConsumer(subscriptionId, address(this));
    }

    function fulfillRandomWords(
        uint256, // requestId
        uint256[] memory _randomWords
    ) internal override {
        randomWords = _randomWords;
    }

    function startLottery(uint256 _endTime) public onlyOwner {
        startingTime = block.timestamp;
        available = true;
        endTime = startingTime + _endTime;
    }

    function endLottery() public payable onlyOwner returns (uint256) {
        require(
            block.timestamp > endTime,
            "Auction ending date not arrived yet!"
        );
        available = false;
        uint256 randomIndex;
        requestRandomWords();
        randomIndex = randomWords[0] % buyers.length;
        winner = buyers[randomIndex];
        return (randomWords[0]);
    }

    function transferWinnings() public payable onlyOwner {
        if (!payable(winner).send(prizePool)) {
            revert("Transaction failed!");
        }
    }
}

I'm using brownie framework to deploy and test, and this is the test function I'm using.

from brownie import Lottery, accounts, config, network
from scripts.helpful_scripts import get_account
from web3 import Web3

def test_random_number():
    account = get_account()
    lottery = Lottery.deploy(config["networks"][network.show_active()]["eth_usd_price_feed"], 1563, {"from": account})
    lottery.addSubscription({"from": account})
    lottery.requestRandomWords({"from": account})
    assert lottery.randomWords[0] != 0

I'm using the VRF admin wallet to do all of this but still it's not adding the contract to my subscriptions.

Also if anyone is familiar with how I could use the Chainlink VRF V2 mocks, any help would be appreciated.

1 Answer 1

1

Your contract is not working because you aren't setting the VRF Address in the constructor. You need to do something like this:

constructor(address _priceFeed, uint64 _subscriptionId, address vrfCoordinator)
        VRFConsumerBaseV2(vrfCoordinator)
    {
        priceFeed = AggregatorV3Interface(_priceFeed);
        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        owner = msg.sender;
        usdEntryFee = 50 * (10**18);
        subscriptionId = _subscriptionId;
    }

As of now, your COORDINATOR doesn't have a VRF address to work with.

If you want to use the VRFV2Mock, you will need to deploy the mock first (on a local chain) and pass in the mock address in the Lottery constructor.

Also, I don't get why you want to add the contract as a consumer everytime it's deployed. You need to only do that once.

You should go to the VRF website and create a subscription using your wallet. Then, after deploying the contract to the Goerli testnet, you can just add the contract as a consumer. This would be a lot simpler.

EDIT: I suspect you want to automatically create a new subscription each time because everytime you run a test the contract gets deployed to a new address. To combat this, just create a subscripton with your wallet and save the subscription ID to your contract.

Then, you can just use COORDINATOR.addConsumer(subscriptionId, address(this), to add every new contract to the same subscription.

1
  • Thanks a lot, your solution did the trick, and well I got around using the mocks for a couple of days and now I know how to work with them. <3
    – Rjey
    Commented Oct 1, 2022 at 11:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.