Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lottery #2

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions contracts/MojitoLotteryPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// SPDX-License-Identifier: MIT

pragma solidity =0.6.12;

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "./interfaces/IMojitoToken.sol";
import "./interfaces/IMojitoSwapLottery.sol";
import "./Schedule.sol";

contract MojitoLotteryPool is Schedule, ReentrancyGuard {
using SafeMath for uint256;
using SafeERC20 for IERC20;

// The Mojito token
IMojitoToken public mojito;
uint256 public startBlock;
uint256 public totalAllocPoint = 0;

address public operator;
address public injector;

struct PoolInfo {
IMojitoSwapLottery lottery;
uint256 allocPoint;
uint256 lastRewardBlock;
uint256 pendingAmount; // pending MJT
uint256 totalInject; // inject MJT
}

PoolInfo[] public poolInfo;
mapping(uint256 => mapping(uint256 => uint256)) public injectInfo;

event AdminTokenRecovery(address token, uint256 amount);
event OperatorUpdate(address indexed from, address to);
event InjectorUpdate(address indexed from, address to);
event InjectPool(uint256 indexed pid, uint256 lotteryId, uint256 amount);
event InjectPending(uint256 indexed pid, uint256 amount);

constructor(
IMojitoToken _mojito,
uint256 _mojitoPerBlock,
uint256 _startBlock
) public Schedule(_mojitoPerBlock) {
mojito = _mojito;
startBlock = _startBlock;
}

modifier onlyOwnerOrOperator() {
require(owner() == _msgSender() || operator == _msgSender(), "not owner or operator");
_;
}

modifier onlyInjector() {
require(_msgSender() == injector, "not injector");
_;
}

function setOperator(address _operator) public onlyOwner {
require(_operator != address(0), "setOperator:zero address");
address pre = operator;
operator = _operator;
emit OperatorUpdate(pre, operator);
}

function setInjector(address _injector) public onlyOwner {
require(_injector != address(0), "setInjector:zero address");
address pre = injector;
injector = _injector;
emit InjectorUpdate(pre, injector);
}

function setMojitoPerBlock(uint256 _mojitoPerBlock) public virtual override onlyOwner {
massUpdatePools();
super.setMojitoPerBlock(_mojitoPerBlock);
}

function add(IMojitoSwapLottery _lottery, uint256 _allocPoint, bool withUpdate) public onlyOwner {
checkPoolDuplicate(_lottery);

if (withUpdate) {
massUpdatePools();
}

mojito.approve(address(_lottery), uint256(- 1));

totalAllocPoint = totalAllocPoint.add(_allocPoint);
uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
poolInfo.push(PoolInfo({
lottery : _lottery,
allocPoint : _allocPoint,
lastRewardBlock : lastRewardBlock,
pendingAmount : 0,
totalInject : 0
}));
}

function set(uint256 _pid, uint256 _allocPoint) public onlyOwner {
massUpdatePools();
totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
poolInfo[_pid].allocPoint = _allocPoint;
}

function massUpdatePools() public {
uint256 length = poolInfo.length;
for (uint256 pid = 0; pid < length; ++pid) {
updatePool(pid);
}
}

function updatePool(uint256 _pid) public {
PoolInfo storage pool = poolInfo[_pid];
if (block.number <= pool.lastRewardBlock) {
return;
}

uint256 blockReward = mintable(pool.lastRewardBlock);
uint256 mojitoReward = blockReward.mul(pool.allocPoint).div(totalAllocPoint);
mojito.mint(address(this), mojitoReward);
pool.pendingAmount = pool.pendingAmount.add(mojitoReward);
pool.lastRewardBlock = block.number;
}

function injectPending(uint256 _pid, uint256 _amount) public onlyInjector {
PoolInfo storage pool = poolInfo[_pid];
mojito.transferFrom(_msgSender(), address(this), _amount);
pool.pendingAmount = pool.pendingAmount.add(_amount);
emit InjectPending(_pid, _amount);
}

function injectPool(uint256 _pid, bool withUpdate) public onlyOwnerOrOperator {
if (withUpdate) {
// update pendingAmount
updatePool(_pid);
}

PoolInfo storage pool = poolInfo[_pid];

uint256 currentLotteryId = pool.lottery.viewCurrentLotteryId();
pool.lottery.injectFunds(currentLotteryId, pool.pendingAmount);

uint256 prePending = pool.pendingAmount;
pool.totalInject = pool.totalInject.add(pool.pendingAmount);
injectInfo[_pid][currentLotteryId] = injectInfo[_pid][currentLotteryId].add(pool.pendingAmount);

pool.pendingAmount = 0;
emit InjectPool(_pid, currentLotteryId, prePending);
}

function checkPoolDuplicate(IMojitoSwapLottery _lottery) internal view {
uint256 length = poolInfo.length;
for (uint256 pid = 0; pid < length; ++pid) {
require(poolInfo[pid].lottery != _lottery, "existing pool");
}
}

function poolLength() external view returns (uint256) {
return poolInfo.length;
}

function withdrawExtraToken() public onlyOwner {
uint256 pending = totalPending();
uint256 balance = mojito.balanceOf(address(this));
// balance >= pending
uint256 amount = balance.sub(pending);
mojito.transfer(_msgSender(), amount);
emit AdminTokenRecovery(address(mojito), amount);
}

function recoverWrongTokens(address _tokenAddress, uint256 _tokenAmount) external onlyOwner {
require(_tokenAddress != address(mojito), "cannot be mojito token");

IERC20(_tokenAddress).safeTransfer(_msgSender(), _tokenAmount);

emit AdminTokenRecovery(_tokenAddress, _tokenAmount);
}

function totalPending() public view returns (uint256) {
uint256 pending;
uint256 length = poolInfo.length;
for (uint256 pid = 0; pid < length; ++pid) {
pending = pending.add(poolInfo[pid].pendingAmount);
}

return pending;
}
}

92 changes: 92 additions & 0 deletions contracts/interfaces/IMojitoSwapLottery.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;

interface IMojitoSwapLottery {
/**
* @notice Buy tickets for the current lottery
* @param _lotteryId: lotteryId
* @param _ticketNumbers: array of ticket numbers between 1,000,000 and 1,999,999
* @dev Callable by users
*/
function buyTickets(uint256 _lotteryId, uint32[] calldata _ticketNumbers) external;

/**
* @notice Claim a set of winning tickets for a lottery
* @param _lotteryId: lottery id
* @param _ticketIds: array of ticket ids
* @param _brackets: array of brackets for the ticket ids
* @dev Callable by users only, not contract!
*/
function claimTickets(
uint256 _lotteryId,
uint256[] calldata _ticketIds,
uint32[] calldata _brackets
) external;

/**
* @notice Close lottery
* @param _lotteryId: lottery id
* @dev Callable by operator
*/
function closeLottery(uint256 _lotteryId) external payable;

/**
* @notice Draw the final number, calculate reward in MJT per group, and make lottery claimable
* @param _lotteryId: lottery id
* @param _autoInjection: reinjects funds into next lottery (vs. withdrawing all)
* @dev Callable by operator
*/
function drawFinalNumberAndMakeLotteryClaimable(uint256 _lotteryId, bool _autoInjection) external;

/**
* @notice Inject funds
* @param _lotteryId: lottery id
* @param _amount: amount to inject in MJT token
* @dev Callable by operator
*/
function injectFunds(uint256 _lotteryId, uint256 _amount) external;

/**
* @notice Start the lottery
* @dev Callable by operator
* @param _endTime: endTime of the lottery
* @param _priceTicketInMJT: price of a ticket in MJT
* @param _discountDivisor: the divisor to calculate the discount magnitude for bulks
* @param _rewardsBreakdown: breakdown of rewards per bracket (must sum to 10,000)
* @param _treasuryFee: treasury fee (10,000 = 100%, 100 = 1%)
*/
function startLottery(
uint256 _endTime,
uint256 _priceTicketInMJT,
uint256 _discountDivisor,
uint256[6] calldata _rewardsBreakdown,
uint256 _treasuryFee
) external;

/**
* @notice View current lottery id
*/
function viewCurrentLotteryId() external view returns (uint256);

/**
* @notice View user ticket ids, numbers, and statuses of user for a given lottery
* @param _user: user address
* @param _lotteryId: lottery id
* @param _cursor: cursor to start where to retrieve the tickets
* @param _size: the number of tickets to retrieve
*/
function viewUserInfoForLotteryId(
address _user,
uint256 _lotteryId,
uint256 _cursor,
uint256 _size
)
external
view
returns (
uint256[] memory,
uint32[] memory,
bool[] memory,
uint256
);
}
39 changes: 39 additions & 0 deletions contracts/test/MojitoLotteryMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT

pragma solidity =0.6.12;

import "../interfaces/IMojitoToken.sol";

contract MojitoLotteryMock {
uint256 lotteryId;
IMojitoToken mojito;

constructor(IMojitoToken _mojito) public {
mojito = _mojito;
}

function viewCurrentLotteryId() external view returns (uint256) {
return lotteryId;
}

function startLottery(
uint256 _endTime,
uint256 _priceTicketInMJT,
uint256 _discountDivisor,
uint256[6] calldata _rewardsBreakdown,
uint256 _treasuryFee
) external {
_endTime;
_priceTicketInMJT;
_discountDivisor;
uint256 length = _rewardsBreakdown.length;
length;
_treasuryFee;
lotteryId++;
}

function injectFunds(uint256 _lotteryId, uint256 _amount) external {
_lotteryId;
mojito.transferFrom(address(msg.sender), address(this), _amount);
}
}
Loading