File 1 of 1: ss.sol
pragma solidity >=0.6.2 <0.8.0;
library SafeMath {
function mul(uint a, uint b) internal pure returns (uint c) {
if (a == 0) {
return 0;
}
c = a * b;
assert(c / a == b);
return c;
}
function div(uint a, uint b) internal pure returns (uint) {
return a / b;
}
function sub(uint a, uint b) internal pure returns (uint) {
assert(b <= a);
return a - b;
}
function add(uint a, uint b) internal pure returns (uint c) {
c = a + b;
assert(c >= a);
return c;
}
}
pragma solidity >=0.6.2 <0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
pragma solidity >=0.6.0 <0.8.0;
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);
function burn(uint256 amount) external;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
pragma solidity >=0.6.0 <0.8.0;
contract Staking {
using SafeMath for uint;
IERC20 public stakingToken;
uint public rewardAmount;
uint public oneUnit;
uint public stakeFee;
uint public unstakeFee;
uint internal amountForRewards;
uint internal amountClaimed;
uint internal amountFees;
uint internal amountBurned;
mapping (address => uint) internal stakedAmounts;
mapping (address => uint) internal lastClaims;
mapping (address => uint) internal claimableAmounts;
mapping (address => uint) internal claimedAmounts;
mapping (address => uint) internal admins;
event Stake(
address indexed owner,
uint amount,
uint fee
);
event Unstake(
address indexed owner,
uint amount,
uint fee
);
event Claimed(
address indexed owner,
uint amount
);
event Compound(
address indexed owner,
uint amount,
uint fee
);
constructor(
address __stakingToken,
uint __rewardAmount,
uint __oneUnit,
uint __stakeFee,
uint __unstakeFee
) {
require(Address.isContract(__stakingToken), "Error: Address should be a contract");
stakingToken = IERC20(__stakingToken);
rewardAmount = __rewardAmount;
oneUnit = __oneUnit;
stakeFee = __stakeFee;
unstakeFee = __unstakeFee;
amountForRewards = 0;
amountClaimed = 0;
amountFees = 0;
amountBurned = 0;
admins[msg.sender] = block.timestamp;
}
function isAdmin(address __address) view public returns(bool) {
if (admins[__address] > 0) {
return (true);
}
return (false);
}
function addAdmin(address __address) external onlyAdmin {
require(__address != address(0), "Error: new admin is the zero address");
admins[__address] = block.timestamp;
}
function fund(uint __amount) external onlyAdmin {
require(__amount > 0, "You are trying to fund with 0 assets");
stakingToken.transferFrom(msg.sender, address(this), __amount);
amountForRewards = amountForRewards.add(__amount);
}
function burn() external onlyAdmin {
require(amountFees.sub(amountBurned) > 0, "Error: you are trying to burn all tokens");
stakingToken.burn(amountFees.sub(amountBurned));
amountBurned = amountFees;
}
function balance() view public returns(uint, uint, int, uint, uint) {
return (amountForRewards, amountClaimed, int(amountForRewards - amountClaimed), amountFees, amountBurned);
}
function deleteAdmin(address __address) external onlyAdmin {
admins[__address] = 0;
}
modifier onlyAdmin() {
require(admins[msg.sender] > 0, "Error: caller is not the admin");
_;
}
function isStaker(address __address) view public returns(bool, uint, uint, uint, uint) {
if (stakedAmounts[__address] == 0) {
return (false, 0, 0, 0, 0);
}
uint __claimableAmount = claimableAmounts[__address].add(stakedAmounts[__address].div(oneUnit).mul(block.timestamp.sub(lastClaims[__address])).mul(rewardAmount));
return (true, stakedAmounts[__address], lastClaims[__address], __claimableAmount, claimedAmounts[__address]);
}
function update(address __address, uint __stakedAmount, uint __lastClaim, uint __claimableAmount, uint __claimedAmount) private {
if (stakedAmounts[__address] != __stakedAmount) {
stakedAmounts[__address] = __stakedAmount;
}
if (lastClaims[__address] != __lastClaim) {
lastClaims[__address] = __lastClaim;
}
if (claimableAmounts[__address] != __claimableAmount) {
claimableAmounts[__address] = __claimableAmount;
}
if (__claimedAmount > 0 && claimedAmounts[__address] != __claimedAmount) {
claimedAmounts[__address] = __claimedAmount;
}
}
function claim() external {
(bool __isStaker, uint __stakedAmount, , uint __claimableAmount, uint __claimedAmount) = isStaker(msg.sender);
require(__isStaker == true, "Error: you are not staking");
update(msg.sender, __stakedAmount, block.timestamp, 0, __claimedAmount.add(__claimableAmount));
amountClaimed = amountClaimed.add(__claimableAmount);
stakingToken.transfer(msg.sender, __claimableAmount);
emit Claimed(msg.sender, __claimableAmount);
}
function compound() external {
(bool __isStaker, uint __stakedAmount, , uint __claimableAmount, uint __claimedAmount) = isStaker(msg.sender);
require(__isStaker == true, "Error: you are not staking");
uint __fee = __claimableAmount.mul(stakeFee).div(10000);
uint __stakeAmount = __claimableAmount.sub(__fee);
update(msg.sender, __stakedAmount.add(__stakeAmount), block.timestamp, 0, __claimedAmount.add(__claimableAmount));
amountClaimed = amountClaimed.add(__claimableAmount);
amountFees = amountFees.add(__fee);
emit Compound(msg.sender, __stakeAmount, __fee);
}
function stake(uint __amount) external {
require(__amount > 0, "Error: you are trying to stake 0 tokens");
stakingToken.transferFrom(msg.sender, address(this), __amount);
(bool __isStaker, uint __stakedAmount, , uint __claimableAmount, ) = isStaker(msg.sender);
uint __fee = __amount.mul(stakeFee).div(10000);
uint __stakeAmount = __amount.sub(__fee);
if (__isStaker) {
update(msg.sender, __stakedAmount.add(__stakeAmount), block.timestamp, __claimableAmount, 0);
} else {
update(msg.sender, __stakeAmount, block.timestamp, 0, 0);
}
amountFees = amountFees.add(__fee);
emit Stake(msg.sender, __stakeAmount, __fee);
}
function unstake() external {
(bool __isStaker, uint __stakedAmount, , uint __claimableAmount, uint __claimedAmount) = isStaker(msg.sender);
require(__isStaker == true, "Error: you are not staking");
uint __fee = __stakedAmount.mul(unstakeFee).div(10000);
uint __unstakeAmount = __stakedAmount.sub(__fee).add(__claimableAmount);
update(msg.sender, 0, block.timestamp, 0, __claimedAmount.add(__claimableAmount));
amountClaimed = amountClaimed.sub(__claimableAmount);
amountFees = amountFees.add(__fee);
stakingToken.transfer(msg.sender, __unstakeAmount);
emit Unstake(msg.sender, __unstakeAmount, __fee);
}
function transferStake(address __to, uint __amount) external {
(bool __isStaker, uint __stakedAmount, , uint __claimableAmount, ) = isStaker(msg.sender);
require(__isStaker == true, "Error: you are not staking");
require(__to != address(0), "Error: new owner is the zero address");
require(__stakedAmount > __amount, "Error: you are trying to transfer more then you have");
if (__amount == 0) {
__amount = __stakedAmount;
}
update(msg.sender, __stakedAmount.add(__amount), block.timestamp, __claimableAmount, 0);
(__isStaker, __stakedAmount, , __claimableAmount, ) = isStaker(__to);
update(__to, __stakedAmount.add(__amount), block.timestamp, __claimableAmount, 0);
}
}