Solution: Part 3
Build our own play-to earn game.
We'll cover the following...
Final derived contract
Next, we'll define our final contract, which will inherit from TreasureHuntVRF.sol
(and thus implicitly from TreasureHuntNoVRF.sol
). Here, we'll implement the constructor, various functions to handle gameplay and payments, as well as OpenZeppelin design patterns.
Press + to interact
input.sol
TreasureHuntNoVRF.sol
TreasureHuntVRF.sol
// SPDX-License-Identifier: MITpragma solidity ^0.8.14;/// @author ndehouche/// @title TreasureHuntimport "./TreasureHuntVRF.sol";import "@openzeppelin/contracts/access/Ownable.sol";import "@openzeppelin/contracts/security/Pausable.sol";import "@openzeppelin/contracts/security/ReentrancyGuard.sol";contract TreasureHunt is Ownable, Pausable, ReentrancyGuard, TreasureHuntVRF {/*** @notice Constructor inherits VRFConsumerBaseV2** @param subscriptionId subscription id that this consumer contract can use*/constructor(address _vrfCoordinator,bytes32 _s_keyHash,uint64 subscriptionId,address _tokenContract,uint _fee,uint _seasonStart,uint _seasonEnd)VRFConsumerBaseV2(_vrfCoordinator) {COORDINATOR = VRFCoordinatorV2Interface(_vrfCoordinator);s_keyHash=_s_keyHash;s_subscriptionId = subscriptionId;tokenContract = _tokenContract;fee=_fee;seasonStart=_seasonStart;seasonEnd=_seasonEnd;}/*** @notice Requests randomness* Will revert if subscription is not set and funded.*/function flipCoin(uint _tokenId) public payableonlyIfSeasonOpenonlyIfPaidEnoughonlyIfNotLocked(_tokenId)onlyIfHoldsToken(_tokenId)onlyIfNotWon(_tokenId)whenNotPausedreturns (uint requestId){require(s_results[_tokenId] != 42, 'Already flipped');if (stage[_tokenId]==0){participants.push(_tokenId);}stage[_tokenId]++;requestId = COORDINATOR.requestRandomWords(s_keyHash,s_subscriptionId,requestConfirmations,callbackGasLimit,numWords);s_players[requestId] = _tokenId;s_results[_tokenId] = FLIP_IN_PROGRESS;emit CoinFlipped(requestId, msg.sender);}function getResult(uint _tokenId) public view returns (uint result){return(s_results[_tokenId]);}function getCurrentStage(uint _tokenId) public view returns (uint _stage){return(stage[_tokenId]);}// Computing the number of winners at the end of the season// The owner has to call this function before withdrawing treasury,// but it can be called by anyone, as a backup.function computeNumWinners() publiconlyIfSeasonEnded{if (numWinners==0){for (uint i=0;i<participants.length;i++){if (stage[participants[i]]==6&&s_results[participants[i]]==2 ){numWinners++;}}}}// Player prizes withdrawal functionfunction withdrawPrize(uint _tokenId) publiconlyIfSeasonEndedonlyIfNotZero(numWinners)onlyIfHoldsToken(_tokenId)onlyIfWon(_tokenId)whenNotPausednonReentrant{(bool sent, ) = msg.sender.call{value: address(this).balance*7/(10*numWinners)}("");require(sent, "Failed to send Ether");}// Treasury withdrawal function// Forces the owner to allow player prizes withdrawal before withdrawing the treasuryfunction withdrawTreasury() publiconlyOwneronlyIfSeasonEndedwhenNotPausednonReentrant{ computeNumWinners();(bool sent, ) = msg.sender.call{value: address(this).balance*3/10}("");require(sent, "Failed to send Ether");}// Remaining balance withdrawal function// If there are no winners at the end of season, after calling computeNumWinners(),// the remaining balance is withdrawn by the ownerfunction withdrawRemainingBalance() publiconlyOwneronlyIfSeasonEndedwhenNotPausednonReentrant{ computeNumWinners();if (numWinners==0){(bool sent, ) = msg.sender.call{value: address(this).balance}("");require(sent, "Failed to send Ether");}}// @dev Toggle pause booleanfunction togglePause() external onlyOwner {if (paused()) {_unpause();}else _pause();}/// @dev Receive functionreceive() external payable {}/// @dev Fallback function. We check data length in fallback functions as a best practicefallback() external payable {require(msg.data.length == 0);}}
Constructor
The constructor of our contract is implemented in lines 15–31. In addition to setting the value of the VRF parameters, we use it to set the address of the NFT contract gating our game, the fee paid by players at each level, and the start and end date of the current season, in lines 27–30.
Design patterns
In ...