1

When I try to add a consumer to my VRF2 subscription via the Subscription Manager, the transaction completes successfully. This also works when interacting with the chainlink contract directly on etherscan.

I'd like to deploy a contract that automatically adds itself to my existing chainlink subscription upon deployment. I have used the code provided by chainlink here.

The following code works on deployment:

    constructor() VRFConsumerBaseV2(vrfCoordinator) {
        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        LINKTOKEN = LinkTokenInterface(link_token_contract);
        s_owner = msg.sender;
        //Create a new subscription when you deploy the contract.
        createNewSubscription();
    }
      
    function createNewSubscription() private onlyOwner {
        s_subscriptionId = COORDINATOR.createSubscription();
        COORDINATOR.addConsumer(s_subscriptionId, address(this));
    }

As seen above the contract deploys and creates a new subscription and adds a consumer. I originally simply wanted to add a consumer to an existing subscription so I modified the createNewSubscription function.

    // Create a new subscription when the contract is initially deployed.
    function createNewSubscription() private onlyOwner {
        s_subscriptionId = COORDINATOR.createSubscription();
        COORDINATOR.addConsumer(1234, address(this));
    }

This does not work. Of course I already have a subscription(1234) that works and can add to it manually, calling from another contract does not work. I have also tried assigning s_subscriptionId initially: uint64 s_subscriptionId = 1234; which also does not seem to be working.

Further tests with (,s_subscriptionId,,) = COORDINATOR.getSubscription(); also failed. Pointers on how to debug this would also be welcome as I'm learning solidity.

2 Answers 2

2

I have found the issue. Looking at VRFCoordinatorV2.sol The function addConsumer has a modifier called onlySubOwner here. Which is not explicitly outlined in the docs.

function addConsumer(uint64 subId, address consumer) external override onlySubOwner(subId) nonReentrant {
    // Already maxed, cannot add any more consumers.
    if (s_subscriptionConfigs[subId].consumers.length == MAX_CONSUMERS) {
      revert TooManyConsumers();
    }
    if (s_consumers[consumer][subId] != 0) {
      // Idempotence - do nothing if already added.
      // Ensures uniqueness in s_subscriptions[subId].consumers.
      return;
    }
    // Initialize the nonce to 1, indicating the consumer is allocated.
    s_consumers[consumer][subId] = 1;
    s_subscriptionConfigs[subId].consumers.push(consumer);

    emit SubscriptionConsumerAdded(subId, consumer);
  }
modifier onlySubOwner(uint64 subId) {
    address owner = s_subscriptionConfigs[subId].owner;
    if (owner == address(0)) {
      revert InvalidSubscription();
    }
    if (msg.sender != owner) {
      revert MustBeSubOwner(owner);
    }
    _;
  }

This means it is not possible to add a consumer if you're not the owner of the subscription.

I was trying to add the subscription via the deployed smart contract with delegate call, but it only accepts direct interactions from the subscription owner (which was my EOA on Metamask). It seems to be a safety feature rather than a bug, the way to go would be to use topUpSubscription to add the LINK token to the subscription programmatically or use the direct funding method.

Including an acceptSubscriptionOwnerTransfer in the deployed contract would be helpful for troubleshooting with an EOA in case of issues.

2
  • Only a subscriptions owner can add consumers to the subscription. I note the answer by safi answered a different question (how programmatically create a new sub). Your situation is different. Trust you've resolved this now?
    – ZeusLawyer
    Feb 17 at 18:00
  • Yes I did, it was not clear to me from reading the docs so diving deeper into the code helped. Thanks ! Feb 18 at 10:52
0

You can easily do by making createNewSubscription method public like

    function createNewSubscription(address vrfCoordinator, uint64 subId) public onlyOwner {
        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        s_subscriptionId = COORDINATOR.createSubscription();
        COORDINATOR.addConsumer(subId, address(this));
    }
3
  • This issue is not that it is private but the fact that I cannot the function reverts even when it is public. Feb 14 at 17:34
  • According to the doc. After you have created subscription you can add new consumer (i.e. contract address in this case). Also, there is a function name topUpSubscription function. I think you need to top up the subscription using this method in that case you have to deposit LINK to the address.
    – Safi
    Feb 14 at 17:56
  • I've posted my answer above after having some help to dive deeper into the code. My issue was I wanted to add all my contracts to the same subscription ID. Apparently, this is not the recommended way. Feb 16 at 20:00

Your Answer

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

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