文件 1 的 8:Address.sol
pragma solidity ^0.7.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 functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(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);
}
}
}
}
文件 2 的 8:CTokenInterface.sol
pragma solidity ^0.7.6;
interface CTokenInterface {
function mint(uint mintAmount) external returns (uint);
function redeem(uint redeemTokens) external returns (uint);
function redeemUnderlying(uint redeemAmount) external returns (uint);
function borrow(uint borrowAmount) external returns (uint);
function repayBorrow(uint repayAmount) external returns (uint);
function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint);
function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external returns (uint);
function transfer(address dst, uint amount) external returns (bool);
function transferFrom(address src, address dst, uint amount) external returns (bool);
function approve(address spender, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function balanceOfUnderlying(address owner) external returns (uint);
function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint);
function borrowRatePerBlock() external view returns (uint);
function supplyRatePerBlock() external view returns (uint);
function totalBorrowsCurrent() external returns (uint);
function borrowBalanceCurrent(address account) external returns (uint);
function borrowBalanceStored(address account) external view returns (uint);
function exchangeRateCurrent() external returns (uint);
function exchangeRateStored() external view returns (uint);
function getCash() external view returns (uint);
function accrueInterest() external returns (uint);
function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint);
}
文件 3 的 8:EIP20NonStandardInterface.sol
pragma solidity ^0.7.6;
interface EIP20NonStandardInterface {
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256 balance);
function transfer(address dst, uint256 amount) external;
function transferFrom(address src, address dst, uint256 amount) external;
function approve(address spender, uint256 amount) external returns (bool success);
function allowance(address owner, address spender) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}
文件 4 的 8:IERC20.sol
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);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 5 的 8:ReentrancyGuard.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 6 的 8:SafeERC20.sol
pragma solidity ^0.7.0;
import "./IERC20.sol";
import "./SafeMath.sol";
import "./Address.sol";
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 {
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 {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 7 的 8:SafeMath.sol
pragma solidity >=0.6.0 <0.8.0;
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) {
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;
}
}
文件 8 的 8:Staking.sol
pragma solidity ^0.7.6;
import "./IERC20.sol";
import "./SafeMath.sol";
import "./ReentrancyGuard.sol";
import "./CTokenInterface.sol";
import "./SafeERC20.sol";
import "./EIP20NonStandardInterface.sol";
contract Staking is ReentrancyGuard {
using SafeMath for uint256;
using SafeERC20 for IERC20;
uint128 constant private BASE_MULTIPLIER = uint128(1 * 10 ** 18);
uint256 public epoch1Start;
uint256 public epochDuration;
mapping(address => mapping(address => uint256)) private balances;
mapping(address => uint256) public stableCoinBalances;
address constant public usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address constant public usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
address constant public dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address constant public wbtcSwappLP = 0x5548F847Fd9a1D3487d5fbB2E8d73972803c4Cce;
address constant public cUsdc = 0x39AA39c021dfbaE8faC545936693aC917d5E7563;
address constant public cUsdt = 0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9;
address constant public cDai = 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643;
address payable constant TEAM_ADDRESS = 0xde121Cc755c1D1786Dd46FfF7e373e9372FD79D8;
struct Pool {
uint256 size;
bool set;
}
mapping(address => mapping(uint256 => Pool)) private poolSize;
struct Checkpoint {
uint128 epochId;
uint128 multiplier;
uint256 startBalance;
uint256 newDeposits;
}
mapping(address => mapping(address => Checkpoint[])) private balanceCheckpoints;
mapping(address => uint128) private lastWithdrawEpochId;
uint256 public firstReferrerRewardPercentage;
uint256 public secondReferrerRewardPercentage;
struct Referrer {
uint256 referralsCount;
mapping(uint256 => address) referrals;
}
mapping(address => address) public referrals;
mapping(address => Referrer) public referrers;
event Deposit(address indexed user, address indexed tokenAddress, uint256 amount);
event Withdraw(address indexed user, address indexed tokenAddress, uint256 amount);
event ManualEpochInit(address indexed caller, uint128 indexed epochId, address[] tokens);
event EmergencyWithdraw(address indexed user, address indexed tokenAddress, uint256 amount);
event RegisteredReferer(address referral, address referrer);
event GetInterest(address indexed token, uint256 amount);
event CheckInterest(uint256 cBalance, uint256 uBalance, uint256 interest);
address public _owner;
constructor () {
epoch1Start = 1624230000;
epochDuration = 2419200;
_owner = msg.sender;
firstReferrerRewardPercentage = 1000;
secondReferrerRewardPercentage = 500;
}
function checkStableCoin(address token) public pure returns (bool) {
if (token == usdc ||
token == usdt ||
token == dai
) {
return true;
}
return false;
}
function deposit(address tokenAddress, uint256 amount, address referrer) public nonReentrant {
require(amount > 0, "Staking: Amount must be > 0");
bool isStableCoin = checkStableCoin(tokenAddress);
require(IERC20(tokenAddress).allowance(msg.sender, address(this)) >= amount, "Staking: Token allowance too small");
if (isStableCoin) {
stableCoinBalances[tokenAddress] = stableCoinBalances[tokenAddress].add(amount);
if (tokenAddress == usdt) {
EIP20NonStandardInterface token = EIP20NonStandardInterface(tokenAddress);
token.transferFrom(msg.sender, address(this), amount);
} else {
IERC20(tokenAddress).transferFrom(msg.sender, address(this), amount);
}
_transferToCompound(tokenAddress, amount);
} else {
IERC20(tokenAddress).transferFrom(msg.sender, address(this), amount);
}
if (referrer != address(0)) {
processReferrals(referrer);
}
balances[msg.sender][tokenAddress] = balances[msg.sender][tokenAddress].add(amount);
uint128 currentEpoch = getCurrentEpoch();
uint128 currentMultiplier = currentEpochMultiplier();
if (!epochIsInitialized(tokenAddress, currentEpoch)) {
address[] memory tokens = new address[](1);
tokens[0] = tokenAddress;
manualEpochInit(tokens, currentEpoch);
}
Pool storage pNextEpoch = poolSize[tokenAddress][currentEpoch + 1];
if (isStableCoin) {
pNextEpoch.size = stableCoinBalances[tokenAddress];
} else {
pNextEpoch.size = IERC20(tokenAddress).balanceOf(address(this));
}
pNextEpoch.set = true;
Checkpoint[] storage checkpoints = balanceCheckpoints[msg.sender][tokenAddress];
uint256 balanceBefore = getEpochUserBalance(msg.sender, tokenAddress, currentEpoch);
if (checkpoints.length == 0) {
checkpoints.push(Checkpoint(currentEpoch, currentMultiplier, 0, amount));
checkpoints.push(Checkpoint(currentEpoch + 1, BASE_MULTIPLIER, amount, 0));
} else {
uint256 last = checkpoints.length - 1;
if (checkpoints[last].epochId < currentEpoch) {
uint128 multiplier = computeNewMultiplier(
getCheckpointBalance(checkpoints[last]),
BASE_MULTIPLIER,
amount,
currentMultiplier
);
checkpoints.push(Checkpoint(currentEpoch, multiplier, getCheckpointBalance(checkpoints[last]), amount));
checkpoints.push(Checkpoint(currentEpoch + 1, BASE_MULTIPLIER, balances[msg.sender][tokenAddress], 0));
}
else if (checkpoints[last].epochId == currentEpoch) {
checkpoints[last].multiplier = computeNewMultiplier(
getCheckpointBalance(checkpoints[last]),
checkpoints[last].multiplier,
amount,
currentMultiplier
);
checkpoints[last].newDeposits = checkpoints[last].newDeposits.add(amount);
checkpoints.push(Checkpoint(currentEpoch + 1, BASE_MULTIPLIER, balances[msg.sender][tokenAddress], 0));
}
else {
if (last >= 1 && checkpoints[last - 1].epochId == currentEpoch) {
checkpoints[last - 1].multiplier = computeNewMultiplier(
getCheckpointBalance(checkpoints[last - 1]),
checkpoints[last - 1].multiplier,
amount,
currentMultiplier
);
checkpoints[last - 1].newDeposits = checkpoints[last - 1].newDeposits.add(amount);
}
checkpoints[last].startBalance = balances[msg.sender][tokenAddress];
}
}
uint256 balanceAfter = getEpochUserBalance(msg.sender, tokenAddress, currentEpoch);
poolSize[tokenAddress][currentEpoch].size = poolSize[tokenAddress][currentEpoch].size.add(balanceAfter.sub(balanceBefore));
emit Deposit(msg.sender, tokenAddress, amount);
}
function updateReferrersPercentage(uint256 first, uint256 second) external {
require(msg.sender == _owner, "Only owner can perfrom this action");
firstReferrerRewardPercentage = first;
secondReferrerRewardPercentage = second;
}
function processReferrals(address referrer) internal {
if (hasReferrer(msg.sender) || !notContract(referrer) || referrer == msg.sender) {
return;
}
if (referrals[referrer] == msg.sender || referrals[referrals[referrer]] == msg.sender) {
return;
}
if (balanceOf(msg.sender, usdc) > 0 || balanceOf(msg.sender, usdt) > 0 || balanceOf(msg.sender, dai) > 0 || balanceOf(msg.sender, wbtcSwappLP) > 0) {
return;
}
referrals[msg.sender] = referrer;
Referrer storage refData = referrers[referrer];
refData.referralsCount = refData.referralsCount.add(1);
refData.referrals[refData.referralsCount] = msg.sender;
emit RegisteredReferer(msg.sender, referrer);
}
function hasReferrer(address addr) public view returns(bool) {
return referrals[addr] != address(0);
}
function getReferralById(address referrer, uint256 id) public view returns (address) {
return referrers[referrer].referrals[id];
}
function _transferToCompound(address tokenAddress, uint256 amount) internal {
address cToken = _getCompoundToken(tokenAddress);
IERC20(tokenAddress).safeApprove(cToken, amount);
CTokenInterface(cToken).mint(amount);
}
function _redeemFromCompound(address tokenAddress, uint256 amount) internal {
address cToken = _getCompoundToken(tokenAddress);
CTokenInterface(cToken).redeemUnderlying(amount);
}
function _getCompoundToken(address tokenAddress) internal pure returns (address cToken) {
if (tokenAddress == usdc) {
return cUsdc;
}
if (tokenAddress == usdt) {
return cUsdt;
}
if (tokenAddress == dai) {
return cDai;
}
}
function checkInterestFromCompound(address tokenAddress) external returns (uint256 interest){
bool isStableCoin = checkStableCoin(tokenAddress);
require(isStableCoin, "Wrong token address");
address cToken = _getCompoundToken(tokenAddress);
uint256 cTBalance = CTokenInterface(cToken).balanceOf(address(this));
if (cTBalance == 0) {
emit CheckInterest(cTBalance, 0, 0);
return 0;
}
uint256 cBalance = CTokenInterface(cToken).balanceOfUnderlying(address(this));
uint256 balance = stableCoinBalances[tokenAddress];
if (balance >= cBalance) {
emit CheckInterest(cBalance, balance, 0);
return 0;
}
uint256 _interest = cBalance.sub(balance);
emit CheckInterest(cBalance, balance, _interest);
return _interest;
}
function getInterestFromCompound(address tokenAddress) external nonReentrant{
bool isStableCoin = checkStableCoin(tokenAddress);
require(isStableCoin, "Wrong token address");
address cToken = _getCompoundToken(tokenAddress);
uint256 cTBalance = CTokenInterface(cToken).balanceOf(address(this));
require(cTBalance > 0, "There is no interest to withdraw");
uint256 cBalance = CTokenInterface(cToken).balanceOfUnderlying(address(this));
uint256 balance = stableCoinBalances[tokenAddress];
require(cBalance > balance, "There is no interest to withdraw");
uint256 interest = cBalance.sub(balance).mul(4).div(5);
if (interest > 0) {
CTokenInterface(cToken).redeemUnderlying(interest);
if (tokenAddress == usdt) {
EIP20NonStandardInterface(tokenAddress).transfer(TEAM_ADDRESS, interest);
} else {
IERC20(tokenAddress).transfer(TEAM_ADDRESS, interest);
}
emit GetInterest(tokenAddress, interest);
}
}
function getInterest(address tokenAddress) external nonReentrant{
bool isStableCoin = checkStableCoin(tokenAddress);
require(isStableCoin, "Wrong token address");
address cToken = _getCompoundToken(tokenAddress);
uint256 cBalance = CTokenInterface(cToken).balanceOf(address(this));
require(cBalance > 0, "No funds to withdraw");
uint256 balance = stableCoinBalances[tokenAddress];
CTokenInterface(cToken).redeem(cBalance);
uint256 tokenBalance = IERC20(tokenAddress).balanceOf(address(this));
uint256 interest = tokenBalance.sub(balance);
if (interest > 0) {
if (tokenAddress == usdt) {
EIP20NonStandardInterface(tokenAddress).transfer(TEAM_ADDRESS, interest);
} else {
IERC20(tokenAddress).transfer(TEAM_ADDRESS, interest);
}
emit GetInterest(tokenAddress, interest);
}
if (balance > 0) {
IERC20(tokenAddress).safeApprove(cToken, balance);
CTokenInterface(cToken).mint(balance);
}
}
function withdraw(address tokenAddress, uint256 amount) public nonReentrant {
require(balances[msg.sender][tokenAddress] >= amount, "Staking: balance too small");
bool isStableCoin = checkStableCoin(tokenAddress);
balances[msg.sender][tokenAddress] = balances[msg.sender][tokenAddress].sub(amount);
if (isStableCoin) {
stableCoinBalances[tokenAddress] = stableCoinBalances[tokenAddress].sub(amount);
_redeemFromCompound(tokenAddress, amount);
if (tokenAddress == usdt) {
EIP20NonStandardInterface(tokenAddress).transfer(msg.sender, amount);
} else {
IERC20(tokenAddress).transfer(msg.sender, amount);
}
} else {
IERC20(tokenAddress).transfer(msg.sender, amount);
}
uint128 currentEpoch = getCurrentEpoch();
lastWithdrawEpochId[tokenAddress] = currentEpoch;
if (!epochIsInitialized(tokenAddress, currentEpoch)) {
address[] memory tokens = new address[](1);
tokens[0] = tokenAddress;
manualEpochInit(tokens, currentEpoch);
}
Pool storage pNextEpoch = poolSize[tokenAddress][currentEpoch + 1];
if (isStableCoin) {
pNextEpoch.size = stableCoinBalances[tokenAddress];
} else {
pNextEpoch.size = IERC20(tokenAddress).balanceOf(address(this));
}
pNextEpoch.set = true;
Checkpoint[] storage checkpoints = balanceCheckpoints[msg.sender][tokenAddress];
uint256 last = checkpoints.length - 1;
if (checkpoints[last].epochId < currentEpoch) {
checkpoints.push(Checkpoint(currentEpoch, BASE_MULTIPLIER, balances[msg.sender][tokenAddress], 0));
poolSize[tokenAddress][currentEpoch].size = poolSize[tokenAddress][currentEpoch].size.sub(amount);
}
else if (checkpoints[last].epochId == currentEpoch) {
checkpoints[last].startBalance = balances[msg.sender][tokenAddress];
checkpoints[last].newDeposits = 0;
checkpoints[last].multiplier = BASE_MULTIPLIER;
poolSize[tokenAddress][currentEpoch].size = poolSize[tokenAddress][currentEpoch].size.sub(amount);
}
else {
Checkpoint storage currentEpochCheckpoint = checkpoints[last - 1];
uint256 balanceBefore = getCheckpointEffectiveBalance(currentEpochCheckpoint);
if (amount < currentEpochCheckpoint.newDeposits) {
uint128 avgDepositMultiplier = uint128(
balanceBefore.sub(currentEpochCheckpoint.startBalance).mul(BASE_MULTIPLIER).div(currentEpochCheckpoint.newDeposits)
);
currentEpochCheckpoint.newDeposits = currentEpochCheckpoint.newDeposits.sub(amount);
currentEpochCheckpoint.multiplier = computeNewMultiplier(
currentEpochCheckpoint.startBalance,
BASE_MULTIPLIER,
currentEpochCheckpoint.newDeposits,
avgDepositMultiplier
);
} else {
currentEpochCheckpoint.startBalance = currentEpochCheckpoint.startBalance.sub(
amount.sub(currentEpochCheckpoint.newDeposits)
);
currentEpochCheckpoint.newDeposits = 0;
currentEpochCheckpoint.multiplier = BASE_MULTIPLIER;
}
uint256 balanceAfter = getCheckpointEffectiveBalance(currentEpochCheckpoint);
poolSize[tokenAddress][currentEpoch].size = poolSize[tokenAddress][currentEpoch].size.sub(balanceBefore.sub(balanceAfter));
checkpoints[last].startBalance = balances[msg.sender][tokenAddress];
}
emit Withdraw(msg.sender, tokenAddress, amount);
}
function manualEpochInit(address[] memory tokens, uint128 epochId) public {
require(epochId <= getCurrentEpoch(), "can't init a future epoch");
for (uint i = 0; i < tokens.length; i++) {
Pool storage p = poolSize[tokens[i]][epochId];
if (epochId == 0) {
p.size = uint256(0);
p.set = true;
} else {
require(!epochIsInitialized(tokens[i], epochId), "Staking: epoch already initialized");
require(epochIsInitialized(tokens[i], epochId - 1), "Staking: previous epoch not initialized");
p.size = poolSize[tokens[i]][epochId - 1].size;
p.set = true;
}
}
emit ManualEpochInit(msg.sender, epochId, tokens);
}
function emergencyWithdraw(address tokenAddress) public {
bool isStableCoin = checkStableCoin(tokenAddress);
require(!isStableCoin, "Cant withdraw stable coins");
require((getCurrentEpoch() - lastWithdrawEpochId[tokenAddress]) >= 10, "At least 10 epochs must pass without success");
uint256 totalUserBalance = balances[msg.sender][tokenAddress];
require(totalUserBalance > 0, "Amount must be > 0");
balances[msg.sender][tokenAddress] = 0;
IERC20 token = IERC20(tokenAddress);
token.transfer(msg.sender, totalUserBalance);
emit EmergencyWithdraw(msg.sender, tokenAddress, totalUserBalance);
}
function getEpochUserBalance(address user, address token, uint128 epochId) public view returns (uint256) {
Checkpoint[] storage checkpoints = balanceCheckpoints[user][token];
if (checkpoints.length == 0 || epochId < checkpoints[0].epochId) {
return 0;
}
uint min = 0;
uint max = checkpoints.length - 1;
if (epochId >= checkpoints[max].epochId) {
return getCheckpointEffectiveBalance(checkpoints[max]);
}
while (max > min) {
uint mid = (max + min + 1) / 2;
if (checkpoints[mid].epochId <= epochId) {
min = mid;
} else {
max = mid - 1;
}
}
return getCheckpointEffectiveBalance(checkpoints[min]);
}
function balanceOf(address user, address token) public view returns (uint256) {
return balances[user][token];
}
function getCurrentEpoch() public view returns (uint128) {
if (block.timestamp < epoch1Start) {
return 0;
}
return uint128((block.timestamp - epoch1Start) / epochDuration + 1);
}
function getEpochPoolSize(address tokenAddress, uint128 epochId) public view returns (uint256) {
if (epochIsInitialized(tokenAddress, epochId)) {
return poolSize[tokenAddress][epochId].size;
}
if (!epochIsInitialized(tokenAddress, 0)) {
return 0;
}
IERC20 token = IERC20(tokenAddress);
if (checkStableCoin(tokenAddress)) {
return stableCoinBalances[tokenAddress];
}
return token.balanceOf(address(this));
}
function currentEpochMultiplier() public view returns (uint128) {
uint128 currentEpoch = getCurrentEpoch();
uint256 currentEpochEnd = epoch1Start + currentEpoch * epochDuration;
uint256 timeLeft = currentEpochEnd - block.timestamp;
uint128 multiplier = uint128(timeLeft * BASE_MULTIPLIER / epochDuration);
return multiplier;
}
function computeNewMultiplier(uint256 prevBalance, uint128 prevMultiplier, uint256 amount, uint128 currentMultiplier) public pure returns (uint128) {
uint256 prevAmount = prevBalance.mul(prevMultiplier).div(BASE_MULTIPLIER);
uint256 addAmount = amount.mul(currentMultiplier).div(BASE_MULTIPLIER);
uint128 newMultiplier = uint128(prevAmount.add(addAmount).mul(BASE_MULTIPLIER).div(prevBalance.add(amount)));
return newMultiplier;
}
function epochIsInitialized(address token, uint128 epochId) public view returns (bool) {
return poolSize[token][epochId].set;
}
function getCheckpointBalance(Checkpoint memory c) internal pure returns (uint256) {
return c.startBalance.add(c.newDeposits);
}
function getCheckpointEffectiveBalance(Checkpoint memory c) internal pure returns (uint256) {
return getCheckpointBalance(c).mul(c.multiplier).div(BASE_MULTIPLIER);
}
function notContract(address _addr) internal view returns (bool) {
uint32 size;
assembly { size := extcodesize(_addr) }
return (size == 0);
}
}
{
"compilationTarget": {
"Staking.sol": "Staking"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"uBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interest","type":"uint256"}],"name":"CheckInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"GetInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"uint128","name":"epochId","type":"uint128"},{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"ManualEpochInit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"referral","type":"address"},{"indexed":false,"internalType":"address","name":"referrer","type":"address"}],"name":"RegisteredReferer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"_owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cDai","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cUsdc","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cUsdt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"checkInterestFromCompound","outputs":[{"internalType":"uint256","name":"interest","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"checkStableCoin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"prevBalance","type":"uint256"},{"internalType":"uint128","name":"prevMultiplier","type":"uint128"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint128","name":"currentMultiplier","type":"uint128"}],"name":"computeNewMultiplier","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"currentEpochMultiplier","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dai","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epoch1Start","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"epochId","type":"uint128"}],"name":"epochIsInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"firstReferrerRewardPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpoch","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint128","name":"epochId","type":"uint128"}],"name":"getEpochPoolSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"epochId","type":"uint128"}],"name":"getEpochUserBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getInterestFromCompound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getReferralById","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"hasReferrer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint128","name":"epochId","type":"uint128"}],"name":"manualEpochInit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"referrals","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"referrers","outputs":[{"internalType":"uint256","name":"referralsCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"secondReferrerRewardPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stableCoinBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"first","type":"uint256"},{"internalType":"uint256","name":"second","type":"uint256"}],"name":"updateReferrersPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdc","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usdt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wbtcSwappLP","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]