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(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(
recoveredAddress != address(0) && recoveredAddress == owner,
"UniswapV2: INVALID_SIGNATURE"
);
_approve(owner, spender, value);
}
}
// File: contracts/uniswapv2/libraries/Math.sol
pragma solidity >=0.6.12;
// a library for performing various math operations
library MathS {
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x < y ? x : y;
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
// File: contracts/uniswapv2/libraries/UQ112x112.sol
pragma solidity >=0.6.12;
// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
// range: [0, 2**112 - 1]
// resolution: 1 / 2**112
library UQ112x112 {
uint224 constant Q112 = 2**112;
// encode a uint112 as a UQ112x112
function encode(uint112 y) internal pure returns (uint224 z) {
z = uint224(y) * Q112; // never overflows
}
// divide a UQ112x112 by a uint112, returning a UQ112x112
function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
z = x / uint224(y);
}
}
// File: contracts/uniswapv2/interfaces/IERC20.sol
pragma solidity >=0.5.0;
interface IERC20Uniswap {
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
event Transfer(address indexed from, address indexed to, uint256 value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
}
// File: contracts/uniswapv2/interfaces/IUniswapV2Callee.sol
pragma solidity >=0.5.0;
interface IUniswapV2Callee {
function uniswapV2Call(
address sender,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external;
}
// File: contracts/uniswapv2/UniswapV2Pair.sol
pragma solidity >=0.6.12;
interface IMigrator {
// Return the desired amount of liquidity token that the migrator wants.
function desiredLiquidity() external view returns (uint256);
}
contract UniswapV2Pair is UniswapV2ERC20 {
using SafeMathUniswap for uint256;
using UQ112x112 for uint224;
uint256 public constant MINIMUM_LIQUIDITY = 10**3;
bytes4 private constant SELECTOR =
bytes4(keccak256(bytes("transfer(address,uint256)")));
address public factory;
address public token0;
address public token1;
uint112 private reserve0; // uses single storage slot, accessible via getReserves
uint112 private reserve1; // uses single storage slot, accessible via getReserves
uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves
uint256 public price0CumulativeLast;
uint256 public price1CumulativeLast;
uint256 public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
uint256 private unlocked = 1;
modifier lock() {
require(unlocked == 1, "UniswapV2: LOCKED");
unlocked = 0;
_;
unlocked = 1;
}
function getReserves()
public
view
returns (
uint112 _reserve0,
uint112 _reserve1,
uint32 _blockTimestampLast
)
{
_reserve0 = reserve0;
_reserve1 = reserve1;
_blockTimestampLast = blockTimestampLast;
}
function _safeTransfer(
address token,
address to,
uint256 value
) private {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(SELECTOR, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"UniswapV2: TRANSFER_FAILED"
);
}
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(
address indexed sender,
uint256 amount0,
uint256 amount1,
address indexed to
);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
constructor() public {
factory = msg.sender;
}
// called once by the factory at time of deployment
function initialize(address _token0, address _token1) external {
require(msg.sender == factory, "UniswapV2: FORBIDDEN"); // sufficient check
token0 = _token0;
token1 = _token1;
}
// update reserves and, on the first call per block, price accumulators
function _update(
uint256 balance0,
uint256 balance1,
uint112 _reserve0,
uint112 _reserve1
) private {
require(
balance0 <= uint112(-1) && balance1 <= uint112(-1),
"UniswapV2: OVERFLOW"
);
uint32 blockTimestamp = uint32(block.timestamp % 2**32);
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
// * never overflows, and + overflow is desired
price0CumulativeLast +=
uint256(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) *
timeElapsed;
price1CumulativeLast +=
uint256(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) *
timeElapsed;
}
reserve0 = uint112(balance0);
reserve1 = uint112(balance1);
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}
// if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
function _mintFee(uint112 _reserve0, uint112 _reserve1)
private
returns (bool feeOn)
{
address feeTo = IUniswapV2Factory(factory).feeTo();
feeOn = feeTo != address(0);
uint256 _kLast = kLast; // gas savings
if (feeOn) {
if (_kLast != 0) {
uint256 rootK = MathS.sqrt(uint256(_reserve0).mul(_reserve1));
uint256 rootKLast = MathS.sqrt(_kLast);
if (rootK > rootKLast) {
uint256 numerator = totalSupply.mul(rootK.sub(rootKLast));
uint256 denominator = rootK.mul(5).add(rootKLast);
uint256 liquidity = numerator / denominator;
if (liquidity > 0) _mint(feeTo, liquidity);
}
}
} else if (_kLast != 0) {
kLast = 0;
}
}
// this low-level function should be called from a contract which performs important safety checks
function mint(address to) external lock returns (uint256 liquidity) {
(uint112 _reserve0, uint112 _reserve1, ) = getReserves(); // gas savings
uint256 balance0 = IERC20Uniswap(token0).balanceOf(address(this));
uint256 balance1 = IERC20Uniswap(token1).balanceOf(address(this));
uint256 amount0 = balance0.sub(_reserve0);
uint256 amount1 = balance1.sub(_reserve1);
bool feeOn = _mintFee(_reserve0, _reserve1);
uint256 _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
if (_totalSupply == 0) {
address migrator = IUniswapV2Factory(factory).migrator();
if (msg.sender == migrator) {
liquidity = IMigrator(migrator).desiredLiquidity();
require(
liquidity > 0 && liquidity != uint256(-1),
"Bad desired liquidity"
);
} else {
require(migrator == address(0), "Must not have migrator");
liquidity = MathS.sqrt(amount0.mul(amount1)).sub(
MINIMUM_LIQUIDITY
);
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
}
} else {
liquidity = MathS.min(
amount0.mul(_totalSupply) / _reserve0,
amount1.mul(_totalSupply) / _reserve1
);
}
require(liquidity > 0, "UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED");
_mint(to, liquidity);
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint256(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
emit Mint(msg.sender, amount0, amount1);
}
// this low-level function should be called from a contract which performs important safety checks
function burn(address to)
external
lock
returns (uint256 amount0, uint256 amount1)
{
(uint112 _reserve0, uint112 _reserve1, ) = getReserves(); // gas savings
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
uint256 balance0 = IERC20Uniswap(_token0).balanceOf(address(this));
uint256 balance1 = IERC20Uniswap(_token1).balanceOf(address(this));
uint256 liquidity = balanceOf[address(this)];
bool feeOn = _mintFee(_reserve0, _reserve1);
uint256 _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
require(
amount0 > 0 && amount1 > 0,
"UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED"
);
_burn(address(this), liquidity);
_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);
balance0 = IERC20Uniswap(_token0).balanceOf(address(this));
balance1 = IERC20Uniswap(_token1).balanceOf(address(this));
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint256(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
emit Burn(msg.sender, amount0, amount1, to);
}
// this low-level function should be called from a contract which performs important safety checks
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external lock {
require(
amount0Out > 0 || amount1Out > 0,
"UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT"
);
(uint112 _reserve0, uint112 _reserve1, ) = getReserves(); // gas savings
require(
amount0Out < _reserve0 && amount1Out < _reserve1,
"UniswapV2: INSUFFICIENT_LIQUIDITY"
);
uint256 balance0;
uint256 balance1;
{
// scope for _token{0,1}, avoids stack too deep errors
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, "UniswapV2: INVALID_TO");
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
if (data.length > 0)
IUniswapV2Callee(to).uniswapV2Call(
msg.sender,
amount0Out,
amount1Out,
data
);
balance0 = IERC20Uniswap(_token0).balanceOf(address(this));
balance1 = IERC20Uniswap(_token1).balanceOf(address(this));
}
uint256 amount0In =
balance0 > _reserve0 - amount0Out
? balance0 - (_reserve0 - amount0Out)
: 0;
uint256 amount1In =
balance1 > _reserve1 - amount1Out
? balance1 - (_reserve1 - amount1Out)
: 0;
require(
amount0In > 0 || amount1In > 0,
"UniswapV2: INSUFFICIENT_INPUT_AMOUNT"
);
{
// scope for reserve{0,1}Adjusted, avoids stack too deep errors
uint256 balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint256 balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(
balance0Adjusted.mul(balance1Adjusted) >=
uint256(_reserve0).mul(_reserve1).mul(1000**2),
"UniswapV2: K"
);
}
_update(balance0, balance1, _reserve0, _reserve1);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}
// force balances to match reserves
function skim(address to) external lock {
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
_safeTransfer(
_token0,
to,
IERC20Uniswap(_token0).balanceOf(address(this)).sub(reserve0)
);
_safeTransfer(
_token1,
to,
IERC20Uniswap(_token1).balanceOf(address(this)).sub(reserve1)
);
}
// force reserves to match balances
function sync() external lock {
_update(
IERC20Uniswap(token0).balanceOf(address(this)),
IERC20Uniswap(token1).balanceOf(address(this)),
reserve0,
reserve1
);
}
}
This diff is collapsed.
This diff is collapsed.
contract StakingRewards {
uint256 public immutable PID; // ip of pair XFT/ETH in Onsen
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;
function totalStaked() external view override returns (uint256);
// staked balance for specific user
function balanceOf(address _account)
external
view
override
returns (uint256);
// reward notified for duration
function getRewardForDuration() external view override returns (uint256);
// withdraw all staked tokens and get reward
function exit() external override;
function stake(uint256 _amount)
external
override
nonReentrant
whenNotPaused
updateReward(msg.sender);
function withdraw(uint256 _amount)
public
override
nonReentrant
updateReward(msg.sender);
// get reward in both tokens
function getReward() public override nonReentrant updateReward(msg.sender); //
function lastTimeRewardApplicable() public view override returns (uint256);
// reward per token in XFT
function rewardPerToken() public view override returns (uint256);
// reward XFT for specific user
function earnedXFT(address _account)
public
view
override
returns (uint256);
// reward SUSHI for specific user
function earnedSushi(address _account)
public
view
override
returns (uint256);
// onlyOwner
// 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.
function notifyRewardAmount(uint256 _reward)
external
onlyOwner
updateReward(address(0));
//
function updatePeriodFinish(uint256 _timestamp)
external
onlyOwner
updateReward(address(0));
// Pausable
// status
function paused() public view virtual returns (bool);
function pause() public onlyOwner;
function unpause() public onlyOwner;
// Ownble
function owner() public view virtual returns (address);
// transfere ownership to zero address
function renounceOwnership() public virtual onlyOwner;
function transferOwnership(address newOwner) public virtual onlyOwner;
}
This diff is collapsed.
const MasterChef = artifacts.require('MasterChef');
const StakingRewards = artifacts.require('StakingRewards');
const TokenMock = artifacts.require('TokenMock');
const {time, BN, ether} = require('@openzeppelin/test-helpers');
const {use, expect} = require('chai');
function bn(params) {
return new BN(params.toString());
}
use(require('chai-bn')(BN));
const PID = 0;
const ALLOCATE_POINT_SUSHI = ether('1000');
const ALLOCATE_POINT_XFT = ether('1000');
contract('DeltaHubStaking', ([owner, dev, wallet1, wallet2, wallet3, wallet4, wallet5]) => {
let slp;
let sushi;
let xft;
let onsen;
let staking;
beforeEach(async () => {
slp = await TokenMock.new('SLP', 'SLP', ether('10000000'), {from: owner});
sushi = await TokenMock.new('SUSHI', 'SUSHI', ether('10000000'), {from: owner});
xft = await TokenMock.new('XFT', 'XFT', ether('10000000'), {from: owner});
onsen = await MasterChef.new(
sushi.address,
dev,
ether('0.01'),
await time.latestBlock(),
await time.latestBlock() + 2592000, // apx. 12 month
{from: owner},
);
staking = await StakingRewards.new(
xft.address,
sushi.address,
slp.address,
onsen.address,
{from: owner},
);
await onsen.add(ALLOCATE_POINT_SUSHI, slp.address, false, {from: owner});
await sushi.mint(onsen.address, ALLOCATE_POINT_SUSHI);
await xft.transfer(staking.address, ALLOCATE_POINT_XFT, {from: owner});
await staking.notifyRewardAmount(ALLOCATE_POINT_XFT, {from: owner});
await slp.transfer(wallet1, ether('100000'), {from: owner});
await slp.transfer(wallet2, ether('100000'), {from: owner});
await slp.transfer(wallet3, ether('100000'), {from: owner});
await slp.transfer(wallet4, ether('100000'), {from: owner});
await slp.transfer(wallet5, ether('100000'), {from: owner});
await slp.approve(staking.address, new BN(2).pow(new BN(255)), {from: wallet1});
await slp.approve(staking.address, new BN(2).pow(new BN(255)), {from: wallet2});
await slp.approve(staking.address, new BN(2).pow(new BN(255)), {from: wallet3});
await slp.approve(staking.address, new BN(2).pow(new BN(255)), {from: wallet4});
await slp.approve(staking.address, new BN(2).pow(new BN(255)), {from: wallet5});
});
describe('test gas usage', () => {
beforeEach( async () => {
await slp.approve(onsen.address, new BN(2).pow(new BN(255)), {from: wallet1});
await slp.approve(onsen.address, new BN(2).pow(new BN(255)), {from: wallet2});
await slp.approve(onsen.address, new BN(2).pow(new BN(255)), {from: wallet3});
});
it('should stake to onsen contract', async () => {
const amount = ether('2');
let totalStaked = await bn(0);
for (let i = 1; i < 50; i++) {
const stakeAmount = await bn(amount).mul(bn(i));
await onsen.deposit(PID, stakeAmount, {from: wallet1});
await onsen.deposit(PID, stakeAmount, {from: wallet2});
await onsen.deposit(PID, stakeAmount, {from: wallet3});
await staking.stake(stakeAmount, {from: wallet1});
await staking.stake(stakeAmount, {from: wallet2});
await staking.stake(stakeAmount, {from: wallet3});
totalStaked = await totalStaked.add(stakeAmount);
}
for (let i = 1; i < 50; i++) {
const stakeAmount = await bn(amount).mul(bn(i));
await staking.withdraw(stakeAmount, {from: wallet1});
await staking.withdraw(stakeAmount, {from: wallet2});
await staking.withdraw(stakeAmount, {from: wallet3});
await onsen.withdraw(PID, stakeAmount, {from: wallet1});
await onsen.withdraw(PID, stakeAmount, {from: wallet2});
await onsen.withdraw(PID, stakeAmount, {from: wallet3});
}
});
it('should withdraw from onsen contract', async () => {
await staking.stake(ether('100'), {from: wallet1});
expect((await staking.userInfo(wallet1)).amount).to.be.a.bignumber.equal(ether('100'));
expect((await onsen.userInfo(PID, staking.address)).amount).to.be.a.bignumber.equal(ether('100'));
await staking.withdraw(ether('100'), {from: wallet1});
expect((await staking.userInfo(wallet1)).amount).to.be.a.bignumber.equal('0');
expect((await onsen.userInfo(PID, staking.address)).amount).to.be.a.bignumber.equal('0');
});
});
});
// contracts/zkAsset.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "../localhost/@openzeppelin/contracts/access/AccessControl.sol";
import "../localhost/@openzeppelin/contracts/GSN/Context.sol";
import "../localhost/@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../localhost/@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
import "../localhost/@openzeppelin/contracts/token/ERC20/ERC20Pausable.sol";
contract zkAsset is ERC20, AccessControl, ERC20Burnable, ERC20Pausable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor() public ERC20("zkTEST-Asset", "zkA") {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(MINTER_ROLE, msg.sender);
_setupRole(BURNER_ROLE, msg.sender);
_setupRole(PAUSER_ROLE, msg.sender);
}
/**
* @dev Destroys `amount` tokens for `from`.
*
* See {ERC20-_burn}.
*
* Requirements:
*
* - the caller must have the `BURNER_ROLE`.
*/
function burn(address from, uint256 amount) public {
require(hasRole(BURNER_ROLE, msg.sender), "zkAsset: must have burner role to burn");
_burn(from, amount);
}
/**
* @dev Creates `amount` new tokens for `to`.
*
* See {ERC20-_mint}.
*
* Requirements:
*
* - the caller must have the `MINTER_ROLE`.
*/
function mint(address to, uint256 amount) public {
require(hasRole(MINTER_ROLE, _msgSender()), "zkAsset: must have minter role to mint");
_mint(to, amount);
}
/**
* @dev Pauses all token transfers.
*
* See {ERC20Pausable} and {Pausable-_pause}.
*
* Requirements:
*
* - the caller must have the `PAUSER_ROLE`.
*/
function pause() public {
require(hasRole(PAUSER_ROLE, _msgSender()), "zkAsset: must have pauser role to pause");
_pause();
}
/**
* @dev Unpauses all token transfers.
*
* See {ERC20Pausable} and {Pausable-_unpause}.
*
* Requirements:
*
* - the caller must have the `PAUSER_ROLE`.
*/
function unpause() public {
require(hasRole(PAUSER_ROLE, _msgSender()), "zkAsset: must have pauser role to unpause");
_unpause();
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Pausable) {
super._beforeTokenTransfer(from, to, amount);
}
}
// contracts/zkAsset.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/GSN/Context.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20Pausable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20Burnable.sol";
contract zkAasset is ERC20, AccessControl, ERC20Burnable, ERC20Pausable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor() public ERC20("xftTEST-Asset", "xftT") {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(MINTER_ROLE, msg.sender);
_setupRole(BURNER_ROLE, msg.sender);
_setupRole(PAUSER_ROLE, msg.sender);
}
/**
* @dev Destroys `amount` tokens for `from`.
*
* See {ERC20-_burn}.
*
* Requirements:
*
* - the caller must have the `BURNER_ROLE`.
*/
function burn(address from, uint256 amount) public {
require(hasRole(BURNER_ROLE, msg.sender), "xftTEST-Asset: must have burner role to burn");
_burn(from, amount);
}
/**
* @dev Creates `amount` new tokens for `to`.
*
* See {ERC20-_mint}.
*
* Requirements:
*
* - the caller must have the `MINTER_ROLE`.
*/
function mint(address to, uint256 amount) public {
require(hasRole(MINTER_ROLE, _msgSender()), "xftTEST-Asset: must have minter role to mint");
_mint(to, amount);
}
/**
* @dev Pauses all token transfers.
*
* See {ERC20Pausable} and {Pausable-_pause}.
*
* Requirements:
*
* - the caller must have the `PAUSER_ROLE`.
*/
function pause() public {
require(hasRole(PAUSER_ROLE, _msgSender()), "xftTEST-Asset: must have pauser role to pause");
_pause();
}
/**
* @dev Unpauses all token transfers.
*
* See {ERC20Pausable} and {Pausable-_unpause}.
*
* Requirements:
*
* - the caller must have the `PAUSER_ROLE`.
*/
function unpause() public {
require(hasRole(PAUSER_ROLE, _msgSender()), "xftTEST-Asset: must have pauser role to unpause");
_unpause();
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Pausable) {
super._beforeTokenTransfer(from, to, amount);
}
}
const Migrations = artifacts.require("Migrations");
module.exports = function (deployer) {
deployer.deploy(Migrations);
};
const StakingRewards = artifacts.require('StakingRewards');
const XFT_TOKEN_ADDRESS = '0xabe580e7ee158da464b51ee1a83ac0289622e6be'.toLowerCase();
const SUSHI_TOKEN_ADDRESS = '0x6b3595068778dd592e39a122f4f5a5cf09c90fe2'.toLowerCase();
const SLP_TOKEN_ADDRESS = '0xF39fF863730268C9bb867b3a69d031d1C1614b31'.toLowerCase();
const MASTER_CHEF_ADDRESS = '0xc2EdaD668740f1aA35E4D8f227fB8E17dcA888Cd'.toLowerCase();
const PID = '149'; // ! BEFORE MIGRATE: make sure that you set up correct PID
module.exports = async (deployer, network, accounts) => {
await deployer.deploy(
StakingRewards,
XFT_TOKEN_ADDRESS,
SUSHI_TOKEN_ADDRESS,
SLP_TOKEN_ADDRESS,
MASTER_CHEF_ADDRESS,
PID,
);
const staking = await StakingRewards.deployed();
// await xft.transfer(staking.address, ALLOCATE_POINT_XFT);
// await staking.notifyRewardAmount(ALLOCATE_POINT_XFT);
console.log(`XFT Token: https://etherscan.io/address/${XFT_TOKEN_ADDRESS}`);
console.log(`SUSHI Token: https://etherscan.io/address/${SUSHI_TOKEN_ADDRESS}`);
console.log(`XFT/ETH SLP Token: https://etherscan.io/address/${SLP_TOKEN_ADDRESS}`);
console.log(`Staking contract: https://etherscan.io/address/${staking.address}`);
console.log(`MasterChef contract: https://etherscan.io/address/${MASTER_CHEF_ADDRESS}`);
console.log('Address owner: ', accounts[0]);
};
This diff is collapsed.
{
"name": "solidity-contracts",
"version": "1.0.0",
"description": "",
"main": "index.js",
"repository": {
"type": "git",
"url": "git+https://zokyo-secured@github.com/offshift-protocol/solidity-contracts.git"
},
"author": "ZOKYO Team",
"license": "MIT",
"bugs": {
"url": "https://github.com/offshift-protocol/solidity-contracts/issues"
},
"homepage": "https://github.com/offshift-protocol/solidity-contracts#readme",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"solhint": "./node_modules/.bin/solhint -f table contracts/**/*.sol",
"prettier:solidity": "./node_modules/.bin/prettier --write contracts/**/*.sol",
"test:coverage": "truffle run coverage",
"compile": "npm run clean && truffle compile --all",
"lint:js": "eslint *.js test/*.js",
"lint:sol": "solhint contracts/*.sol"
},
"dependencies": {
"@openzeppelin/contracts": "^3.2.1-solc-0.7",
"@truffle/hdwallet-provider": "^1.2.1",
"dotenv": "^8.2.0",
"ganache-cli": "^6.10.1",
"solc": "^0.7.4",
"truffle": "^5.1.64"
},
"devDependencies": {
"@openzeppelin/test-helpers": "^0.5.6",
"chai": "^4.2.0",
"eslint": "^7.7.0",
"eslint-config-google": "^0.14.0",
"eth-gas-reporter": "^0.2.22",
"eth-sig-util": "^3.0.0",
"ethlint": "^1.2.5",
"mocha": "^8.1.1",
"prettier": "^2.2.1",
"prettier-plugin-solidity": "^1.0.0-beta.5",
"solhint": "^3.2.0",
"solhint-plugin-prettier": "0.0.5",
"solidity-coverage": "^0.7.16",
"truffle-plugin-verify": "^0.5.4",
"web3": "^1.2.11"
}
}
/**
* Use this file to configure your truffle project. It's seeded with some
* common settings for different networks and features like migrations,
* compilation and testing. Uncomment the ones you need or modify
* them to suit your project as necessary.
*
* More information about configuration can be found at:
*
* trufflesuite.com/docs/advanced/configuration
*
* To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider)
* to sign your transactions before they're sent to a remote public node. Infura accounts
* are available for free at: infura.io/register.
*
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
* phrase from a file you've .gitignored so it doesn't accidentally become public.
*
*/
const HDWalletProvider = require('@truffle/hdwallet-provider');
const dotenv = require('dotenv');
dotenv.config();
module.exports = {
/**
* Networks define how you connect to your ethereum client and let you set the
* defaults web3 uses to send transactions. If you don't specify one truffle
* will spin up a development blockchain for you on port 9545 when you
* run `develop` or `test`. You can ask a truffle command to use a specific
* network from the command line, e.g
*
* $ truffle test --network <network-name>
*/
networks: {
development: {
host: '127.0.0.1', // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: '*', // Any network (default: none)
},
ropsten: {
provider: () => new HDWalletProvider([process.env.PRIVATE_KEY], `wss://ropsten.infura.io/ws/v3/${process.env.PROJECT_ID}`),
network_id: 3, // Ropsten's id
gas: 6721975, // Ropsten has a lower block limit than mainnet
gasPrice: 160000000000,
skipDryRun: true, // Skip dry run before migrations? (default: false for public nets )
},
rinkeby: {
provider: () => new HDWalletProvider([process.env.PRIVATE_KEY], `wss://rinkeby.infura.io/ws/v3/${process.env.PROJECT_ID}`),
network_id: 4,
gas: 10000000,
skipDryRun: true,
},
mainnet: {
provider: () => new HDWalletProvider([process.env.PRIVATE_KEY], `wss://mainnet.infura.io/ws/v3/${process.env.PROJECT_ID}`),
network_id: 1,
gas: 3000000,
gasPrice: 160000000000,
skipDryRun: true,
},
},
mocha: {
timeout: 300000,
reporter: 'eth-gas-reporter',
reporterOptions: {
showTimeSpent: true,
currency: 'USD',
coinmarketcap: process.env.COINMARKETCAP_API_KEY,
},
},
plugins: ['truffle-plugin-verify', 'solidity-coverage'],
api_keys: {
etherscan: process.env.ETHERSCAN_KEY,
},
// Configure your compilers
compilers: {
solc: {
version: '>=0.4.20 <0.8.0', // Fetch exact version from solc-bin (default: truffle's version)
docker: false, // Use "0.5.1" you've installed locally with docker (default: false)
settings: { // See the solidity docs for advice about optimization and evmVersion
optimizer: {
enabled: true,
runs: 200,
},
evmVersion: 'istanbul',
},
},
},
};
MIT License
Copyright (c) 2020 offshift
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment