0

I'm trying to implement Chainlink VRFv2 with my upgradeable smart contract, and none of the versions seem to work. Although I'm able to deploy the proxy correctly, when I'm trying to call function that is supposed to request for random numbers, I'm getting: estimation gas price failed error:

metamask transaction

Here is the VRFConsumerBaseV2Upgradeable I found in github issue: https://github.com/smartcontractkit/chainlink/issues/4976#issuecomment-1484938645

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

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

abstract contract VRFConsumerBaseV2Upgradeable is Initializable {
  error OnlyCoordinatorCanFulfill(address have, address want);
  
  address private vrfCoordinator;

  function __VRFConsumerBaseV2Upgradeable_init(address _vrfCoordinator) internal onlyInitializing {
    vrfCoordinator = _vrfCoordinator;
  }

  function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual;

  function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external {
    if (msg.sender != vrfCoordinator) {
      revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
    }
    fulfillRandomWords(requestId, randomWords);
  }
}

And here is dummy contract where I'm trying to use this VRF:

// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.24;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "./VRFConsumerBaseV2Upgradeable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract VRFTest is Initializable, VRFConsumerBaseV2Upgradeable {

  using Counters for Counters.Counter;
  Counters.Counter private tokenIds;

  /// @custom:oz-upgrades-unsafe-allow constructor
  constructor() {
    _disableInitializers();
  }

  VRFCoordinatorV2Interface COORDINATOR;

  // mumbai
  address constant vrfCoordinator = 0x7a1BaC17Ccc5b313516C5E16fb24f7659aA5ebed;
  // bytes32 constant keyHash = 0x4b09e658ed251bcafeebbc69400383d49f344ace09b9576fe248bb02c003fe9f;

  uint256 public requestId;
  uint64 constant subscriptionId = 180;
  uint32 constant callbackGasLimit = 2500000;
  uint16 constant requestConfirmations = 3;
  uint16 constant randomNumbersAmount =  1;


  mapping(uint256 => uint256) public requestIdToTokenId;
  mapping(uint256 => uint256) public tokenIdToRandomNumber;

  function initialize() initializer public {
    __VRFConsumerBaseV2Upgradeable_init(vrfCoordinator);
  }

  function mint() external {
    tokenIds.increment();

    requestId = COORDINATOR.requestRandomWords(
      0x4b09e658ed251bcafeebbc69400383d49f344ace09b9576fe248bb02c003fe9f, //keyhash
      subscriptionId,
      requestConfirmations,
      callbackGasLimit,
      randomNumbersAmount
    );

    requestIdToTokenId[requestId] = tokenIds.current();
  }

  function fulfillRandomWords(uint256 _requestId, uint256[] memory randomNumbers) internal override {
    uint256 tokenId = requestIdToTokenId[_requestId];

    tokenIdToRandomNumber[tokenId] = randomNumbers[0];
  }
}

I also added to my subscription both contracts just in case, the proxy one, and the implementation.

1 Answer 1

1

You don't need to import the Initializable.sol and inherit the Initializable contract in your VRFTest contract (as it's already being imported and inherited in VRFConsumerBaseV2Upgradeable contract which is itself being inherited in the VRFTest contract).

And, you're disabling the initializers in its constructor by calling _disableInitializers(). So, you've to remove that too, in order to be able to call the initialize() function (as it's utilising the initializer modifier). Since, that's the only function you're calling inside the constructor(), so you can get rid of the constructor itself (unless you decide to initialise some of the params inside the constructor).

Also, you've to initialise the COORDINATOR inside the initialize() function, like this:

COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);

Here's the revised code for VRFTest:

// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.24;

import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "./VRFConsumerBaseV2Upgradeable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract VRFTest is VRFConsumerBaseV2Upgradeable {

  using Counters for Counters.Counter;
  Counters.Counter private tokenIds;

  VRFCoordinatorV2Interface COORDINATOR;

  // mumbai
  address constant vrfCoordinator = 0x7a1BaC17Ccc5b313516C5E16fb24f7659aA5ebed;
  // bytes32 constant keyHash = 0x4b09e658ed251bcafeebbc69400383d49f344ace09b9576fe248bb02c003fe9f;

  uint256 public requestId;
  uint64 constant subscriptionId = 180;
  uint32 constant callbackGasLimit = 2500000;
  uint16 constant requestConfirmations = 3;
  uint16 constant randomNumbersAmount =  1;


  mapping(uint256 => uint256) public requestIdToTokenId;
  mapping(uint256 => uint256) public tokenIdToRandomNumber;

  function initialize() public initializer {
    __VRFConsumerBaseV2Upgradeable_init(vrfCoordinator);
    COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
  }

  function mint() external {
    tokenIds.increment();

    requestId = COORDINATOR.requestRandomWords(
      0x4b09e658ed251bcafeebbc69400383d49f344ace09b9576fe248bb02c003fe9f, //keyhash
      subscriptionId,
      requestConfirmations,
      callbackGasLimit,
      randomNumbersAmount
    );

    requestIdToTokenId[requestId] = tokenIds.current();
  }

  function fulfillRandomWords(uint256 _requestId, uint256[] memory randomNumbers) internal override {
    uint256 tokenId = requestIdToTokenId[_requestId];

    tokenIdToRandomNumber[tokenId] = randomNumbers[0];
  }
}

P.S., As you've mentioned "I also added to my subscription both contracts just in case, the proxy one, and the implementation.":

You don't need to add the implementation contract (i.e., VRFCoordinatorV2) in your subscription, just add the proxy contract (i.e., VRFTest).

2
  • 1
    Thank you for your answer! Indeed I forgot to initialise COORDINATOR like this: COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);. So that solves my problem without having to remove the constructor completely with _disableInitializers() as it is and extra safety mechanism forum.openzeppelin.com/t/…
    – BlackH3art
    Mar 22 at 5:30
  • You're welcome. But, in my case, it's throwing gas estimation error unless I remove the _disableInitializers(); though? Mar 22 at 8:23

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.