-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from SuperFarmDAO/lp-incentive-program-contracts
- Loading branch information
Showing
8 changed files
with
309 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
pragma solidity 0.7.3; | ||
|
||
interface ISuperStaking { | ||
// Views | ||
function lastTimeRewardApplicable() external view returns (uint256); | ||
|
||
function rewardPerToken() external view returns (uint256); | ||
|
||
function earned(address account) external view returns (uint256); | ||
|
||
function balanceOf(address account) external view returns (uint256); | ||
|
||
// Mutative | ||
|
||
function stake(uint256 amount) external; | ||
|
||
function withdraw(uint256 amount) external; | ||
|
||
function getReward() external; | ||
|
||
function exit() external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
pragma solidity 0.7.3; | ||
|
||
import "@openzeppelin/contracts/math/Math.sol"; | ||
import "@openzeppelin/contracts/math/SafeMath.sol"; | ||
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; | ||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; | ||
import "@openzeppelin/contracts/access/Ownable.sol"; | ||
|
||
import './ISuperStaking.sol'; | ||
|
||
contract SuperStaking is ISuperStaking, Ownable, ReentrancyGuard { | ||
using SafeMath for uint256; | ||
using SafeERC20 for IERC20; | ||
|
||
uint256 public periodFinish; | ||
uint256 public rewardRate; | ||
uint256 public rewardsDuration; | ||
uint256 public lastUpdateTime; | ||
uint256 public rewardPerTokenStored; | ||
uint256 public lastBalance; | ||
uint256 public totalSupply; | ||
|
||
IERC20 public rewardsToken; | ||
IERC20 public stakingToken; | ||
|
||
mapping(address => uint256) private userRewardPerTokenPaid; | ||
mapping(address => uint256) private rewards; | ||
mapping(address => uint256) private _balances; | ||
|
||
constructor( | ||
address _owner, | ||
address _rewardsToken, | ||
address _stakingToken | ||
) { | ||
rewardsToken = IERC20(_rewardsToken); | ||
stakingToken = IERC20(_stakingToken); | ||
} | ||
|
||
function balanceOf(address account) external override view returns (uint256) { | ||
return _balances[account]; | ||
} | ||
|
||
function lastTimeRewardApplicable() public override view returns (uint256) { | ||
return Math.min(block.timestamp, periodFinish); | ||
} | ||
|
||
function rewardPerToken() public override view returns (uint256) { | ||
if (totalSupply == 0) { | ||
return rewardPerTokenStored; | ||
} | ||
return rewardPerTokenStored.add(lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(totalSupply)); | ||
} | ||
|
||
function earned(address account) public override view returns (uint256) { | ||
return _balances[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add(rewards[account]); | ||
} | ||
|
||
/* ========== MUTATIVE FUNCTIONS ========== */ | ||
|
||
function stake(uint256 amount) external override nonReentrant updateReward(msg.sender) { | ||
require(amount > 0, 'Cannot stake 0'); | ||
totalSupply = totalSupply.add(amount); | ||
_balances[msg.sender] = _balances[msg.sender].add(amount); | ||
stakingToken.safeTransferFrom(msg.sender, address(this), amount); | ||
emit Staked(msg.sender, amount); | ||
} | ||
|
||
function withdraw(uint256 amount) public override nonReentrant updateReward(msg.sender) { | ||
require(amount > 0, 'Cannot withdraw 0'); | ||
totalSupply = totalSupply.sub(amount); | ||
_balances[msg.sender] = _balances[msg.sender].sub(amount); | ||
stakingToken.safeTransfer(msg.sender, amount); | ||
emit Withdrawn(msg.sender, amount); | ||
} | ||
|
||
function getReward() public override nonReentrant updateReward(msg.sender) { | ||
uint256 reward = rewards[msg.sender]; | ||
if (reward > 0) { | ||
rewards[msg.sender] = 0; | ||
rewardsToken.safeTransfer(msg.sender, reward); | ||
emit RewardPaid(msg.sender, reward); | ||
} | ||
} | ||
|
||
function exit() external override { | ||
withdraw(_balances[msg.sender]); | ||
getReward(); | ||
} | ||
|
||
/* ========== RESTRICTED FUNCTIONS ========== */ | ||
|
||
function notifyRewardAmount(uint256 reward) external onlyOwner updateReward(address(0)) { | ||
require(rewardRate > 0, 'Reward Rate is not yet set'); | ||
if (block.timestamp >= periodFinish) { | ||
rewardsDuration = reward.div(rewardRate); | ||
} else { | ||
uint256 remaining = periodFinish.sub(block.timestamp); | ||
uint256 leftover = remaining.mul(rewardRate); | ||
rewardsDuration = reward.add(leftover).div(rewardRate); | ||
} | ||
|
||
// Ensure the provided reward amount is not more than the balance in the contract. | ||
// This keeps the reward rate in the right range, preventing overflows due to | ||
// very high values of rewardRate in the earned and rewardsPerToken functions; | ||
// Reward + leftover must be less than 2^256 / 10^18 to avoid overflow. | ||
uint256 balance = rewardsToken.balanceOf(address(this)); | ||
require(rewardRate <= balance.div(rewardsDuration), 'Provided reward too high'); | ||
|
||
lastUpdateTime = block.timestamp; | ||
periodFinish = block.timestamp.add(rewardsDuration); | ||
emit RewardAdded(reward); | ||
} | ||
|
||
function setRewardRate(uint256 rewardsPerInterval, uint256 interval) external onlyOwner { | ||
require(rewardsPerInterval > 0 && interval > 0, 'rewardsPerInterval and interval should be greater than 0'); | ||
require(block.timestamp > periodFinish, 'Previous rewards period must be complete before changing the reward rate'); | ||
rewardRate = rewardsPerInterval.div(interval); | ||
|
||
RewardRateUpdated(rewardsPerInterval, interval, rewardRate); | ||
} | ||
|
||
/* ========== MODIFIERS ========== */ | ||
|
||
modifier updateReward(address account) { | ||
rewardPerTokenStored = rewardPerToken(); | ||
lastUpdateTime = lastTimeRewardApplicable(); | ||
if (account != address(0)) { | ||
rewards[account] = earned(account); | ||
userRewardPerTokenPaid[account] = rewardPerTokenStored; | ||
} | ||
_; | ||
} | ||
|
||
/* ========== EVENTS ========== */ | ||
|
||
event RewardAdded(uint256 reward); | ||
event Staked(address indexed user, uint256 amount); | ||
event Withdrawn(address indexed user, uint256 amount); | ||
event RewardPaid(address indexed user, uint256 reward); | ||
event RewardRateUpdated(uint256 rewardsPerInterval, uint256 interval, uint256 rewardRate); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.