1

I am trying to create a CoinFlip program and using VRF Chainlink subscription funding method. In VRF we need to define two functions in order to get first the request ID and then the random number of words , I have curb both of the functions into one , however since my program does not get the request ID on time the getStatus() function never runs. What could be the solution ?

pragma solidity ^0.8.7;

interface funding{
    function transferToken(address payable owner,uint256 amount)external returns(bool);
}

interface vrfConsumer{
    function requestRandomWords() external returns(uint256 rqeuestId);

    function getRequestStatus(uint256 _requestId) external view returns (bool fulfilled, uint256[] memory randomWords);
  
}

contract CoinFlip {

    event ChoicePlayer(uint256 requestId);
    event NumOfWords(uint256[] numberofwords);
    event Winner(string Winner);

    uint256 public constant fee = 1000000000000000;
    address private vrfcontract = 0x278EfB5Bfec8ca3723CfE3e1d78a30F9d86fe2fa; 
    uint256 public requestId;
    address public owner;
    address private contactAddress = 0x96b52c38bd0859B25dCed6315e692be0C68B9a3C;

    enum choice{
        HEADS,
        TAILS
    }

    struct coinflipStatus{
        choice player;
        uint256 requestId;
        uint256[]  numofWords;
        bool fullfilled;
    }

    mapping(uint256 => coinflipStatus) public playerStatus;

    constructor () {
        owner = msg.sender;
    }

    modifier  amountPaid(uint256 value){
        require(value == fee,"Did not pay enough!");
        _;
    }

    modifier  onlyOwner(){
        require(owner == msg.sender,"Only called by owner!");
        _;
    }

    receive () external  payable {}

    function flip(choice value)   internal   {
        uint256 _requestedId = vrfConsumer(vrfcontract).requestRandomWords();

        coinflipStatus memory coinstatus;
        coinstatus.player = value; // You can set the choice here (HEADS or TAILS)
        coinstatus.requestId = _requestedId;

        playerStatus[_requestedId] = coinstatus;
        requestId = _requestedId;
        emit ChoicePlayer(_requestedId);
    }

    function withdraw() internal onlyOwner {
        (bool result , ) = contactAddress.call{value: address(this).balance}("");
    }

    function playerDecision(uint256[] memory numofwords) internal {
        uint256 number = numofwords[0] % 2;
        coinflipStatus memory coin;
        coin = playerStatus[requestId];

        if(number  ==   uint256(coin.player)){
            funding(contactAddress).transferToken(payable(owner),fee);
            emit Winner("Player won!");
        }else{
            withdraw();
            emit Winner("Player Lost!");
        }   
       
    }

    function getStatus() internal  returns(uint256[] memory numsWords){
        coinflipStatus memory coin;
        coin = playerStatus[requestId];
        (bool result , uint256[] memory numofwords) = vrfConsumer(vrfcontract).getRequestStatus(coin.requestId);
        if(result == true){
            coin.numofWords = numofwords;
            coin.fullfilled = true;
            emit NumOfWords(numofwords);
            playerDecision(numofwords);
            return(numofwords); 
        }
    }

    function madhanFlipBitch(choice value)external payable amountPaid(msg.value){
        flip(value);    
        getStatus();
    }
    

}```

1 Answer 1

0

Chainlink VRF is an on-demand service and randomness request process is like below:

Step 1: you send a VRF request to VRFCoordinator
Step 2: then the off-chain oracle node detects your request
Step 3: oracle node calculates a randomness using VRF algorithm
Step 4: oracle node finally fulfills randomness back to your smart contract.

In the process, Step 1 and step 4 cannot be in the same transaction, so Chainlink oracle node cannot write the randomness(return value) back to your contract in the same transaction you send the request. You have to wait for several blocks and then check the randomness returned from Chainlink oracle node.

I assume that you are using the codes in Chainlink official docs for as your vrfConsumer. If you check the source code, you will find that randomness is written by Chainlink oracle node via the function fulfillRandomWords in contract vrfConsumer. That is the reason you think the function getStatus() does not run. Actually, it runs but there is no value returned by Chainlink node yet.

IMO, it is impossible to get randomness right in the same transaction where you send a request. You have to create one more function in your contract CoinFlip to read the randomness in vrfConsumer rather than curb both of the functions into one.

Hope it helps.

3
  • Hi , thanks for the explanation. However, while creating the frontend, I don't want the user to do a transaction twice to check if he/she has won the flip. What could I do from my frontend in order to make it a simple single interaction? Aug 3, 2023 at 17:11
  • @kaushikchoudhury my suggestion is to add a variable in the contract to flag if the request is fulfilled. In the front-end, you can create a service to monitor the variable of the contract and display the result once fulfilled.
    – Frank Kong
    Aug 4, 2023 at 2:27
  • GOTCHA ! Thanks alot Aug 6, 2023 at 20:12

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.