foundry-rs / foundry

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.
https://getfoundry.sh
Apache License 2.0
8.09k stars 1.67k forks source link

Getting InvalidFEOpcode error with VRFv2 on fork-url mainnet #8475

Open cqlyj opened 1 month ago

cqlyj commented 1 month ago

Component

Forge

Have you ensured that all of these are up to date?

What version of Foundry are you on?

forge 0.2.0

What command(s) is the bug in?

forge test --fork-url $MAINNET_RPC_URL --mt testFulfillRandomWordsUpdatesRandomWords -vvvvv

Operating System

Linux

Describe the bug

I was writing a foundry script:

contract FundSubscriptionDistribution is Script {
    uint96 public constant FUND_AMOUNT = 3 ether;

    function fundSubscriptionWithConfig() internal {
        HelperConfig helperConfig = new HelperConfig();
        (
            uint64 subscriptionId,
            address vrfCoordinator,
            ,
            ,
            address linkTokenAddress,
            uint256 deployerKey
        ) = helperConfig.activeNetworkConfig();

        fundSubscription(
            vrfCoordinator,
            subscriptionId,
            linkTokenAddress,
            deployerKey
        );
    }

    function fundSubscription(
        address vrfCoordinator,
        uint64 subscriptionId,
        address linkTokenAddress,
        uint256 deployerKey
    ) public {
        console.log("Funding subscription: ", subscriptionId);
        console.log("Using vrfCoordinator: ", vrfCoordinator);
        console.log("ChainId:", block.chainid);

        if (block.chainid == 31337) {
            vm.startBroadcast(deployerKey);

            VRFCoordinatorV2Mock(vrfCoordinator).fundSubscription(
                subscriptionId,
                FUND_AMOUNT
            );

            vm.stopBroadcast();
        } else {
            vm.startBroadcast(deployerKey);

            MockLinkToken link = MockLinkToken(linkTokenAddress);

            link.transferAndCall(
                vrfCoordinator,
                FUND_AMOUNT,
                abi.encode(subscriptionId)
            );

            vm.stopBroadcast();
        }
    }

    function run() external {
        fundSubscriptionWithConfig();
    }
}

And for the test:

function testFulfillRandomWordsUpdatesRandomWords() external {
        distribution.distributionRandomNumberForVerifiers();
        uint256[] memory randomWords = distribution.getRandomWords();
        assertEq(randomWords.length, uint256(numWords));
    }

This test will first deploy the contract and then createSubscription on chainlink VRFv2, then fundSubscription it and addConsumer to get the randomWords back. The test works fine on local anvil chain and testnet sepolia. But it reverts on fork mainnet and reverts with the error below:

    │   │   ├─ [8797746687667313448] LinkToken::transferAndCall(VRFCoordinatorV2: [0x271682DEB8C4E0901D1a1550aD2e64D568E69909], 3000000000000000000 [3e18], 0x00000000000000000000000000000000000000000000000000000000000003b5)
    │   │   │   └─ ← [InvalidFEOpcode] EvmError: InvalidFEOpcode
    │   │   └─ ← [Revert] EvmError: Revert
    │   └─ ← [Revert] EvmError: Revert
    └─ ← [Revert] EvmError: Revert

I have no idea why this revert on mainnet fork testing.

DaniPopes commented 1 month ago

Maybe you're missing approval for the token? 0xfe opcode was used in old solidity versions for failed assertions/requires

cqlyj commented 1 month ago

Maybe you're missing approval for the token? 0xfe opcode was used in old solidity versions for failed assertions/requires

Here is the MockLinkToken Contract: https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/mocks/MockLinkToken.sol

I think it has nothing to do with approval because it works on sepolia testnet and if I try to call the setBalance function on mainnet forking, it will also revert. But the setBalance function in the MockLinkContract got no modifier or anything related to owner, it should not revert.

I don't know why it behaves differently on testnet sepolia and the mainnet.

 MockLinkToken(linkTokenAddress).setBalance(
                address(this),
                FUND_AMOUNT
            );
  │   │   ├─ [327] LinkToken::setBalance(FundSubscriptionDistribution: [0xDDc10602782af652bB913f7bdE1fD82981Db7dd9], 3000000000000000000 [3e18])
    │   │   │   └─ ← [Revert] EvmError: Revert
    │   │   └─ ← [Revert] EvmError: Revert
    │   └─ ← [Revert] EvmError: Revert
    └─ ← [Revert] EvmError: Revert