123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // SPDX-License-Identifier: BUSL-1.1
- pragma solidity 0.7.6;
- // imports
- import "@openzeppelin/contracts/utils/EnumerableSet.sol";
- import "@openzeppelin/contracts/access/Ownable.sol";
- import "./MirrorgateToken.sol";
- // interfaces
- import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
- // libraries
- import "@openzeppelin/contracts/math/SafeMath.sol";
- import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
- contract LPStaking is Ownable {
- using SafeMath for uint256;
- using SafeERC20 for IERC20;
- // Info of each user.
- struct UserInfo {
- uint256 amount; // How many LP tokens the user has provided.
- uint256 rewardDebt; // Reward debt. See explanation below.
- //
- // We do some fancy math here. Basically, any point in time, the amount of STGs
- // entitled to a user but is pending to be distributed is:
- //
- // pending reward = (user.amount * pool.accStargatePerShare) - user.rewardDebt
- //
- // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:
- // 1. The pool's `accStargatePerShare` (and `lastRewardBlock`) gets updated.
- // 2. User receives the pending reward sent to his/her address.
- // 3. User's `amount` gets updated.
- // 4. User's `rewardDebt` gets updated.
- }
- // Info of each pool.
- struct PoolInfo {
- IERC20 lpToken; // Address of LP token contract.
- uint256 allocPoint; // How many allocation points assigned to this pool. STGs to distribute per block.
- uint256 lastRewardBlock; // Last block number that STGs distribution occurs.
- uint256 accMirrorgatePerShare; // Accumulated STGs per share, times 1e12. See below.
- }
- // The STG TOKEN!
- MirrorgateToken public mirrorgate;
- // Block number when bonus STG period ends.
- uint256 public bonusEndBlock;
- // STG tokens created per block.
- uint256 public mirrorgatePerBlock;
- // Bonus multiplier for early stargate makers.
- uint256 public constant BONUS_MULTIPLIER = 1;
- // Track which tokens have been added.
- mapping(address => bool) private addedLPTokens;
- mapping(uint256 => uint256) public lpBalances;
- // Info of each pool.
- PoolInfo[] public poolInfo;
- // Info of each user that stakes LP tokens.
- mapping(uint256 => mapping(address => UserInfo)) public userInfo;
- // Total allocation points. Must be the sum of all allocation points in all pools.
- uint256 public totalAllocPoint = 0;
- // The block number when STG mining starts.
- uint256 public startBlock;
- event Deposit(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);
- constructor(
- MirrorgateToken _mirrorgate,
- uint256 _mirrorgatePerBlock,
- uint256 _startBlock,
- uint256 _bonusEndBlock
- ) {
- require(_startBlock >= block.number, "LPStaking: _startBlock must be >= current block");
- require(_bonusEndBlock >= _startBlock, "LPStaking: _bonusEndBlock must be > than _startBlock");
- require(address(_mirrorgate) != address(0x0), "Mirrorgate: _mirrorgate cannot be 0x0");
- mirrorgate = _mirrorgate;
- mirrorgatePerBlock = _mirrorgatePerBlock;
- startBlock = _startBlock;
- bonusEndBlock = _bonusEndBlock;
- }
- function poolLength() external view returns (uint256) {
- return poolInfo.length;
- }
- /// @notice handles adding a new LP token (Can only be called by the owner)
- /// @param _allocPoint The alloc point is used as the weight of the pool against all other alloc points added.
- /// @param _lpToken The lp token address
- function add(uint256 _allocPoint, IERC20 _lpToken) public onlyOwner {
- massUpdatePools();
- require(address(_lpToken) != address(0x0), "Mirrorgate: lpToken cant be 0x0");
- require(addedLPTokens[address(_lpToken)] == false, "Mirrorgate: _lpToken already exists");
- addedLPTokens[address(_lpToken)] = true;
- uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
- totalAllocPoint = totalAllocPoint.add(_allocPoint);
- poolInfo.push(PoolInfo({lpToken: _lpToken, allocPoint: _allocPoint, lastRewardBlock: lastRewardBlock, accMirrorgatePerShare: 0}));
- }
- function set(uint256 _pid, uint256 _allocPoint) public onlyOwner {
- massUpdatePools();
- totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
- poolInfo[_pid].allocPoint = _allocPoint;
- }
- function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) {
- if (_to <= bonusEndBlock) {
- return _to.sub(_from).mul(BONUS_MULTIPLIER);
- } else if (_from >= bonusEndBlock) {
- return _to.sub(_from);
- } else {
- return bonusEndBlock.sub(_from).mul(BONUS_MULTIPLIER).add(_to.sub(bonusEndBlock));
- }
- }
- function pendingMirrorgate(uint256 _pid, address _user) external view returns (uint256) {
- PoolInfo storage pool = poolInfo[_pid];
- UserInfo storage user = userInfo[_pid][_user];
- uint256 accMirrorgatePerShare = pool.accMirrorgatePerShare;
- uint256 lpSupply = pool.lpToken.balanceOf(address(this));
- if (block.number > pool.lastRewardBlock && lpSupply != 0) {
- uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
- uint256 mirrorgateReward = multiplier.mul(mirrorgatePerBlock).mul(pool.allocPoint).div(totalAllocPoint);
- accMirrorgatePerShare = accMirrorgatePerShare.add(mirrorgateReward.mul(1e12).div(lpSupply));
- }
- return user.amount.mul(accMirrorgatePerShare).div(1e12).sub(user.rewardDebt);
- }
- 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 lpSupply = pool.lpToken.balanceOf(address(this));
- if (lpSupply == 0) {
- pool.lastRewardBlock = block.number;
- return;
- }
- uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
- uint256 mirrorgateReward = multiplier.mul(mirrorgatePerBlock).mul(pool.allocPoint).div(totalAllocPoint);
- pool.accMirrorgatePerShare = pool.accMirrorgatePerShare.add(mirrorgateReward.mul(1e12).div(lpSupply));
- pool.lastRewardBlock = block.number;
- }
- function deposit(uint256 _pid, uint256 _amount) public {
- PoolInfo storage pool = poolInfo[_pid];
- UserInfo storage user = userInfo[_pid][msg.sender];
- updatePool(_pid);
- if (user.amount > 0) {
- uint256 pending = user.amount.mul(pool.accMirrorgatePerShare).div(1e12).sub(user.rewardDebt);
- safeMirrorgateTransfer(msg.sender, pending);
- }
- pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
- user.amount = user.amount.add(_amount);
- user.rewardDebt = user.amount.mul(pool.accMirrorgatePerShare).div(1e12);
- lpBalances[_pid] = lpBalances[_pid].add(_amount);
- emit Deposit(msg.sender, _pid, _amount);
- }
- function withdraw(uint256 _pid, uint256 _amount) public {
- PoolInfo storage pool = poolInfo[_pid];
- UserInfo storage user = userInfo[_pid][msg.sender];
- require(user.amount >= _amount, "withdraw: _amount is too large");
- updatePool(_pid);
- uint256 pending = user.amount.mul(pool.accMirrorgatePerShare).div(1e12).sub(user.rewardDebt);
- safeMirrorgateTransfer(msg.sender, pending);
- user.amount = user.amount.sub(_amount);
- user.rewardDebt = user.amount.mul(pool.accMirrorgatePerShare).div(1e12);
- pool.lpToken.safeTransfer(address(msg.sender), _amount);
- lpBalances[_pid] = lpBalances[_pid].sub(_amount);
- emit Withdraw(msg.sender, _pid, _amount);
- }
- /// @notice Withdraw without caring about rewards.
- /// @param _pid The pid specifies the pool
- function emergencyWithdraw(uint256 _pid) public {
- PoolInfo storage pool = poolInfo[_pid];
- UserInfo storage user = userInfo[_pid][msg.sender];
- uint256 userAmount = user.amount;
- user.amount = 0;
- user.rewardDebt = 0;
- pool.lpToken.safeTransfer(address(msg.sender), userAmount);
- lpBalances[_pid] = lpBalances[_pid].sub(userAmount);
- emit EmergencyWithdraw(msg.sender, _pid, userAmount);
- }
- /// @notice Safe transfer function, just in case if rounding error causes pool to not have enough STGs.
- /// @param _to The address to transfer tokens to
- /// @param _amount The quantity to transfer
- function safeMirrorgateTransfer(address _to, uint256 _amount) internal {
- uint256 mirrorgateBal = mirrorgate.balanceOf(address(this));
- if (_amount > mirrorgateBal) {
- IERC20(mirrorgate).safeTransfer(_to, mirrorgateBal);
- } else {
- IERC20(mirrorgate).safeTransfer(_to, _amount);
- }
- }
- function setMirrorgatePerBlock(uint256 _mirrorgatePerBlock) external onlyOwner {
- massUpdatePools();
- mirrorgatePerBlock = _mirrorgatePerBlock;
- }
- // Override the renounce ownership inherited by zeppelin ownable
- function renounceOwnership() public override onlyOwner {}
- }
|