EGG MasterChef vs SALT MasterChef
62 removals
235 lines
65 additions
239 lines
// MasterChef is the master of Egg. He can make Egg and he is a fair guy.
//
// MasterChef is the master of Salt. He can make Salt and he is a fair guy.
//
//
// Note that it's ownable and the owner wields tremendous power. The ownership
// Note that it's ownable and the owner wields tremendous power. The ownership
// will be transferred to a governance smart contract once EGG is sufficiently
// will be transferred to a governance smart contract once SALT is sufficiently
// distributed and the community can show to govern itself.
// distributed and the community can show to govern itself.
//
//
// Have fun reading it. Hopefully it's bug-free. God bless.
// Have fun reading it. Hopefully it's bug-free. God bless.
contract MasterChef is Ownable {
contract MasterChef is Ownable {
using SafeMath for uint256;
using SafeMath for uint256;
using SafeBEP20 for IBEP20;
using SafeBEP20 for IBEP20;
// Info of each user.
// Info of each user.
struct UserInfo {
struct UserInfo {
uint256 amount; // How many LP tokens the user has provided.
uint256 amount; // How many LP tokens the user has provided.
uint256 rewardDebt; // Reward debt. See explanation below.
uint256 rewardDebt; // Reward debt. See explanation below.
//
//
// We do some fancy math here. Basically, any point in time, the amount of EGGs
// We do some fancy math here. Basically, any point in time, the amount of SALTs
// entitled to a user but is pending to be distributed is:
// entitled to a user but is pending to be distributed is:
//
//
// pending reward = (user.amount * pool.accEggPerShare) - user.rewardDebt
// pending reward = (user.amount * pool.accSaltPerShare) - user.rewardDebt
//
//
// Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:
// Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:
// 1. The pool's `accEggPerShare` (and `lastRewardBlock`) gets updated.
// 1. The pool's `accSaltPerShare` (and `lastRewardBlock`) gets updated.
// 2. User receives the pending reward sent to his/her address.
// 2. User receives the pending reward sent to his/her address.
// 3. User's `amount` gets updated.
// 3. User's `amount` gets updated.
// 4. User's `rewardDebt` gets updated.
// 4. User's `rewardDebt` gets updated.
}
}
// Info of each pool.
// Info of each pool.
struct PoolInfo {
struct PoolInfo {
IBEP20 lpToken; // Address of LP token contract.
IBEP20 lpToken; // Address of LP token contract.
uint256 allocPoint; // How many allocation points assigned to this pool. EGGs to distribute per block.
uint256 allocPoint; // How many allocation points assigned to this pool. SALTs to distribute per block.
uint256 lastRewardBlock; // Last block number that EGGs distribution occurs.
uint256 lastRewardBlock; // Last block number that SALTs distribution occurs.
uint256 accEggPerShare; // Accumulated EGGs per share, times 1e12. See below.
uint256 accSaltPerShare; // Accumulated SALTs per share, times 1e12. See below.
uint16 depositFeeBP; // Deposit fee in basis points
uint16 depositFeeBP; // Deposit fee in basis points
}
}
// The EGG TOKEN!
// The SALT TOKEN!
EggToken public egg;
SaltToken public salt;
// Dev address.
// Dev address.
address public devaddr;
address public devaddr;
// EGG tokens created per block.
// SALT tokens created per block.
uint256 public eggPerBlock;
uint256 public saltPerBlock;
// Bonus muliplier for early egg makers.
// Bonus muliplier for early salt makers.
uint256 public constant BONUS_MULTIPLIER = 1;
uint256 public constant BONUS_MULTIPLIER = 1;
// Deposit Fee address
// Deposit Fee address
address public feeAddress;
address public feeAddress;
// Info of each pool.
// Info of each pool.
PoolInfo[] public poolInfo;
PoolInfo[] public poolInfo;
// Info of each user that stakes LP tokens.
// Info of each user that stakes LP tokens.
mapping (uint256 => mapping (address => UserInfo)) public userInfo;
mapping (uint256 => mapping (address => UserInfo)) public userInfo;
// Total allocation points. Must be the sum of all allocation points in all pools.
// Total allocation points. Must be the sum of all allocation points in all pools.
uint256 public totalAllocPoint = 0;
uint256 public totalAllocPoint = 0;
// The block number when EGG mining starts.
// The block number when SALT mining starts.
uint256 public startBlock;
uint256 public startBlock;
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
constructor(
constructor(
EggToken _egg,
SaltToken _salt,
address _devaddr,
address _devaddr,
address _feeAddress,
address _feeAddress,
uint256 _eggPerBlock,
uint256 _saltPerBlock
uint256 _startBlock
) public {
) public {
egg = _egg;
salt = _salt;
devaddr = _devaddr;
devaddr = _devaddr;
feeAddress = _feeAddress;
feeAddress = _feeAddress;
eggPerBlock = _eggPerBlock;
saltPerBlock = _saltPerBlock;
startBlock = _startBlock;
}
}
function poolLength() external view returns (uint256) {
function poolLength() external view returns (uint256) {
return poolInfo.length;
return poolInfo.length;
}
}
function setStartBlock(uint256 _startBlock) public onlyOwner {
require(_startBlock == 0, "already started");
startBlock = _startBlock;
}
// Add a new lp to the pool. Can only be called by the owner.
// Add a new lp to the pool. Can only be called by the owner.
// XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do.
// XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do.
function add(uint256 _allocPoint, IBEP20 _lpToken, uint16 _depositFeeBP, bool _withUpdate) public onlyOwner {
function add(uint256 _allocPoint, IBEP20 _lpToken, uint16 _depositFeeBP, bool _withUpdate) public onlyOwner {
require(_depositFeeBP <= 10000, "add: invalid deposit fee basis points");
require(_depositFeeBP <= 10000, "add: invalid deposit fee basis points");
if (_withUpdate) {
if (_withUpdate) {
massUpdatePools();
massUpdatePools();
}
}
uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
totalAllocPoint = totalAllocPoint.add(_allocPoint);
totalAllocPoint = totalAllocPoint.add(_allocPoint);
poolInfo.push(PoolInfo({
poolInfo.push(PoolInfo({
lpToken: _lpToken,
lpToken: _lpToken,
allocPoint: _allocPoint,
allocPoint: _allocPoint,
lastRewardBlock: lastRewardBlock,
lastRewardBlock: lastRewardBlock,
accEggPerShare: 0,
accSaltPerShare: 0,
depositFeeBP: _depositFeeBP
depositFeeBP: _depositFeeBP
}));
}));
}
}
// Update the given pool's EGG allocation point and deposit fee. Can only be called by the owner.
// Update the given pool's SALT allocation point and deposit fee. Can only be called by the owner.
function set(uint256 _pid, uint256 _allocPoint, uint16 _depositFeeBP, bool _withUpdate) public onlyOwner {
function set(uint256 _pid, uint256 _allocPoint, uint16 _depositFeeBP, bool _withUpdate) public onlyOwner {
require(_depositFeeBP <= 10000, "set: invalid deposit fee basis points");
require(_depositFeeBP <= 10000, "set: invalid deposit fee basis points");
if (_withUpdate) {
if (_withUpdate) {
massUpdatePools();
massUpdatePools();
}
}
totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
poolInfo[_pid].allocPoint = _allocPoint;
poolInfo[_pid].allocPoint = _allocPoint;
poolInfo[_pid].depositFeeBP = _depositFeeBP;
poolInfo[_pid].depositFeeBP = _depositFeeBP;
}
}
// Return reward multiplier over the given _from to _to block.
// Return reward multiplier over the given _from to _to block.
function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) {
function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) {
return _to.sub(_from).mul(BONUS_MULTIPLIER);
return _to.sub(_from).mul(BONUS_MULTIPLIER);
}
}
// View function to see pending EGGs on frontend.
// View function to see pending SALTs on frontend.
function pendingEgg(uint256 _pid, address _user) external view returns (uint256) {
function pendingSalt(uint256 _pid, address _user) external view returns (uint256) {
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][_user];
UserInfo storage user = userInfo[_pid][_user];
uint256 accEggPerShare = pool.accEggPerShare;
uint256 accSaltPerShare = pool.accSaltPerShare;
uint256 lpSupply = pool.lpToken.balanceOf(address(this));
uint256 lpSupply = pool.lpToken.balanceOf(address(this));
if (block.number > pool.lastRewardBlock && lpSupply != 0) {
if (block.number > pool.lastRewardBlock && lpSupply != 0) {
uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
uint256 eggReward = multiplier.mul(eggPerBlock).mul(pool.allocPoint).div(totalAllocPoint);
uint256 saltReward = multiplier.mul(saltPerBlock).mul(pool.allocPoint).div(totalAllocPoint);
accEggPerShare = accEggPerShare.add(eggReward.mul(1e12).div(lpSupply));
accSaltPerShare = accSaltPerShare.add(saltReward.mul(1e12).div(lpSupply));
}
}
return user.amount.mul(accEggPerShare).div(1e12).sub(user.rewardDebt);
return user.amount.mul(accSaltPerShare).div(1e12).sub(user.rewardDebt);
}
}
// Update reward variables for all pools. Be careful of gas spending!
// Update reward variables for all pools. Be careful of gas spending!
function massUpdatePools() public {
function massUpdatePools() public {
uint256 length = poolInfo.length;
uint256 length = poolInfo.length;
for (uint256 pid = 0; pid < length; ++pid) {
for (uint256 pid = 0; pid < length; ++pid) {
updatePool(pid);
updatePool(pid);
}
}
}
}
// Update reward variables of the given pool to be up-to-date.
// Update reward variables of the given pool to be up-to-date.
function updatePool(uint256 _pid) public {
function updatePool(uint256 _pid) public {
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
if (block.number <= pool.lastRewardBlock) {
if (block.number <= pool.lastRewardBlock) {
return;
return;
}
}
uint256 lpSupply = pool.lpToken.balanceOf(address(this));
uint256 lpSupply = pool.lpToken.balanceOf(address(this));
if (lpSupply == 0 || pool.allocPoint == 0) {
if (lpSupply == 0 || pool.allocPoint == 0) {
pool.lastRewardBlock = block.number;
pool.lastRewardBlock = block.number;
return;
return;
}
}
uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
uint256 eggReward = multiplier.mul(eggPerBlock).mul(pool.allocPoint).div(totalAllocPoint);
uint256 saltReward = multiplier.mul(saltPerBlock).mul(pool.allocPoint).div(totalAllocPoint);
egg.mint(devaddr, eggReward.div(10));
salt.mint(devaddr, saltReward.div(10));
egg.mint(address(this), eggReward);
salt.mint(address(this), saltReward);
pool.accEggPerShare = pool.accEggPerShare.add(eggReward.mul(1e12).div(lpSupply));
pool.accSaltPerShare = pool.accSaltPerShare.add(saltReward.mul(1e12).div(lpSupply));
pool.lastRewardBlock = block.number;
pool.lastRewardBlock = block.number;
}
}
// Deposit LP tokens to MasterChef for EGG allocation.
// Deposit LP tokens to MasterChef for SALT allocation.
function deposit(uint256 _pid, uint256 _amount) public {
function deposit(uint256 _pid, uint256 _amount) public {
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
UserInfo storage user = userInfo[_pid][msg.sender];
updatePool(_pid);
updatePool(_pid);
if (user.amount > 0) {
if (user.amount > 0) {
uint256 pending = user.amount.mul(pool.accEggPerShare).div(1e12).sub(user.rewardDebt);
uint256 pending = user.amount.mul(pool.accSaltPerShare).div(1e12).sub(user.rewardDebt);
if(pending > 0) {
if(pending > 0) {
safeEggTransfer(msg.sender, pending);
safeSaltTransfer(msg.sender, pending);
}
}
}
}
if(_amount > 0) {
if(_amount > 0) {
pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
if(pool.depositFeeBP > 0){
if(pool.depositFeeBP > 0){
uint256 depositFee = _amount.mul(pool.depositFeeBP).div(10000);
uint256 depositFee = _amount.mul(pool.depositFeeBP).div(10000);
pool.lpToken.safeTransfer(feeAddress, depositFee);
pool.lpToken.safeTransfer(feeAddress, depositFee);
user.amount = user.amount.add(_amount).sub(depositFee);
user.amount = user.amount.add(_amount).sub(depositFee);
}else{
}else{
user.amount = user.amount.add(_amount);
user.amount = user.amount.add(_amount);
}
}
}
}
user.rewardDebt = user.amount.mul(pool.accEggPerShare).div(1e12);
user.rewardDebt = user.amount.mul(pool.accSaltPerShare).div(1e12);
emit Deposit(msg.sender, _pid, _amount);
emit Deposit(msg.sender, _pid, _amount);
}
}
// Withdraw LP tokens from MasterChef.
// Withdraw LP tokens from MasterChef.
function withdraw(uint256 _pid, uint256 _amount) public {
function withdraw(uint256 _pid, uint256 _amount) public {
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
UserInfo storage user = userInfo[_pid][msg.sender];
require(user.amount >= _amount, "withdraw: not good");
require(user.amount >= _amount, "withdraw: not good");
updatePool(_pid);
updatePool(_pid);
uint256 pending = user.amount.mul(pool.accEggPerShare).div(1e12).sub(user.rewardDebt);
uint256 pending = user.amount.mul(pool.accSaltPerShare).div(1e12).sub(user.rewardDebt);
if(pending > 0) {
if(pending > 0) {
safeEggTransfer(msg.sender, pending);
safeSaltTransfer(msg.sender, pending);
}
}
if(_amount > 0) {
if(_amount > 0) {
user.amount = user.amount.sub(_amount);
user.amount = user.amount.sub(_amount);
pool.lpToken.safeTransfer(address(msg.sender), _amount);
pool.lpToken.safeTransfer(address(msg.sender), _amount);
}
}
user.rewardDebt = user.amount.mul(pool.accEggPerShare).div(1e12);
user.rewardDebt = user.amount.mul(pool.accSaltPerShare).div(1e12);
emit Withdraw(msg.sender, _pid, _amount);
emit Withdraw(msg.sender, _pid, _amount);
}
}
// Withdraw without caring about rewards. EMERGENCY ONLY.
// Withdraw without caring about rewards. EMERGENCY ONLY.
function emergencyWithdraw(uint256 _pid) public {
function emergencyWithdraw(uint256 _pid) public {
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
UserInfo storage user = userInfo[_pid][msg.sender];
uint256 amount = user.amount;
uint256 amount = user.amount;
user.amount = 0;
user.amount = 0;
user.rewardDebt = 0;
user.rewardDebt = 0;
pool.lpToken.safeTransfer(address(msg.sender), amount);
pool.lpToken.safeTransfer(address(msg.sender), amount);
emit EmergencyWithdraw(msg.sender, _pid, amount);
emit EmergencyWithdraw(msg.sender, _pid, amount);
}
}
// Safe egg transfer function, just in case if rounding error causes pool to not have enough EGGs.
// Safe salt transfer function, just in case if rounding error causes pool to not have enough SALTs.
function safeEggTransfer(address _to, uint256 _amount) internal {
function safeSaltTransfer(address _to, uint256 _amount) internal {
uint256 eggBal = egg.balanceOf(address(this));
uint256 saltBal = salt.balanceOf(address(this));
if (_amount > eggBal) {
if (_amount > saltBal) {
egg.transfer(_to, eggBal);
salt.transfer(_to, saltBal);
} else {
} else {
egg.transfer(_to, _amount);
salt.transfer(_to, _amount);
}
}
}
}
// Update dev address by the previous dev.
// Update dev address by the previous dev.
function dev(address _devaddr) public {
function dev(address _devaddr) public {
require(msg.sender == devaddr, "dev: wut?");
require(msg.sender == devaddr, "dev: wut?");
devaddr = _devaddr;
devaddr = _devaddr;
}
}
function setFeeAddress(address _feeAddress) public{
function setFeeAddress(address _feeAddress) public{
require(msg.sender == feeAddress, "setFeeAddress: FORBIDDEN");
require(msg.sender == feeAddress, "setFeeAddress: FORBIDDEN");
feeAddress = _feeAddress;
feeAddress = _feeAddress;
}
}
//Pancake has to add hidden dummy pools inorder to alter the emission, here we make it simple and transparent to all.
//Pancake has to add hidden dummy pools inorder to alter the emission, here we make it simple and transparent to all.
function updateEmissionRate(uint256 _eggPerBlock) public onlyOwner {
function updateEmissionRate(uint256 _saltPerBlock) public onlyOwner {
massUpdatePools();
massUpdatePools();
eggPerBlock = _eggPerBlock;
saltPerBlock = _saltPerBlock;
}
}
}
}