Skip to main content

Lottery Features

Example lottery contract

Player

purchase

function purchase(Ticket[] calldata tickets, address beneficiary) external;

Anyone can execute this method

While a lottery is running (isGameActive() == true), users may purchase new tickets using the purchase function. The purchase function uses the prizeToken ERC-20 token for payment, so users must give allowance to the contract to move the ticket prize amount.

Alternatively, users can use the LooteryETHAdapter contract to pay with the native token if the lottery uses the native wrapped token.

Parameters

ArgumentDescriptionExample input
valueticketPrize * number of tickets (ETH adapter only)99000000000000000000 (99 DEGEN per ticket)
looteryAddressThe address of the lottery (ETH adapter only)0x8958e46ddc4c7f16a0379f1e4d007f958f607e3d
ticketsA list of tickets to purchase. Array of tuples with [beneficiaries, pick][ [ "0xe737e1fd3bf4593b340fef896d77398faa2e4487", [ 5, 6, 11, 13, 14 ] ] ]

Example transaction.

Additional notes:

  • LooteryETHAdapter: The ETH adapter contract enables you to use the native currency of the chain you have deployed to, such as ETH on Ethereum, instead of needing to use ERC-20 tokens exclusively.
  • Tickets: the whomst argument here refers to the receiver/beneficiary of the ticket, and pick represents the sorted array of numbers that the user picks for the lottery ticket. The count of numbers needs to be exactly pickLength, with the maximum possible number set at maxBallValue, no repeat numbers, and sorted from lowest to highest.

claimWinnings

function claimWinnings(uint256 tokenId) public;

Anyone can execute this method, also for foreign tickets (= claim for a user)

Users may claim a prize for a ticket at any point. The win condition is either:

  • Having a winning ticket (all numbers match the winning numbers from the last draw); or
  • If the last round was “apocalypse mode”, and there was no winner, then any ticket holder from any round may claim an equal share of the prize pool (a "consolation prize") by burning their ticket NFT (see the kill function for more information about apocalypse mode).

The user calls claimWinnings with a token ID which either sends the ERC-20 prize, or reverts if the ticket didn’t win. The ticket NFT is burnt if the consolation prize is claimed after apocalypse mode.

To determine if a ticket is a winning ticket, the user computes the ticket pick ID and then compares it to the last draw's winningPickId.

Owner/operator

kill

function kill() public;

Only owner can execute this method

The owner can decide to kill the lottery at any point by calling the kill function, initiating what we like to call “apocalypse mode”.

This means that the current round will be the final round regardless of whether there are winners or not. In the case that there is no winner, all players will be able to claim an equal share of the jackpot via claimWinnings.

Withdraw operations: withdrawAccruedFees, rescueETH, rescueTokens

Only owner can execute these methods

The owner can rescue native tokens and ERC-20s using rescueETH and rescueTokens. It is not possible to rescue the token set as the prizeToken!

The owner can also withdraw accrued fees (accruedCommunityFees) using withdrawAccruedFees.

Permissionless

seedJackpot

function seedJackpot(uint256 value) public;

Anyone can execute this method

The jackpot can be topped up by anyone, provided that the lottery has not activated a cooldown period.

Alternatively, the jackpot can be seeded with native tokens via LooteryETHAdapter#seedJackpot.

draw

function draw() public;

Anyone can execute this method

When the current round is over, anyone can execute the draw method to draw the random numbers and finalise the round.

Note that the round ends when gameData[currentGame.id].startedAt + gamePeriod < block.timestamp.

The contract must have native currency available to pay for the VRF request. If it doesn’t have enough funds, or the round is not over yet, this transaction reverts.

Helpful view functions

Determine if a round had a winner

  1. Get the winning pick ID of the round $winningId = gameData[$gameId].winningPickId
  2. Get the token ID of the first winning ticket tokenByPickIdentity($gameId, $winningId, 0)
  3. If tokenByPickIdentity is zero there is no winner. Otherwise loop through all winning IDs by incrementing the last parameter and find all winning token IDs
  4. Call ownerOf with winning token IDs to determine the winner addresses, or claim for them with claimWinnings

ERC721 Interface

Every ticket is a regular ERC721 NFT and therefore can be transferred or sold on a marketplace.

Ownable Interface

The Lootery contract implements the standard Ownable interface, so ownership can be transferred or renounced.

Lottery indexer

We provide a GraphQL API to query lottery data.

Get list of tickets that a user purchased in a given round

query tickets($gameId: String!, $whomst: String!) {
tickets(where: { whomstId: $whomst, gameId: $gameId }) {
items {
tokenId
picks
}
}
}

where:

  • $whomst is the user address (in checksummed format, e.g. 0x8958e46Ddc4C7f16A0379F1e4D007f958F607E3d)
  • $gameId is ID of the game, in the format ${lotteryId}-${roundId} (e.g. "0x8958e46Ddc4C7f16A0379F1e4D007f958F607E3d-0")

Get winning numbers of a round

query winningPicks($lotteryId: String!, $gameId: String!) {
lootery(id: $lotteryId) {
id
games(where: { id: $gameId }) {
items {
id
gameId
winningPicks
}
}
}
}

where:

  • $lotteryId = Lottery address (in checksummed format, eg 0x8958e46Ddc4C7f16A0379F1e4D007f958F607E3d)
  • $gameId = ID of the game, in the format ${lotteryId}-${roundId} (e.g. "0x8958e46Ddc4C7f16A0379F1e4D007f958F607E3d-0")
  • winningPicks is the list of winning numbers.