smartcontractkit / smartcontractkit/chainlink
[SMRT] Nothing is reverted when requestRandomWords is called
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 8.2k
- Forks
- 2k
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 202
Description
Description
When my smart contract is calling requestRandomWords, it reverts but it's revert with a blank character.
I'm using forge 0.2.0.
CoinFlip.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
import {VRFCoordinatorV2Interface} from "@chainlink/contracts/src/v0.8/vrf/interfaces/VRFCoordinatorV2Interface.sol";
contract CoinFlip is VRFConsumerBaseV2 {
/////////////////
/* Errors */
////////////////
error CoinFlip__InsufficientBetAmount();
error CoinFlip__TransferFailed();
////////////////
/* Events */
////////////////
event FlipRequested();
event CoinFlippedAndWon(uint256 indexed requestId, uint256 indexed amount, address player);
/////////////////////////
/* State Variables */
/////////////////////////
enum Side {
HEADS,
TAILS
}
Side private s_choice;
/* Constants */
uint256 private constant MINIMUM_BET = 0.01 ether;
uint32 private constant NUM_WORDS = 1;
uint16 private constant REQUEST_CONFIRMATIONS = 3;
/* Immutable state variables */
bytes32 private immutable i_keyHash;
VRFCoordinatorV2Interface private immutable i_vrfCoordinator;
address private immutable i_link;
uint64 private immutable i_subId;
uint32 private immutable i_callbackGasLimit;
/////////////////////
/* Functions */
/////////////////////
/* Constructor */
constructor(address _vrfCoordinator, address _link, bytes32 _keyHash, uint64 _subId, uint32 _callbackGasLimit)
VRFConsumerBaseV2(_vrfCoordinator)
{
i_vrfCoordinator = VRFCoordinatorV2Interface(_vrfCoordinator);
i_link = _link;
i_keyHash = _keyHash;
i_subId = _subId;
i_callbackGasLimit = _callbackGasLimit;
}
/*
@notice Flip the coin
@dev This function is called by the player to flip the coin
@param _choice The side the player is betting on
*/
function flip(Side _choice) external payable returns (uint256 requestId) {
if (msg.value <= MINIMUM_BET) {
revert CoinFlip__InsufficientBetAmount();
}
s_choice = _choice;
requestId = i_vrfCoordinator.requestRandomWords(
i_keyHash, //gasLane
i_subId,
REQUEST_CONFIRMATIONS,
i_callbackGasLimit,
NUM_WORDS
);
emit FlipRequested();
return requestId;
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
if (Side(randomWords[0] % 2) == s_choice) {
// Player wins
// Transfer the winnings to the player
(bool success,) = payable(msg.sender).call{value: address(this).balance * 2}("");
if (!success) {
revert CoinFlip__TransferFailed();
}
emit CoinFlippedAndWon(requestId, address(this).balance, msg.sender);
}
}
}
CoinFlipTest.t.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Test,console} from "forge-std/Test.sol";
import {CoinFlip} from "../../src/CoinFlip.sol";
import {DeployCoinFlip} from "../../script/DeployCoinFlip.s.sol";
import {HelperConfig} from "../../script/HelperConfig.s.sol";
contract CoinFlipTest is Test {
event FlipRequested();
CoinFlip coinFlip;
HelperConfig helperConfig;
address vrfCoordinator;
address link;
bytes32 keyHash;
uint64 subId;
uint32 callbackGasLimit;
address public PLAYER = makeAddr("player");
uint256 public STARTING_BALANCE = 10 ether;
function setUp() public {
DeployCoinFlip deployer = new DeployCoinFlip();
(coinFlip, helperConfig) = deployer.run(); //run the deploy script
(vrfCoordinator, link, keyHash, subId, callbackGasLimit) = helperConfig.activeNetwork();
}
function testFlipWithInsuffisantBetAmount() public {
vm.prank(PLAYER);
vm.expectRevert(CoinFlip.CoinFlip__InsufficientBetAmount.selector);
coinFlip.flip(CoinFlip.Side.HEADS);
}
function testFlip() public {
hoax(PLAYER, STARTING_BALANCE);
// vm.expectEmit(true, false, false, false, address(coinFlip));
// emit FlipRequested();
uint256 requestId = coinFlip.flip{value: 1 ether}(CoinFlip.Side.HEADS);
console.log(requestId);
}
}
DeployCoinFlip.s.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Script} from "forge-std/Script.sol";
import {CoinFlip} from "../src/CoinFlip.sol";
import {HelperConfig} from "./HelperConfig.s.sol";
contract DeployCoinFlip is Script {
function run() external returns (CoinFlip, HelperConfig) {
HelperConfig helperConfig = new HelperConfig();
(address vrfCoordinator, address link, bytes32 keyHash, uint64 subId, uint32 callbackGasLimit) =
helperConfig.activeNetwork();
vm.startBroadcast();
CoinFlip coinFlip = new CoinFlip(vrfCoordinator, link, keyHash, subId, callbackGasLimit);
vm.stopBroadcast();
return (coinFlip, helperConfig);
}
}
HelperConfig.s.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Script} from "forge-std/Script.sol";
import {VRFCoordinatorV2Mock} from "@chainlink/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2Mock.sol";
contract HelperConfig is Script {
struct NetworkConfig {
address vrfCoordinator;
address link;
bytes32 keyHash;
uint64 subId;
uint32 callbackGasLimit;
}
NetworkConfig public activeNetwork;
uint96 baseFee = 0.25 ether;
uint96 gasPriceLink = 1e9;
constructor() {
if (block.chainid == 11155111) {
activeNetwork = getSepoliaEthConfig();
} else {
activeNetwork = getOrCreateAnvilConfig();
}
}
function getSepoliaEthConfig() public pure returns (NetworkConfig memory) {
return NetworkConfig({
vrfCoordinator: 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625,
link: 0x779877A7B0D9E8603169DdbD7836e478b4624789,
keyHash: 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c,
subId: 8119,
callbackGasLimit: 500000
});
}
function getOrCreateAnvilConfig() public returns (NetworkConfig memory) {
if (activeNetwork.vrfCoordinator == address(0)) {
return activeNetwork;
}
vm.startBroadcast();
VRFCoordinatorV2Mock vrfCoordinator = new VRFCoordinatorV2Mock(baseFee, gasPriceLink);
vm.stopBroadcast();
return NetworkConfig({
vrfCoordinator: address(vrfCoordinator),
link: 0x779877A7B0D9E8603169DdbD7836e478b4624789,
keyHash: 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c,
subId: 0,
callbackGasLimit: 500000
});
}
}
Steps to Reproduce
forge test --mt testFlip -vvvvv --fork-url $SEPOLIA_TESTNET
Returned error
[FAIL. Reason: ] testFlip() (gas: 32112)
Traces:
[6275867] CoinFlipTest::setUp()
├─ [3927161] → new DeployCoinFlip@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
│ └─ ← 19503 bytes of code
├─ [2194211] DeployCoinFlip::run()
│ ├─ [1840802] → new HelperConfig@0x104fBc016F4bb334D775a19E8A6510109AC63E00
│ │ └─ ← 8529 bytes of code
│ ├─ [795] HelperConfig::activeNetwork() [staticcall]
│ │ └─ ← 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625, 0x779877A7B0D9E8603169DdbD7836e478b4624789, 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c, 8119, 500000 [5e5]
│ ├─ [0] VM::startBroadcast()
│ │ └─ ← ()
│ ├─ [280223] → new CoinFlip@0x90193C961A926261B756D1E5bb255e67ff9498A1
│ │ └─ ← 1396 bytes of code
│ ├─ [0] VM::stopBroadcast()
│ │ └─ ← ()
│ └─ ← CoinFlip: [0x90193C961A926261B756D1E5bb255e67ff9498A1], HelperConfig: [0x104fBc016F4bb334D775a19E8A6510109AC63E00]
├─ [795] HelperConfig::activeNetwork() [staticcall]
│ └─ ← 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625, 0x779877A7B0D9E8603169DdbD7836e478b4624789, 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c, 8119, 500000 [5e5]
└─ ← ()
[32112] CoinFlipTest::testFlip()
├─ [0] VM::deal(player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C], 10000000000000000000 [1e19])
│ └─ ← ()
├─ [0] VM::prank(player: [0x44E97aF4418b7a17AABD8090bEA0A471a366305C])
│ └─ ← ()
├─ [12741] CoinFlip::flip{value: 1000000000000000000}(0)
│ ├─ [7390] 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625::requestRandomWords(0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c, 8119, 3, 500000 [5e5], 1)
│ │ └─ ←
│ └─ ←
└─ ←
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with CoinFlipTest.t.sol and reproduce testFlip using the listed forge command and Sepolia fork. Trace CoinFlip.flip into VRFCoordinatorV2Interface.requestRandomWords, then compare the parameters with HelperConfig.s.sol and the deployment setup. Done means the request completes with a visible failure reason or returns a request ID as expected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- solidity
- Domain
- blockchain
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100