PatrickAlphaC / hardhat-smartcontract-lottery-fcc

MIT License
117 stars 183 forks source link

fullFillRandomWords() function is not calling by chainlink VRF in seplia testnet #200

Open kolliparasurya opened 6 months ago

kolliparasurya commented 6 months ago

any can say that i have done all steps correctly up to end of the raffle lesson 9 but when I run the staging test I entered the raffle , the RequestedRaffleWinner() has been called but the fulfillWords() function was not called in the upkeep subscription website the transaction completed , but for in vrf.chain.link account it is showing in pending as you shown in the photo , but the transaction is showing success in sepolia.etherscan.io, now the raffleState is in calculating so i can't enter or call performUpkeep() function or do anything until that fulfillrandomWords () function is callled by chainllink VRF , so what i have to do to resolve this is anything i have done in wrong in the code

i have give my raffle solidity code

it is from chainlink vrf account Screenshot from 2024-01-19 21-00-07

it is from etherscan Screenshot from 2024-01-19 21-26-08 Screenshot from 2024-01-19 21-25-43

it is from chianlink keepers account Screenshot from 2024-01-19 21-28-01

/Raffle
//Enter the lottery (paying some amount
//pick a random winner (verifiabley random)
//wnner to be selected every x minutes -> completly automated
// Chain link Oracle -> Randomness, Automated Execution (Chainlink keeper)

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

import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol";

error Raffle__NotEnoughETHEntered();
error Raffle__TranferFailed();
error Raffle__NotOpen();
error Raffle__UpkeepNotNeeded(
    uint256 currentBalance,
    uint256 numPlayers,
    uint256 raffleState
);

/**@title A sample Raffle Contract
 * @author kollipara surya
 * @notice This contract is for creating an untamperble decentralize smart contract
 * @dev This implements Chianlink VRF V2 and Chainlink keepers
 */
contract Raffle is VRFConsumerBaseV2, AutomationCompatibleInterface {
    /* Type declarations */
    enum RaffleState {
        OPEN,
        CALCULATING
    } //uint256

    /*state Variables*/
    uint256 private immutable i_entranceFee;
    address payable[] private s_players;
    VRFCoordinatorV2Interface private immutable i_COORDINATOR;
    bytes32 private immutable i_keyHash;
    uint64 private immutable i_subscriptionId;
    uint32 private immutable i_callbackGasLimit;
    uint16 private constant REQUEST_CONFIRMATIONS = 3;
    uint32 private constant numWords = 1;

    /*lotery variables*/
    address private s_recentWinner;
    RaffleState private s_raffleState;
    uint256 private s_lastTimeStamp;
    uint256 private immutable i_interval;

    /*Events*/
    event RaffleEnter(address indexed player);
    event RequestedRaffleWinner(uint256 indexed requestId);
    event WinnerPicked(address indexed winner);

    /* Functions */
    constructor(
        address vrfCoordinatorV2, //contract
        uint256 entranceFee,
        bytes32 keyHash,
        uint64 subscriptionId,
        uint32 callbackGasLimit,
        uint256 interval
    ) VRFConsumerBaseV2(vrfCoordinatorV2) {
        i_entranceFee = entranceFee;
        i_COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinatorV2);
        i_keyHash = keyHash;
        i_subscriptionId = subscriptionId;
        i_callbackGasLimit = callbackGasLimit;
        s_raffleState = RaffleState.OPEN;
        s_lastTimeStamp = block.timestamp;
        i_interval = interval;
    }

    function enterRaffle() public payable {
        //require (msg.value > i_entranceFee , "Not enough ETH!")
        if (msg.value < i_entranceFee) {
            revert Raffle__NotEnoughETHEntered();
        }
        if (s_raffleState != RaffleState.OPEN) {
            revert Raffle__NotOpen();
        }
        s_players.push(payable(msg.sender));
        //Emit an event when we update a dynamic Array or mapping
        // Named events with the function named reversed
        emit RaffleEnter(msg.sender);
    }

    /**
     * @dev This is the function that the chailink keeper nodes call
     * they look for the `upkeepNeeded to return true
     * The following should be true in order to return true:
     * 1. Our time intervel should have passed
     * 2. The lottery should have at least 1 player, and have some ETH
     * 3. Our subscription is funded with  LINK
     * 4. The lottery should be in an "open" state.
     */
    function checkUpkeep(
        bytes calldata /* checkData */
    )
        public
        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);
    }

    function performUpkeep(bytes calldata /*performData*/) external override {
        (bool upkeepNeeded, ) = this.checkUpkeep("");
        if (!upkeepNeeded) {
            revert Raffle__UpkeepNotNeeded(
                address(this).balance,
                s_players.length,
                uint256(s_raffleState)
            );
        }

        s_raffleState = RaffleState.CALCULATING;
        uint256 requestId = i_COORDINATOR.requestRandomWords(
            i_keyHash, //gaslane
            i_subscriptionId,
            REQUEST_CONFIRMATIONS,
            i_callbackGasLimit,
            numWords
        );
        emit RequestedRaffleWinner(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_raffleState = RaffleState.OPEN;
        s_players = new address payable[](0);
        s_lastTimeStamp = block.timestamp;
        (bool success, ) = recentWinner.call{value: address(this).balance}("");

        if (!success) {
            revert Raffle__TranferFailed();
        }
        emit WinnerPicked(recentWinner);
    }

    /* View /Pure functions */
    function getEntranceFee() public view returns (uint256) {
        return i_entranceFee;
    }

    function getPlayer(uint256 index) public view returns (address) {
        return s_players[index];
    }

    function getRecentWinner() public view returns (address) {
        return s_recentWinner;
    }

    function getRaffleState() public view returns (RaffleState) {
        return s_raffleState;
    }

    function getNumWords() public pure returns (uint256) {
        return numWords;
    }

    function getNumberOfPlayers() public view returns (uint256) {
        return s_players.length;
    }

    function getLatestTimeStamp() public view returns (uint256) {
        return s_lastTimeStamp;
    }

    function getRequestConfirmations() public pure returns (uint256) {
        return REQUEST_CONFIRMATIONS;
    }

    function getInterval() public view returns (uint256) {
        return i_interval;
    }
}
PatrickAlphaC commented 6 months ago

hmm... Thanks for this. This is likely an issue with the testnet. They are testnets for a reason! If all your tests are passing, then I'd recommend just moving past this. Seems like you've learned what this lesson was intended to teach, and now the testnet is just giving issues.

It being a testnet, that happens sometimes sadly :/

rocco1226 commented 5 months ago

This is because of the instability testnet sepolia. My vrfCoordinator on sepolia finished after maintaining pending for more than 2 hours. Finally it worked.