1

I started coding project and I got error when trying to test him. When I trying to call some contract function from js tester i got error:

Error: call revert exception [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ] (method="getEntranceFee()", data="0x", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.7.0)
      at Logger.makeError (node_modules\@ethersproject\logger\src.ts\index.ts:269:28)
      at Logger.throwError (node_modules\@ethersproject\logger\src.ts\index.ts:281:20)
      at Interface.decodeFunctionResult (node_modules\@ethersproject\abi\src.ts\interface.ts:427:23)
      at Contract.<anonymous> (node_modules\@ethersproject\contracts\src.ts\index.ts:400:44)
      at step (node_modules\@ethersproject\contracts\lib\index.js:48:23)
      at Object.next (node_modules\@ethersproject\contracts\lib\index.js:29:53)
      at fulfilled (node_modules\@ethersproject\contracts\lib\index.js:20:58)
      at processTicksAndRejections (node:internal/process/task_queues:95:5)
      at runNextTicks (node:internal/process/task_queues:64:3)
      at listOnTimeout (node:internal/timers:540:9)

Here is my codes:

!developmentChains.includes(network.name) ? describe.skip : describe("Lottery Unit Test", () => {
    let lottery, vrfCoordinatorV2Mock, lotteryEntranceFee, deployer, interval;
    const chainId = network.config.chainId;

    beforeEach(async () => { // before all testing we do this part of code (get our contracts)
        deployer = (await getNamedAccounts()).deployer;
        await deployments.fixture(["all"]); // means that we want deploy everything
        lottery = await ethers.getContractAt("Lottery", deployer);
        vrfCoordinatorV2Mock = await ethers.getContractAt("VRFCoordinatorV2Mock", deployer);
        lotteryEntranceFee = await lottery.getEntranceFee(); // here I got error
        interval = await lottery.getInterval(); // and here I got error
    })
})
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/interfaces/KeeperCompatibleInterface.sol";
import "hardhat/console.sol";

/* using error instead of require is gas efficient */
error Lottery__NotEnoughFund();
error Lottery__TransferFailed();
error Lottery__LotteryIsClosed();
error Lottery__UpkeepNotNeeded(
    uint256 curBalance,
    uint256 numPlayers,
    uint256 lotteryState
);

/**
 * @title A sample Lottery contract
 * @author me
 * @notice This contract is for creation untamperable decentralized smart contract
 * @dev This contract is using Chainlink VRF v2 and Chainlink Keepers
 */

contract Lottery is VRFConsumerBaseV2, KeeperCompatibleInterface {
    // here: 0 - OPEN, 1 - PENDING and etc.
    enum LotteryState {
        OPEN,
        PENDING
    }

    uint256 private constant ENTRANCE_FEE = 0.01 ether;
    uint8 private constant REQUEST_CONFIRMATION = 3;
    uint8 private constant NUM_WORD = 1; // we need only one random number

    address payable[] private s_players; // if somebody from players array wins we should pay them and its why we put here 'payable'
    address private s_winner;

    VRFCoordinatorV2Interface private immutable i_vrfCoordinator;

    bytes32 private immutable i_gasLane;
    uint64 private immutable i_subId;
    uint32 private immutable i_callbackGasLimit;
    uint256 private immutable i_interval;

    uint256 private s_lastTimeStamp;

    LotteryState private s_lotteryState;

    /*'event' use for getting some info from blockchain when in blockchain something happens (winning in lottery like example) */
    event LotteryEnter(address indexed player); // 'indexed' means that value will be easier to read
    event RequestedLotteryWinner(uint256 indexed requestId);
    event PickedWinner(address indexed winner);

    constructor(
        address vrfCoordinatorV2, // contract
        bytes32 gasLane, // keyHash
        uint64 subscriptionId,
        uint32 callbackGasLimit,
        uint256 interval
    ) VRFConsumerBaseV2(vrfCoordinatorV2) {
        i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
        i_gasLane = gasLane;
        i_subId = subscriptionId;
        i_callbackGasLimit = callbackGasLimit;
        s_lotteryState = LotteryState.OPEN; // checker if lottery is open or closed
        s_lastTimeStamp = block.timestamp;
        i_interval = interval;
    }

    function enterLottery() public payable {
        if (msg.value < ENTRANCE_FEE) {
            revert Lottery__NotEnoughFund(); // instead a require to be gas efficient
        }
        if (s_lotteryState != LotteryState.OPEN) {
            revert Lottery__LotteryIsClosed();
        }
        s_players.push(payable(msg.sender));
        emit LotteryEnter(msg.sender);
    }

    /**
     * @dev This is the function that the Chainlink Keeper nodes call
     * they look for `upkeepNeeded` to return True.
     * the following should be true for this to return true:
     * 1. The time interval has passed between raffle runs.
     * 2. The lottery is open.
     * 3. The contract has ETH.
     * 4. Implicity, your subscription is funded with LINK.
     */

    function checkUpkeep(
        bytes memory /* checkData */ // also function don't use it but needed
    )
        public
        view
        override
        returns (
            bool upkeepNeeded,
            bytes memory /* performData */ // use if we want that 'checkUpkeep' doing some other stuff
        )
    {
        bool isOpen = (s_lotteryState == LotteryState.OPEN);
        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); // check all indicators
    }

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

        s_lotteryState = LotteryState.PENDING; // while we pick a random winner we put our state pending

        /* this is also call event like emit */
        uint256 requestId = i_vrfCoordinator.requestRandomWords(
            i_gasLane, // maximum size of gas for number
            i_subId, // Id that this contract uses for funding requests
            REQUEST_CONFIRMATION, // how many confirmations we should wait to get result (recommend min. 2)
            i_callbackGasLimit, // limit how much gas uses to callback requests
            NUM_WORD // how much numbers we get
        );

        /* this is redundant event because in the top requestId also call event */
        emit RequestedLotteryWinner(requestId);
    }

    function fulfillRandomWords(
        uint256 /* requestId */, // we make 'requestId' unused instead of delete because we didn't use this value but out function are needed this
        uint256[] memory randomWords
    ) internal override {
        uint256 indexOfWinner = randomWords[0] % s_players.length;
        address payable winner = s_players[indexOfWinner];
        s_winner = winner;
        s_players = new address payable[](0);
        s_lastTimeStamp = block.timestamp;
        (bool success, ) = winner.call{value: address(this).balance}(""); // we tranfer all money to address of winner
        if (!success) {
            revert Lottery__TransferFailed();
        }

        s_lotteryState = LotteryState.OPEN;
        emit PickedWinner(winner);
    }

    function getInterval() public view returns (uint256) {
        return i_interval;
    }

    function getEntranceFee() public pure returns (uint256) {
        return ENTRANCE_FEE;
    }

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

    function getWinner() public view returns (address) {
        return s_winner;
    }

    function getLotteryState() public view returns (LotteryState) {
        return s_lotteryState;
    }

    function getNumWords() public pure returns (uint256) {
        // function 'pure' instead of 'view' because we return constant variable
        return NUM_WORD;
    }

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

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

    function getRequestConfirmation() public pure returns (uint256) {
        return REQUEST_CONFIRMATION;
    }
}

Please help

1 Answer 1

0

The (CALL_EXCEPTION) error occurs when your smart contract is not deployed yet.

So you should wait for the deployment transaction to pass before accessing the contract's methods.

It looks like your attempt to deploy all smart contracts at once may have adverse effects.

You can't just simply call deployments.fixture(['all']) without setting up the deployment scripts in advance.

I haven't seen your deployment scripts, but if you do have them set up, make sure you added the tag at the end of the contract deployment script files.

Like the following:

module.exports.tag = ["all", "Lottery"]

Otherwise, deployments.fixture(['all']) will not work, and you'll only be able to work it around by deploying the Lottery contract explicitly and inline, like this:

const lottery = await ethers.deployContract("Lottery", [...whatever arguments are required based on your current deployment script], {});

await lottery.waitForDeployment(); // IMPORTANT!

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.