Last active
February 21, 2021 20:43
-
-
Save tomarsachin2271/ea6dee39905ca8c4acde225df39d2e00 to your computer and use it in GitHub Desktop.
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
/** | |
*Submitted for verification at BscScan.com on 2021-02-19 | |
*/ | |
pragma solidity ^0.6.2; | |
interface IERC20 { | |
function totalSupply() external view returns (uint256); | |
function balanceOf(address account) external view returns (uint256); | |
function transfer(address recipient, uint256 amount) external returns (bool); | |
function allowance(address owner, address spender) external view returns (uint256); | |
function approve(address spender, uint256 amount) external returns (bool); | |
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
event Approval(address indexed owner, address indexed spender, uint256 value); | |
} | |
library SafeMath { | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a, "SafeMath: addition overflow"); | |
return c; | |
} | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
return sub(a, b, "SafeMath: subtraction overflow"); | |
} | |
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b <= a, errorMessage); | |
uint256 c = a - b; | |
return c; | |
} | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the | |
// benefit is lost if 'b' is also tested. | |
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 | |
if (a == 0) { | |
return 0; | |
} | |
uint256 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
return div(a, b, "SafeMath: division by zero"); | |
} | |
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b > 0, errorMessage); | |
uint256 c = a / b; | |
return c; | |
} | |
function mod(uint256 a, uint256 b) internal pure returns (uint256) { | |
return mod(a, b, "SafeMath: modulo by zero"); | |
} | |
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b != 0, errorMessage); | |
return a % b; | |
} | |
} | |
library Address { | |
function isContract(address account) internal view returns (bool) { | |
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts | |
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned | |
// for accounts without code, i.e. `keccak256('')` | |
bytes32 codehash; | |
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; | |
// solhint-disable-next-line no-inline-assembly | |
assembly { codehash := extcodehash(account) } | |
return (codehash != accountHash && codehash != 0x0); | |
} | |
function toPayable(address account) internal pure returns (address payable) { | |
return address(uint160(account)); | |
} | |
function sendValue(address payable recipient, uint256 amount) internal { | |
require(address(this).balance >= amount, "Address: insufficient balance"); | |
// solhint-disable-next-line avoid-call-value | |
(bool success, ) = recipient.call{value:amount}(""); | |
require(success, "Address: unable to send value, recipient may have reverted"); | |
} | |
} | |
library SafeERC20 { | |
using SafeMath for uint256; | |
using Address for address; | |
function safeTransfer(IERC20 token, address to, uint256 value) internal { | |
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); | |
} | |
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { | |
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); | |
} | |
function safeApprove(IERC20 token, address spender, uint256 value) internal { | |
// safeApprove should only be called when setting an initial allowance, | |
// or when resetting it to zero. To increase and decrease it, use | |
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance' | |
// solhint-disable-next-line max-line-length | |
require((value == 0) || (token.allowance(address(this), spender) == 0), | |
"SafeERC20: approve from non-zero to non-zero allowance" | |
); | |
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); | |
} | |
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { | |
uint256 newAllowance = token.allowance(address(this), spender).add(value); | |
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); | |
} | |
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { | |
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); | |
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); | |
} | |
function callOptionalReturn(IERC20 token, bytes memory data) private { | |
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since | |
// we're implementing it ourselves. | |
// A Solidity high level call has three parts: | |
// 1. The target address is checked to verify it contains contract code | |
// 2. The call itself is made, and success asserted | |
// 3. The return value is decoded, which in turn checks the size of the returned data. | |
// solhint-disable-next-line max-line-length | |
require(address(token).isContract(), "SafeERC20: call to non-contract"); | |
// solhint-disable-next-line avoid-low-level-calls | |
(bool success, bytes memory returndata) = address(token).call(data); | |
require(success, "SafeERC20: low-level call failed"); | |
if (returndata.length > 0) { // Return data is optional | |
// solhint-disable-next-line max-line-length | |
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); | |
} | |
} | |
} | |
// SPDX-License-Identifier:MIT | |
pragma solidity ^0.6.2; | |
/** | |
* a contract must implement this interface in order to support relayed transaction. | |
* It is better to inherit the BaseRelayRecipient as its implementation. | |
*/ | |
abstract contract IRelayRecipient { | |
/** | |
* return if the forwarder is trusted to forward relayed transactions to us. | |
* the forwarder is required to verify the sender's signature, and verify | |
* the call is not a replay. | |
*/ | |
function isTrustedForwarder(address forwarder) public virtual view returns(bool); | |
/** | |
* return the sender of this call. | |
* if the call came through our trusted forwarder, then the real sender is appended as the last 20 bytes | |
* of the msg.data. | |
* otherwise, return `msg.sender` | |
* should be used in the contract anywhere instead of msg.sender | |
*/ | |
function _msgSender() internal virtual view returns (address payable); | |
// function versionRecipient() external virtual view returns (string memory); | |
} | |
/** | |
* A base contract to be inherited by any contract that want to receive relayed transactions | |
* A subclass must use "_msgSender()" instead of "msg.sender" | |
*/ | |
abstract contract BaseRelayRecipient is IRelayRecipient { | |
/* | |
* Forwarder singleton we accept calls from | |
*/ | |
address public trustedForwarder; | |
/* | |
* require a function to be called through GSN only | |
*/ | |
modifier trustedForwarderOnly() { | |
require(msg.sender == address(trustedForwarder), "Function can only be called through the trusted Forwarder"); | |
_; | |
} | |
function isTrustedForwarder(address forwarder) public override view returns(bool) { | |
return forwarder == trustedForwarder; | |
} | |
/** | |
* return the sender of this call. | |
* if the call came through our trusted forwarder, return the original sender. | |
* otherwise, return `msg.sender`. | |
* should be used in the contract anywhere instead of msg.sender | |
*/ | |
function _msgSender() internal override virtual view returns (address payable ret) { | |
if (msg.data.length >= 24 && isTrustedForwarder(msg.sender)) { | |
// At this point we know that the sender is a trusted forwarder, | |
// so we trust that the last bytes of msg.data are the verified sender address. | |
// extract sender address from the end of msg.data | |
assembly { | |
ret := shr(96,calldataload(sub(calldatasize(),20))) | |
} | |
} else { | |
return msg.sender; | |
} | |
} | |
} | |
contract ReentrancyGuard { | |
bool private _notEntered; | |
constructor () internal { | |
_notEntered = true; | |
} | |
modifier nonReentrant() { | |
// On the first call to nonReentrant, _notEntered will be true | |
require(_notEntered, "ReentrancyGuard: reentrant call"); | |
// Any calls to nonReentrant after this point will fail | |
_notEntered = false; | |
_; | |
// By storing the original value once again, a refund is triggered (see | |
// https://eips.ethereum.org/EIPS/eip-2200) | |
_notEntered = true; | |
} | |
} | |
contract StakingTokenWrapper is ReentrancyGuard, BaseRelayRecipient { | |
using SafeMath for uint256; | |
using SafeERC20 for IERC20; | |
IERC20 public stakingToken; | |
uint256 private _totalSupply; | |
mapping(address => uint256) private _balances; | |
constructor(address _stakingToken) internal { | |
stakingToken = IERC20(_stakingToken); | |
} | |
function totalSupply() | |
public | |
view | |
returns (uint256) | |
{ | |
return _totalSupply; | |
} | |
function balanceOf(address _account) | |
public | |
view | |
returns (uint256) | |
{ | |
return _balances[_account]; | |
} | |
function _stake(address _beneficiary, uint256 _amount) | |
internal | |
nonReentrant | |
{ | |
_totalSupply = _totalSupply.add(_amount); | |
_balances[_beneficiary] = _balances[_beneficiary].add(_amount); | |
stakingToken.safeTransferFrom(_msgSender(), address(this), _amount); | |
} | |
function _withdraw(uint256 _amount) | |
internal | |
nonReentrant | |
{ | |
_totalSupply = _totalSupply.sub(_amount); | |
_balances[_msgSender()] = _balances[_msgSender()].sub(_amount); | |
stakingToken.safeTransfer(_msgSender(), _amount); | |
} | |
} | |
interface IRewardsDistributionRecipient { | |
// function notifyRewardAmount(uint256 reward) external; | |
function getRewardToken() external view returns (IERC20); | |
} | |
abstract contract RewardsDistributionRecipient is IRewardsDistributionRecipient, BaseRelayRecipient { | |
// @abstract | |
// function notifyRewardAmount(uint256 reward) external; | |
function getRewardToken() external virtual override view returns (IERC20); | |
// This address has the ability to distribute the rewards | |
address public rewardsDistributor; | |
/** @dev Recipient is a module, governed by mStable governance */ | |
constructor(address _rewardsDistributor) | |
internal | |
{ | |
rewardsDistributor = _rewardsDistributor; | |
} | |
/** | |
* @dev Only the rewards distributor can notify about rewards | |
*/ | |
modifier onlyRewardsDistributor() { | |
require(_msgSender() == rewardsDistributor, "Caller is not reward distributor"); | |
_; | |
} | |
} | |
library StableMath { | |
using SafeMath for uint256; | |
uint256 private constant FULL_SCALE = 1e18; | |
uint256 private constant RATIO_SCALE = 1e8; | |
function getFullScale() internal pure returns (uint256) { | |
return FULL_SCALE; | |
} | |
function getRatioScale() internal pure returns (uint256) { | |
return RATIO_SCALE; | |
} | |
function scaleInteger(uint256 x) | |
internal | |
pure | |
returns (uint256) | |
{ | |
return x.mul(FULL_SCALE); | |
} | |
function mulTruncate(uint256 x, uint256 y) | |
internal | |
pure | |
returns (uint256) | |
{ | |
return mulTruncateScale(x, y, FULL_SCALE); | |
} | |
function mulTruncateScale(uint256 x, uint256 y, uint256 scale) | |
internal | |
pure | |
returns (uint256) | |
{ | |
// e.g. assume scale = fullScale | |
// z = 10e18 * 9e17 = 9e36 | |
uint256 z = x.mul(y); | |
// return 9e38 / 1e18 = 9e18 | |
return z.div(scale); | |
} | |
function mulTruncateCeil(uint256 x, uint256 y) | |
internal | |
pure | |
returns (uint256) | |
{ | |
// e.g. 8e17 * 17268172638 = 138145381104e17 | |
uint256 scaled = x.mul(y); | |
// e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17 | |
uint256 ceil = scaled.add(FULL_SCALE.sub(1)); | |
// e.g. 13814538111.399...e18 / 1e18 = 13814538111 | |
return ceil.div(FULL_SCALE); | |
} | |
function divPrecisely(uint256 x, uint256 y) | |
internal | |
pure | |
returns (uint256) | |
{ | |
// e.g. 8e18 * 1e18 = 8e36 | |
uint256 z = x.mul(FULL_SCALE); | |
// e.g. 8e36 / 10e18 = 8e17 | |
return z.div(y); | |
} | |
function mulRatioTruncate(uint256 x, uint256 ratio) | |
internal | |
pure | |
returns (uint256 c) | |
{ | |
return mulTruncateScale(x, ratio, RATIO_SCALE); | |
} | |
function mulRatioTruncateCeil(uint256 x, uint256 ratio) | |
internal | |
pure | |
returns (uint256) | |
{ | |
// e.g. How much mAsset should I burn for this bAsset (x)? | |
// 1e18 * 1e8 = 1e26 | |
uint256 scaled = x.mul(ratio); | |
// 1e26 + 9.99e7 = 100..00.999e8 | |
uint256 ceil = scaled.add(RATIO_SCALE.sub(1)); | |
// return 100..00.999e8 / 1e8 = 1e18 | |
return ceil.div(RATIO_SCALE); | |
} | |
function divRatioPrecisely(uint256 x, uint256 ratio) | |
internal | |
pure | |
returns (uint256 c) | |
{ | |
// e.g. 1e14 * 1e8 = 1e22 | |
uint256 y = x.mul(RATIO_SCALE); | |
// return 1e22 / 1e12 = 1e10 | |
return y.div(ratio); | |
} | |
function min(uint256 x, uint256 y) | |
internal | |
pure | |
returns (uint256) | |
{ | |
return x > y ? y : x; | |
} | |
function max(uint256 x, uint256 y) | |
internal | |
pure | |
returns (uint256) | |
{ | |
return x > y ? x : y; | |
} | |
function clamp(uint256 x, uint256 upperBound) | |
internal | |
pure | |
returns (uint256) | |
{ | |
return x > upperBound ? upperBound : x; | |
} | |
} | |
contract Staking is StakingTokenWrapper, RewardsDistributionRecipient { | |
using StableMath for uint256; | |
string public quote; | |
address public owner; | |
IERC20 public rewardsToken; | |
// uint256 public constant ONE_DAY = 86400; // in seconds | |
// uint256 public constant ONE_DAY = 60; // 1 mins in seconds | |
uint256 public minStakingAmount = 10*10**18; | |
uint256 public maxStakingAmount = 10000000*10**18; | |
uint256 public sixMonthRewardPercent = 5 * 1e18; // 5% | |
uint256 public twelveMonthRewardPercent = 10 * 1e18; // 10% | |
// Timestamps of staking duration | |
uint256 public constant SIX_MONTHS_DURATION = 180 days; | |
uint256 public constant TWELVE_MONTHS_DURATION = 360 days; | |
uint256 public stakingDuration = 0; | |
// Amount the user has staked | |
mapping(address => uint256) public userStakedTokens; | |
// Reward the user will get after staking period ends | |
mapping(address => uint256) public rewards; | |
// Rewards paid to user | |
mapping(address => uint256) public userRewardsPaid; | |
// Stake starting timestamp | |
mapping(address => uint256) public stakeStarted; | |
// Stake ending timestamp | |
mapping(address => uint256) public stakeEnded; | |
event Staked(address indexed user, uint256 amount, uint256 reward,uint256 time); | |
event Withdrawn(address indexed user, uint256 amount,uint256 time); | |
event RewardPaid(address indexed user, uint256 reward,uint256 time); | |
/*************************************** | |
CONSTRUCTOR | |
****************************************/ | |
constructor ( | |
address _stakingToken, | |
address _rewardsToken, | |
address _rewardsDistributor, | |
address _trustedForwarder | |
) | |
public | |
StakingTokenWrapper(_stakingToken) | |
RewardsDistributionRecipient(_rewardsDistributor) | |
{ | |
rewardsToken = IERC20(_rewardsToken); | |
trustedForwarder = _trustedForwarder; | |
} | |
/*************************************** | |
MODIFIERS | |
****************************************/ | |
modifier isAccount(address _account) { | |
require(!Address.isContract(_account), "Only external owned accounts allowed"); | |
_; | |
} | |
/*************************************** | |
ACTIONS | |
****************************************/ | |
function setTrustedForwarder( address forwarderAddress ) public onlyRewardsDistributor { | |
require(forwarderAddress != address(0), "Forwarder Address cannot be 0"); | |
trustedForwarder = forwarderAddress; | |
} | |
function setQuote(string memory newQuote) public { | |
quote = newQuote; | |
owner = _msgSender(); | |
} | |
function getQuote() view public returns(string memory currentQuote, address currentOwner) { | |
currentQuote = quote; | |
currentOwner = owner; | |
} | |
function stake6m(address _beneficiary, uint256 _amount) | |
external | |
{ | |
__stake(_beneficiary, _amount, SIX_MONTHS_DURATION); | |
} | |
function stake12m(address _beneficiary, uint256 _amount) | |
external | |
{ | |
__stake(_beneficiary, _amount, TWELVE_MONTHS_DURATION); | |
} | |
function unstake() | |
external | |
{ | |
require(block.timestamp >= stakeEnded[_msgSender()], "Reward cannot be claimed before staking ends"); | |
withdraw(balanceOf(_msgSender())); | |
claimReward(); | |
stakeStarted[_msgSender()] = 0; | |
stakeEnded[_msgSender()] = 0; | |
} | |
function __stake(address _beneficiary, uint256 _amount, uint256 _period) | |
internal | |
isAccount(_beneficiary) | |
{ | |
require( | |
_amount <= maxStakingAmount && | |
_amount >= minStakingAmount, | |
"Invalid staking amount" | |
); | |
require( | |
_period == SIX_MONTHS_DURATION || | |
_period == TWELVE_MONTHS_DURATION, | |
"Invalid staking period" | |
); | |
super._stake(_beneficiary, _amount); | |
stakeStarted[_beneficiary] = block.timestamp; | |
userStakedTokens[_beneficiary] = userStakedTokens[_beneficiary].add(_amount); | |
uint256 __userAmount = userStakedTokens[_beneficiary]; | |
// calculation is on the basis: | |
// reward = (monthPercentageInWei * stakedAmountInWei) / 1e20 | |
// e.g: (2.5% * 1e18 * 100 * 1e18) / 1e20 = 2.5 * 1e18 | |
uint256 _rewardAmount; | |
if (_period == SIX_MONTHS_DURATION) { | |
_rewardAmount = (sixMonthRewardPercent * __userAmount) / 1e20; | |
rewards[_beneficiary] += _rewardAmount; | |
stakeEnded[_beneficiary] = (block.timestamp).add(SIX_MONTHS_DURATION); | |
} else if (_period == TWELVE_MONTHS_DURATION) { | |
_rewardAmount = (twelveMonthRewardPercent * __userAmount) / 1e20; | |
rewards[_beneficiary] += _rewardAmount; | |
stakeEnded[_beneficiary] = (block.timestamp).add(TWELVE_MONTHS_DURATION); | |
} else { | |
revert("Error: duration not allowed!"); | |
} | |
emit Staked(_beneficiary, _amount, _rewardAmount,now); | |
} | |
function withdraw(uint256 _amount) | |
internal | |
isAccount(_msgSender()) | |
{ | |
require(_amount > 0, "Cannot withdraw 0"); | |
require(block.timestamp >= stakeEnded[_msgSender()], "Reward cannot be claimed before staking ends"); | |
userStakedTokens[_msgSender()] = userStakedTokens[_msgSender()].sub(_amount); | |
_withdraw(_amount); | |
emit Withdrawn(_msgSender(), _amount,now); | |
} | |
function claimReward() | |
internal | |
isAccount(_msgSender()) | |
{ | |
require(block.timestamp >= stakeEnded[_msgSender()], "Reward cannot be claimed before staking ends"); | |
uint256 reward = rewards[_msgSender()]; | |
if (reward > 0) { | |
rewards[_msgSender()] = 0; | |
rewardsToken.transfer(_msgSender(), reward); | |
userRewardsPaid[_msgSender()] = userRewardsPaid[_msgSender()].add(reward); | |
emit RewardPaid(_msgSender(), reward,now); | |
} | |
} | |
/*************************************** | |
GETTERS | |
****************************************/ | |
function getRewardToken() | |
external | |
override | |
view | |
returns (IERC20) | |
{ | |
return rewardsToken; | |
} | |
function earned(address _account) | |
public | |
view | |
returns (uint256) | |
{ | |
return rewards[_account]; | |
} | |
function tokensStaked(address _account) | |
public | |
view | |
returns (uint256) | |
{ | |
return userStakedTokens[_account]; | |
} | |
/*************************************** | |
ADMIN | |
****************************************/ | |
function sendRewardTokens(uint256 _amount) | |
public | |
onlyRewardsDistributor | |
{ | |
require(rewardsToken.transferFrom(_msgSender(), address(this), _amount), "Transfering not approved!"); | |
} | |
function withdrawRewardTokens(address receiver, uint256 _amount) | |
public | |
onlyRewardsDistributor | |
{ | |
require(rewardsToken.transfer(receiver, _amount), "Not enough tokens on contract!"); | |
} | |
function withdrawFarmTokens(address receiver, uint256 _amount) | |
public | |
onlyRewardsDistributor | |
{ | |
require(stakingToken.transfer(receiver, _amount), "Not enough tokens on contract!"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment