Commit ce39a68a authored by XFT-dev's avatar XFT-dev
Browse files

initializing repo

parent 8605a110
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract Migrations {
address public owner = msg.sender;
uint256 public last_completed_migration;
modifier restricted() {
require(
msg.sender == owner,
"This function is restricted to the contract's owner"
);
_;
}
function setCompleted(uint256 completed) public restricted {
last_completed_migration = completed;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./interfaces/IStakingRewards.sol";
import "./interfaces/IMasterChef.sol";
contract StakingRewards is IStakingRewards, Ownable, Pausable, ReentrancyGuard {
using SafeMath for uint256;
using SafeERC20 for IERC20;
uint256 public immutable PID;
IERC20 public rewardsTokenXFT;
IERC20 public rewardsTokenSUSHI;
IERC20 public stakingToken;
IMasterChef public masterChef;
PoolInfo public poolInfo;
uint256 public periodFinish;
uint256 public rewardRate;
uint256 public rewardsDuration;
uint256 public lastUpdateTime;
uint256 public rewardPerTokenStored;
mapping(address => UserInfo) public userInfo;
mapping(address => uint256) public userRewardPerTokenPaid;
mapping(address => uint256) public rewardsXFT;
mapping(address => uint256) public rewardsSUSHI;
uint256 private _totalStaked;
modifier updateReward(address account) {
rewardPerTokenStored = rewardPerToken();
lastUpdateTime = lastTimeRewardApplicable();
if (account != address(0)) {
rewardsXFT[account] = earnedXFT(account);
userRewardPerTokenPaid[account] = rewardPerTokenStored;
updatePoolInfo();
if (userInfo[account].amount > 0) {
rewardsSUSHI[account] = earnedSushi(account);
}
}
_;
}
constructor(
address _rewardsTokenXFT,
address _rewardsTokenSUSHI,
address _stakingToken,
address _masterChef,
uint256 _pid
) Ownable() {
rewardsTokenXFT = IERC20(_rewardsTokenXFT);
rewardsTokenSUSHI = IERC20(_rewardsTokenSUSHI);
stakingToken = IERC20(_stakingToken);
masterChef = IMasterChef(_masterChef);
PID = _pid;
periodFinish = 0;
rewardRate = 0;
rewardsDuration = 30 days;
}
function totalStaked() external view override returns (uint256) {
return _totalStaked;
}
function balanceOf(address _account)
external
view
override
returns (uint256)
{
return userInfo[_account].amount;
}
function getRewardForDuration() external view override returns (uint256) {
return rewardRate.mul(rewardsDuration);
}
function exit() external override {
withdraw(userInfo[msg.sender].amount);
getReward();
}
function notifyRewardAmount(uint256 _reward)
external
onlyOwner
updateReward(address(0))
{
if (block.timestamp >= periodFinish) {
rewardRate = _reward.div(rewardsDuration);
} else {
uint256 remaining = periodFinish.sub(block.timestamp);
uint256 leftover = remaining.mul(rewardRate);
rewardRate = _reward.add(leftover).div(rewardsDuration);
}
uint256 balance = rewardsTokenXFT.balanceOf(address(this));
require(
rewardRate <= balance.div(rewardsDuration),
"Provided reward too high"
);
lastUpdateTime = block.timestamp;
periodFinish = block.timestamp.add(rewardsDuration);
emit RewardAdded(_reward);
}
function updatePeriodFinish(uint256 _timestamp)
external
onlyOwner
updateReward(address(0))
{
periodFinish = _timestamp;
}
function stake(uint256 _amount)
external
override
nonReentrant
whenNotPaused
updateReward(msg.sender)
{
require(_amount > 0, "Stake: cant stake 0");
_totalStaked = _totalStaked.add(_amount);
stakingToken.safeTransferFrom(msg.sender, address(this), _amount);
stakingToken.approve(address(masterChef), _amount);
masterChef.deposit(PID, _amount);
UserInfo storage user = userInfo[msg.sender];
user.amount = user.amount.add(_amount);
user.rewardDebt = user.amount
.mul(poolInfo.accSushiPerShare)
.div(1e12);
emit Staked(msg.sender, _amount);
}
function withdraw(uint256 _amount)
public
override
nonReentrant
updateReward(msg.sender)
{
require(_amount > 0, "Withdraw: cant withdraw 0");
UserInfo storage user = userInfo[msg.sender];
require(user.amount >= _amount, "Withdraw: insufficient funds");
_totalStaked = _totalStaked.sub(_amount);
masterChef.withdraw(PID, _amount);
user.amount = user.amount.sub(_amount);
user.rewardDebt = user.amount
.mul(poolInfo.accSushiPerShare)
.div(1e12);
stakingToken.safeTransfer(msg.sender, _amount);
emit Withdrawn(msg.sender, _amount);
}
function getReward() public override nonReentrant updateReward(msg.sender) {
uint256 rewardXFT = rewardsXFT[msg.sender];
uint256 rewardSUSHI = rewardsSUSHI[msg.sender];
if (rewardXFT > 0) {
rewardsXFT[msg.sender] = 0;
rewardsTokenXFT.safeTransfer(msg.sender, rewardXFT);
emit XFTRewardPaid(msg.sender, rewardXFT);
}
if (rewardSUSHI > 0) {
masterChef.deposit(PID, 0); // to get pending Sushi from onsen
rewardsSUSHI[msg.sender] = 0;
userInfo[msg.sender].rewardDebt = userInfo[msg.sender]
.amount
.mul(poolInfo.accSushiPerShare)
.div(1e12);
safeSushiTransfer(msg.sender, rewardSUSHI);
emit SUSHIRewardPaid(msg.sender, rewardSUSHI);
}
}
function lastTimeRewardApplicable() public view override returns (uint256) {
return Math.min(block.timestamp, periodFinish);
}
function rewardPerToken() public view override returns (uint256) {
if (_totalStaked == 0) {
return rewardPerTokenStored;
}
return
rewardPerTokenStored.add(
lastTimeRewardApplicable()
.sub(lastUpdateTime)
.mul(rewardRate)
.mul(1e18)
.div(_totalStaked)
);
}
function earnedXFT(address _account)
public
view
override
returns (uint256)
{
return
userInfo[_account]
.amount
.mul(rewardPerToken().sub(userRewardPerTokenPaid[_account]))
.div(1e18)
.add(rewardsXFT[_account]);
}
function earnedSushi(address _account)
public
view
override
returns (uint256)
{
(
address _lpToken,
uint256 _allocPoint,
uint256 _lastRewardBlock,
uint256 _accSushiPerShare
) = masterChef.poolInfo(PID);
UserInfo storage user = userInfo[_account];
if (user.amount == 0) return rewardsSUSHI[_account];
uint256 lpSupply = IERC20(_lpToken).balanceOf(address(masterChef));
if (block.number > _lastRewardBlock && lpSupply != 0) {
uint256 multiplier =
masterChef.getMultiplier(_lastRewardBlock, block.number);
uint256 sushiReward =
multiplier.mul(masterChef.sushiPerBlock()).mul(_allocPoint).div(
masterChef.totalAllocPoint()
);
_accSushiPerShare = _accSushiPerShare.add(
sushiReward.mul(1e12).div(lpSupply)
);
}
return
rewardsSUSHI[_account].add(
user.amount.mul(_accSushiPerShare).div(1e12).sub(
user.rewardDebt
)
);
}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function safeSushiTransfer(address _to, uint256 _amount) internal {
uint256 sushiBal = rewardsTokenSUSHI.balanceOf(address(this));
if (_amount > sushiBal) {
rewardsTokenSUSHI.safeTransfer(_to, sushiBal);
} else {
rewardsTokenSUSHI.safeTransfer(_to, _amount);
}
}
function updatePoolInfo() internal {
masterChef.updatePool(PID);
(
address _lpToken,
uint256 _allocPoint,
uint256 _lastRewardBlock,
uint256 _accSushiPerShare
) = masterChef.poolInfo(PID);
poolInfo = PoolInfo(
IERC20(_lpToken),
_allocPoint,
_lastRewardBlock,
_accSushiPerShare
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.4;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
interface IMasterChef {
function poolInfo(uint256)
external
view
returns (
address lpToken,
uint256 allocPoint,
uint256 lastRewardBlock,
uint256 accSushiPerShare
);
function userInfo(uint256, address)
external
view
returns (uint256 amount, uint256 rewardDebt);
function deposit(uint256 _pid, uint256 _amount) external;
function withdraw(uint256 _pid, uint256 _amount) external;
function updatePool(uint256 _pid) external;
function getMultiplier(uint256 _from, uint256 _to)
external
view
returns (uint256);
function sushiPerBlock() external view returns (uint256);
function totalAllocPoint() external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IStakingRewards {
struct UserInfo {
uint256 amount; // How many LP tokens the user has provided.
uint256 rewardDebt; // Reward debt. See explanation below.
}
// Info of each pool.
struct PoolInfo {
IERC20 lpToken; // Address of LP token contract.
uint256 allocPoint; // How many allocation points assigned to this pool. SUSHIs to distribute per block.
uint256 lastRewardBlock; // Last block number that SUSHIs distribution occurs.
uint256 accSushiPerShare; // Accumulated SUSHIs per share, times 1e12. See below.
}
event RewardAdded(uint256 reward);
event Staked(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
event XFTRewardPaid(address indexed user, uint256 reward);
event SUSHIRewardPaid(address indexed user, uint256 reward);
function totalStaked() external view returns (uint256);
function balanceOf(address _account) external view returns (uint256);
function getRewardForDuration() external view returns (uint256);
function lastTimeRewardApplicable() external view returns (uint256);
function rewardPerToken() external view returns (uint256);
function earnedXFT(address _account) external view returns (uint256);
function earnedSushi(address _account) external view returns (uint256);
function stake(uint256 _amount) external;
function withdraw(uint256 _amount) external;
function getReward() external;
function exit() external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.4;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TokenMock is ERC20 {
constructor(
string memory name,
string memory symbol,
uint256 _amount
) ERC20(name, symbol) {
_mint(msg.sender, _amount);
}
function mint(address _to, uint256 _amount) public {
_mint(_to, _amount);
}
}
/**
*Submitted for verification at Etherscan.io on 2020-09-05
*/
// File: contracts/uniswapv2/interfaces/IUniswapV2Factory.sol
pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(
address indexed token0,
address indexed token1,
address pair,
uint256
);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function migrator() external view returns (address);
function getPair(address tokenA, address tokenB)
external
view
returns (address pair);
function allPairs(uint256) external view returns (address pair);
function allPairsLength() external view returns (uint256);
function createPair(address tokenA, address tokenB)
external
returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
function setMigrator(address) external;
}
// File: contracts/uniswapv2/libraries/SafeMath.sol
pragma solidity >=0.6.12;
// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
library SafeMathUniswap {
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
}
}
// File: contracts/uniswapv2/UniswapV2ERC20.sol
pragma solidity >=0.6.12;
contract UniswapV2ERC20 {
using SafeMathUniswap for uint256;
string public constant name = "SushiSwap LP Token";
string public constant symbol = "SLP";
uint8 public constant decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
bytes32 public DOMAIN_SEPARATOR;
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant PERMIT_TYPEHASH =
0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
mapping(address => uint256) public nonces;
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
event Transfer(address indexed from, address indexed to, uint256 value);
constructor() public {
uint256 chainId;
assembly {
chainId := chainid()
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256(bytes(name)),
keccak256(bytes("1")),
chainId,
address(this)
)
);
}
function _mint(address to, uint256 value) internal {
totalSupply = totalSupply.add(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(address(0), to, value);
}
function _burn(address from, uint256 value) internal {
balanceOf[from] = balanceOf[from].sub(value);
totalSupply = totalSupply.sub(value);
emit Transfer(from, address(0), value);
}
function _approve(
address owner,
address spender,
uint256 value
) private {
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _transfer(
address from,
address to,
uint256 value
) private {
balanceOf[from] = balanceOf[from].sub(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(from, to, value);
}
function approve(address spender, uint256 value) external returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transfer(address to, uint256 value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool) {
if (allowance[from][msg.sender] != uint256(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(
value
);
}
_transfer(from, to, value);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
require(deadline >= block.timestamp, "UniswapV2: EXPIRED");
bytes32 digest =
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(