Lottery Contract

Contract roles:

Role
Description
injectorAddress (onlyInjector)
Injector is the address used to fund the lottery with periodic injections
operatorAddress (onlyOperator)
The lottery scheduler account used to run regular operations.
treasuryAddress (onlyTreasury)
The address in which the burn is sent
Owner (onlyOwner)
The contract owner

Owner

Address controlled by gnosis multisignature contract with a threshold of 3/6

Operator Address

Scheduler address - entirely automated and no human interaction. Not on multisig and doesn't have access to sensitive contract operations.

Treasury Address

Address controlled by gnosis multisignature contract with a threshold of 3/6

Injector Address (Currently the same as Owner)

Address controlled by gnosis multisignature contract with a threshold of 3/6

Functions

injectFunds - Injector and Owner

1
function injectFunds(uint256 _lotteryId, uint256 _amount) external override onlyOwnerOrInjector {
2
require(_lotteries[_lotteryId].status == Status.Open, "Lottery not open");
3
4
wtnToken.safeTransferFrom(address(msg.sender), address(this), _amount);
5
_lotteries[_lotteryId].amountCollectedInWTN += _amount;
6
7
emit LotteryInjection(_lotteryId, _amount);
8
}
Copied!
The Injector or Owner can call this function to inject a specific lotteryId with a specified amount of WTN.

startLottery - Operator

1
function startLottery(
2
uint256 _endTime,
3
uint256 _priceTicketInWTN,
4
uint256 _discountDivisor,
5
uint256[6] calldata _rewardsBreakdown,
6
uint256 _treasuryFee
7
) external override onlyOperator {
8
require(
9
(currentLotteryId == 0) || (_lotteries[currentLotteryId].status == Status.Claimable),
10
"Not time to start lottery"
11
);
12
13
require(
14
((_endTime - block.timestamp) > MIN_LENGTH_LOTTERY) && ((_endTime - block.timestamp) < MAX_LENGTH_LOTTERY),
15
"Lottery length outside of range"
16
);
17
18
require(
19
(_priceTicketInWTN >= minPriceTicketInWTN) && (_priceTicketInWTN <= maxPriceTicketInWTN),
20
"Outside of limits"
21
);
22
23
require(_discountDivisor >= MIN_DISCOUNT_DIVISOR, "Discount divisor too low");
24
require(_treasuryFee <= MAX_TREASURY_FEE, "Treasury fee too high");
25
26
require(
27
(_rewardsBreakdown[0] +
28
_rewardsBreakdown[1] +
29
_rewardsBreakdown[2] +
30
_rewardsBreakdown[3] +
31
_rewardsBreakdown[4] +
32
_rewardsBreakdown[5]) == 10000,
33
"Rewards must equal 10000"
34
);
35
36
currentLotteryId++;
37
38
_lotteries[currentLotteryId] = Lottery({
39
status: Status.Open,
40
startTime: block.timestamp,
41
endTime: _endTime,
42
priceTicketInCake: _priceTicketInWTN,
43
discountDivisor: _discountDivisor,
44
rewardsBreakdown: _rewardsBreakdown,
45
treasuryFee: _treasuryFee,
46
wtnPerBracket: [uint256(0), uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)],
47
countWinnersPerBracket: [uint256(0), uint256(0), uint256(0), uint256(0), uint256(0), uint256(0)],
48
firstTicketId: currentTicketId,
49
firstTicketIdNextLottery: currentTicketId,
50
amountCollectedInWTN: pendingInjectionNextLottery,
51
finalNumber: 0
52
});
53
54
emit LotteryOpen(
55
currentLotteryId,
56
block.timestamp,
57
_endTime,
58
_priceTicketInWTN,
59
currentTicketId,
60
pendingInjectionNextLottery
61
);
62
63
pendingInjectionNextLottery = 0;
64
}
Copied!
The startLottery function is only callable by the Operator in order to start a new lottery round.

closeLottery - Operator

1
function closeLottery(uint256 _lotteryId) external override onlyOperator nonReentrant {
2
require(_lotteries[_lotteryId].status == Status.Open, "Lottery not open");
3
require(block.timestamp > _lotteries[_lotteryId].endTime, "Lottery not over");
4
_lotteries[_lotteryId].firstTicketIdNextLottery = currentTicketId;
5
6
// Request a random number from the generator based on a seed
7
randomGenerator.getRandomNumber(uint256(keccak256(abi.encodePacked(_lotteryId, currentTicketId))));
8
9
_lotteries[_lotteryId].status = Status.Close;
10
11
emit LotteryClose(_lotteryId, currentTicketId);
12
}
Copied!
Callable by the Operator to close a round of the lottery.

drawFinalNumberAndMakeLotteryClaimable - Operator

1
function drawFinalNumberAndMakeLotteryClaimable(uint256 _lotteryId, bool _autoInjection)
2
external
3
override
4
onlyOperator
5
nonReentrant
6
{
7
require(_lotteries[_lotteryId].status == Status.Close, "Lottery not close");
8
require(_lotteryId == randomGenerator.viewLatestLotteryId(), "Numbers not drawn");
9
10
// Calculate the finalNumber based on the randomResult generated by ChainLink's fallback
11
uint32 finalNumber = randomGenerator.viewRandomResult();
12
13
// Initialize a number to count addresses in the previous bracket
14
uint256 numberAddressesInPreviousBracket;
15
16
// Calculate the amount to share post-treasury fee
17
uint256 amountToShareToWinners = (
18
((_lotteries[_lotteryId].amountCollectedInWTN) * (10000 - _lotteries[_lotteryId].treasuryFee))
19
) / 10000;
20
21
// Initializes the amount to withdraw to treasury
22
uint256 amountToWithdrawToTreasury;
23
24
// Calculate prizes in WTN for each bracket by starting from the highest one
25
for (uint32 i = 0; i < 6; i++) {
26
uint32 j = 5 - i;
27
uint32 transformedWinningNumber = _bracketCalculator[j] + (finalNumber % (uint32(10)**(j + 1)));
28
29
_lotteries[_lotteryId].countWinnersPerBracket[j] =
30
_numberTicketsPerLotteryId[_lotteryId][transformedWinningNumber] -
31
numberAddressesInPreviousBracket;
32
33
// A. If number of users for this _bracket number is superior to 0
34
if (
35
(_numberTicketsPerLotteryId[_lotteryId][transformedWinningNumber] - numberAddressesInPreviousBracket) !=
36
0
37
) {
38
// B. If rewards at this bracket are > 0, calculate, else, report the numberAddresses from previous bracket
39
if (_lotteries[_lotteryId].rewardsBreakdown[j] != 0) {
40
_lotteries[_lotteryId].wtnPerBracket[j] =
41
((_lotteries[_lotteryId].rewardsBreakdown[j] * amountToShareToWinners) /
42
(_numberTicketsPerLotteryId[_lotteryId][transformedWinningNumber] -
43
numberAddressesInPreviousBracket)) /
44
10000;
45
46
// Update numberAddressesInPreviousBracket
47
numberAddressesInPreviousBracket = _numberTicketsPerLotteryId[_lotteryId][transformedWinningNumber];
48
}
49
// A. No WTN to distribute, they are added to the amount to withdraw to treasury address
50
} else {
51
_lotteries[_lotteryId].wtnPerBracket[j] = 0;
52
53
amountToWithdrawToTreasury +=
54
(_lotteries[_lotteryId].rewardsBreakdown[j] * amountToShareToWinners) /
55
10000;
56
}
57
}
58
59
// Update internal statuses for lottery
60
_lotteries[_lotteryId].finalNumber = finalNumber;
61
_lotteries[_lotteryId].status = Status.Claimable;
62
63
if (_autoInjection) {
64
pendingInjectionNextLottery = amountToWithdrawToTreasury;
65
amountToWithdrawToTreasury = 0;
66
}
67
68
amountToWithdrawToTreasury += (_lotteries[_lotteryId].amountCollectedInWTN - amountToShareToWinners);
69
70
// Transfer WTN to treasury address
71
cakeToken.safeTransfer(treasuryAddress, amountToWithdrawToTreasury);
72
73
emit LotteryNumberDrawn(currentLotteryId, finalNumber, numberAddressesInPreviousBracket);
74
}
Copied!
For Operator to draw the final number using ChainLink VRF function.

recoverWrongTokens - Owner

1
function recoverWrongTokens(address _tokenAddress, uint256 _tokenAmount) external onlyOwner {
2
require(_tokenAddress != address(cakeToken), "Cannot be WTN token");
3
4
IERC20(_tokenAddress).safeTransfer(address(msg.sender), _tokenAmount);
5
6
emit AdminTokenRecovery(_tokenAddress, _tokenAmount);
7
}
8
Copied!
In the case of tokens other than WTN mistakenly being sent to the lottery contract, this function is used to recover them and is only callable by the Owner

setMinAndMaxTicketPriceInWTN - Owner

1
function setMinAndMaxTicketPriceInWTN(uint256 _minPriceTicketInWTN, uint256 _maxPriceTicketInWTN)
2
external
3
onlyOwner
4
{
5
require(_minPriceTicketInWTN <= _maxPriceTicketInWTN, "minPrice must be < maxPrice");
6
7
minPriceTicketInWTN = _minPriceTicketInWTN;
8
maxPriceTicketInWTN = _maxPriceTicketInWTN;
9
}
10
Copied!
To prevent the Operator setting the tickets to arbitrary prices during the event of a flash crash/pump.

setMaxNumberTicketsPerBuy - Owner

1
function setMaxNumberTicketsPerBuy(uint256 _maxNumberTicketsPerBuy) external onlyOwner {
2
require(_maxNumberTicketsPerBuy != 0, "Must be > 0");
3
maxNumberTicketsPerBuyOrClaim = _maxNumberTicketsPerBuy;
4
}
Copied!
The Owner can modify the maximum number of tickets per transaction. This may be modified in the case of BSC block size increasing or decreasing.

setOperatorAndTreasuryAndInjectorAddresses - Owner

1
function setOperatorAndTreasuryAndInjectorAddresses(
2
address _operatorAddress,
3
address _treasuryAddress,
4
address _injectorAddress
5
) external onlyOwner {
6
require(_operatorAddress != address(0), "Cannot be zero address");
7
require(_treasuryAddress != address(0), "Cannot be zero address");
8
require(_injectorAddress != address(0), "Cannot be zero address");
9
10
operatorAddress = _operatorAddress;
11
treasuryAddress = _treasuryAddress;
12
injectorAddress = _injectorAddress;
13
14
emit NewOperatorAndTreasuryAndInjectorAddresses(_operatorAddress, _treasuryAddress, _injectorAddress);
15
}
16
Copied!
Function used to set the Operator, Treasury, and Injector addresses.

changeRandomGenerator - Owner

1
function changeRandomGenerator(address _randomGeneratorAddress) external onlyOwner {
2
require(
3
(currentLotteryId == 0) || (_lotteries[currentLotteryId].status == Status.Claimable),
4
"Lottery not in claimable"
5
);
6
7
// Request a random number from the generator based on a seed
8
IRandomNumberGenerator(_randomGeneratorAddress).getRandomNumber(
9
uint256(keccak256(abi.encodePacked(currentLotteryId, currentTicketId)))
10
);
11
12
// Calculate the finalNumber based on the randomResult generated by ChainLink's fallback
13
IRandomNumberGenerator(_randomGeneratorAddress).viewRandomResult();
14
15
randomGenerator = IRandomNumberGenerator(_randomGeneratorAddress);
16
17
emit NewRandomGenerator(_randomGeneratorAddress);
18
}
Copied!
For the Owner to update the RandomNumberGenerator contract in case we need to update the drawing logic, or release an update.
Last modified 6mo ago