Feeling Lucky Smart Contract
Feeling Lucky Smart Contract
The Feeling Lucky system utilizes smart contracts to operate a transparent, fair, and decentralized game directly on the Base blockchain, en suring the establishment of trust and maintenance of integrity. This system is structured around a sequence of 'rounds', with each round representing a distinct game iteration.
Participants have the opportunity to enter these rounds by placing deposits (ETH, ERC-721 or ERC-20 tokens). The probability of a participant winning is directly proportional to the portion of the total pool they have deposited. Each deposit value is calculated, and each participant is allocated a number of entry spots proportional to the value deposited. Winners are selected at the end of each round.
The Feeling Lucky contract depends on several external oracles: Chainlink for ensuring fair randomness, Reservoir for ERC-721 pricing information, and Uniswap for ERC-20 pricing data.
Please note: The valuation of all NFTs is the floor price of the collection; rarity is not taken into consideration.
Functions
The contract encompasses a range of features for engaging with the Feeling Lucky system. These functionalities encompass actions like depositing into the current round (deposit), drawing the winners (drawWinner), claiming the prizes (claimPrizes), cancelling a round if less than two addresses deposited (cancel), withdrawing the deposit if cancelled (withdrawDeposits) and cancelling a round (if less than two addresses entered).
Events
Events are generated to capture different actions within the system, including actions like making deposits, withdrawing deposits, updating round statuses, claiming prizes, and sending randomness requests, among others.
Deployed contracts addresses
Base Mainnet
FeelingLucky
0xFbCe94A2686B8315208b39045E77B3a1Cf56D1De
TransferManager
0xF6A78b7e55dE9c5503848FbCB483B84a8824aDd5
PriceOracle
0x1d67FA31AbfB60b368004b4B038AE7845D1BcCC4
Table of content
This page includes the following sections.
Feeling Lucky System Overview
The contract is initialised with the rounds configurations: These values define things like the duration of each round (roundDuration), the limits on participants per round and deposits (maximumNumberOfDepositsPerRound/maximumNumberOfParticipantsPerRound) and the various oracles addresses and details.
The contract owner can adjust these values.
The round is started: The status is set to
Open. At this point, people can deposit assets and currencies and receive entries equivalent to the total value of the assets deposited. The Reservoir and Uniswap oracles are used to calculate the assets' value. The price is fetched only with the first deposit, and it will stay the same for the whole round.The round is successfully completed: The status is set to
Drawing. The randomness request is sent to Chainlink; this can take a few minutes to complete based on the network status.The randomness request is completed: The status is set to
Drawn. The value returned by Chainlink is used to select the winner of the last round.
The round does not reach two participants: The status is set to
Cancelled. The deposits can be withdrawn by callingwithdrawDepositsorrolled over to the next round when cancelling a round the current by callingcancelCurrentRoundAndDepositToTheNextRound.
After a round is completed or cancelled, a new one is started.
Feeling Lucky configuration
When the contract is first deployed, there are a set of parameters that are required within the constructor and govern the logic of each round. Most of these values can be updated by the contract owner.
Here is the ConstructorCalldata struct:
Copy
struct ConstructorCalldata {
// The following values can be updated after the contract has been deployed
address owner;
address operator;
uint40 maximumNumberOfDepositsPerRound;
uint40 maximumNumberOfParticipantsPerRound;
uint40 roundDuration;
uint256 valuePerEntry;
address protocolFeeRecipient;
uint16 protocolFeeBp;
address reservoirOracle;
address erc20Oracle;
uint40 signatureValidityPeriod;
// The following values cannot be updated after the contract has been deployed
bytes32 keyHash;
uint64 subscriptionId;
address vrfCoordinator;
address weth;
address transferManager;
}Definitions
owner: The contract’s owner can update all the values outlined above except the currencies allowlist.operator: The contract’s operator can update the allowed currencies.maximumNumberOfDepositsPerRound: The maximum number of deposits per round.maximumNumberOfParticipantsPerRound: The maximum number of participants per round.roundDuration: The duration of each round in seconds. It must be at most 1 hour.valuePerEntry: The value in ETH of one entry.protocolFeeRecipient: The address which will receive the protocol fee.protocolFeeBp: The protocol fee in bps, taken from the total value of a round. At most 2500 (25%).reservoirOracle: The address of Reservoir’s price oracle. erc20Oracle: The address of Uniswap’s price oracle.signatureValidityPeriod: The time in seconds that a given Reservoir signature will be valid for.keyHash/subscriptionId/vrfCoordinator: Chainlinks configuration variables, see Random Numbers: Using Chainlink VRF for more details.weth: TheWETHaddress. Used for the protocol fee transfer function in case theETHtransfer fails.transferManager: The Transfer Manager contract’s address. Used to transfer the assets. For more details, see the transfer manager contract interface. Approvals must be granted to this contract by the participants.
As soon as the contract is deployed, a round will start.
Events emitted
ERC20OracleUpdated(address erc20Oracle)MaximumNumberOfDepositsPerRoundUpdated(uint40 maximumNumberOfDepositsPerRound)MaximumNumberOfParticipantsPerRoundUpdated(uint40 maximumNumberOfParticipantsPerRound)ProtocolFeeBpUpdated(uint16 protocolFeeBp)ProtocolFeeRecipientUpdated(address protocolFeeRecipient)ReservoirOracleUpdated(address reservoirOracle)RoundDurationUpdated(uint40 roundDuration)SignatureValidityPeriodUpdated(uint40 signatureValidityPeriod)ValuePerEntryUpdated(uint256 valuePerEntry)RoundStatusUpdated(roundId, RoundStatus.Open)
Deposit assets
Once a round has started, users can deposit one or more assets/currencies (ETH/ERC-20/ERC-721) by calling the function deposit(uint256 roundId, DepositCalldata[] calldata deposits). This function accepts a roundId and an array of DepositCalldata. Not needed for ETH deposits.
The DepositCalldata struct includes the following properties:
tokenType: Either 1 forERC-20or 2 forERC-721(ETHdoes not need a DepositCalldata to be provided, the value sent alongside the transaction will be automatically deposited asETH).tokenAddress: The asset’s address.tokenIdsOrAmounts: The asset’s ids forERC-721or the amount forERC-20.If the type is
ERC-20the tokenIdsOrAmounts length must be 1.
reservoirOracleFloorPrice: An object of type ReservoirOracleFloorPrice that can be retrieved via the Reservoir API (see the section below for more details) with the following properties:id: The response’s id.payload: The response’s payload.timestamp: The response’s timestamp.signature: The response’s signature.
How to retrieve the floor price data via the Reservoir API
The signed collection’s floor price data can be retrieved via the Reservoir API.
Use the Collection floor endpoint.
Set the kind to TWAP.
Set the twapSeconds to 86400.
Set the useNonFlaggedFloorAsk to false.
DepositCalldata struct:
struct ReservoirOracleFloorPrice {
bytes32 id;
bytes payload;
uint256 timestamp;
bytes signature;
}ReservoirOracleFloorPrice struct:
struct DepositCalldata {
TokenType tokenType;
address tokenAddress;
uint256[] tokenIdsOrAmounts;
ReservoirOracleFloorPrice reservoirOracleFloorPrice;
}Please note: All NFTs in a collection will be valued at the floor price of the collection; rarity is not accounted for.
Events emitted
Deposited(msg.sender, roundId, totalEntriesCount)RandomnessRequested(roundId, requested)if the deposit reached the limit per roundRoundStatusUpdated(roundId, RoundStatus.Drawing)if the deposit reached the limit per round
Draw winner
Anyone can trigger for the winner to be drawn if the round’s cutoffTime has been reached but not the maximum participants/deposits threshold, as long as there are at least 2 participants. This can be achieved by calling the function drawWinner().
Events emitted
RandomnessRequested(roundId, requested)RoundStatusUpdated(roundId, RoundStatus.Drawing)
Claim prizes
Once a round has been completed successfully, the winner can claim the prizes by calling the function claimPrizes(ClaimPrizesCalldata[] calldata claimPrizesCalldata); it can be used to claim multiple rounds at once. The function accepts an array of ClaimPrizesCalldata.
The ClaimPrizesCalldata struct includes the following properties:
roundId: The id of the round won.prizeIndices: An array of indices of prizes to claim.
ClaimPrizesCalldata struct:
struct ClaimPrizesCalldata {
uint256 roundId;
uint256[] prizeIndices;
}Events emitted
PrizesClaimed(perRoundClaimPrizesCalldata.roundId, msg.sender, prizeIndices)
Cancel a round
Anyone can cancel a round if one of the following two conditions are met:
The round’s
cutoffTimehas been reached, and there are less than 2 participants.The randomness request to Chainlink has not been fulfilled within 12 minutes.
There are 3 different cancel functions:
cancel()cancelCurrentRoundAndDepositToTheNextRound(DepositCalldata[] calldata deposits)cancelAfterRandomnessRequest()
The cancelCurrentRoundAndDepositToTheNextRound function requires a DepositCalldata array.
For more details on DepositCalldata, see the deposit assets section.
Events emitted
RoundStatusUpdated(roundId, RoundStatus.Cancelled)current round cancelledRoundStatusUpdated(roundId, RoundStatus.Open)new round startedDeposited(msg.sender, roundId, totalEntriesCount)if the deposit has been rolled over.
Withdraw deposits
After a round has been cancelled, the participants can withdraw their deposits by calling withdrawDeposits(uint256 roundId, uint256[] calldata depositIndices).
The function accepts a roundId and an array of deposit indices.
Events emitted
DepositsWithdrawn(roundId, msg.sender, depositIndices)
Variables update
The contract has a group of functions which are used to update some of the variables initially set at deployment. See Feeling Lucky configuration for more details.
Update Functions
updateRoundDuration
uint40 _roundDuration
owner
updateSignatureValidityPeriod
uint40 _signatureValidityPeriod
owner
updateValuePerEntry
uint256 _valuePerEntry
owner
updateProtocolFeeRecipient
address _protocolFeeRecipient
owner
updateProtocolFeeBp
uint16 _protocolFeeBp
owner
updateMaximumNumberOfDepositsPerRound
uint40 _maximumNumberOfDepositsPerRound
owner
updateMaximumNumberOfParticipantsPerRound
uint40 _maximumNumberOfParticipantsPerRound
owner
updateReservoirOracle
address _reservoirOracle
owner
updateERC20Oracle
address _erc20Oracle
owner
updateTokensStatus
address[] tokens, uint tokenType, bool isAllowed
operator
Last updated

