文件 1 的 10:AccessControl.sol
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(uint160(account), 20),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
文件 2 的 10:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 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
) internal 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);
}
}
}
}
文件 3 的 10:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 4 的 10:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 5 的 10:IAccessControl.sol
pragma solidity ^0.8.0;
interface IAccessControl {
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
文件 6 的 10:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 7 的 10:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, 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 from,
address to,
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);
}
文件 8 的 10:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
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) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_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");
}
}
}
文件 9 的 10:Staking.sol
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
contract Staking is AccessControl {
using SafeERC20 for IERC20;
bytes32 public constant SPONSOR = keccak256("SPONSOR");
uint256 public constant PERCENTS_BASE = 100;
uint256 public constant MULTIPLIER = 10**19;
uint256 public constant YEAR = 365 days;
IERC20 public immutable KON;
uint256 public immutable deployTime;
IERC20 public rewardToken;
uint256 public maxPool = 20 * (10**6) * (10**18);
uint256 public inactiveTokensInPool;
uint256 public globalKoeffUSDTFW;
uint256 public globalKoeffKONFW;
uint256 public globalKoeffUSDTRW;
uint256 public globalKoeffKONRW;
uint256 public poolFullWeight;
uint256 public poolReducedWeight;
uint256 public penalty;
uint256 public penaltyInKONAfterChange;
uint256 public excessOfRewards;
uint256 public capacity = 10;
uint256[3] public percents = [15, 20, 25];
uint256[3] public totalStaked;
uint256[4] public updateIndexes;
uint256[4] public lastUpdate;
uint256[] public weight;
DepositInfo[] public allDeposits;
uint256 private _lock;
mapping(address => uint256[]) public indexForUser;
mapping(uint256 => uint256) public weightForDeposit;
mapping(address => WhiteListInfo) public whiteListForUser;
mapping(uint256 => RewardPool_3) public reward3Info;
mapping(uint256 => uint256) public lockOwnersDeposits;
struct WhiteListInfo {
uint256 index;
uint256 enteredAt;
uint256 amount;
uint256 lockUpWL;
}
struct DepositInfo {
Koeff varKoeff;
address user;
uint256 lockUp;
uint256 sumInLock;
uint256 enteredAt;
uint256 pool;
uint256 countHarvest;
bool gotFixed;
}
struct Koeff {
uint256 koeffBeforeDepositKON;
uint256 koeffBeforeDepositUSDT;
uint256 unreceivedRewardKON;
uint256 unreceivedRewardUSDT;
uint256 receivedRewardKON;
uint256 receivedRewardUSDT;
}
struct RewardPool_3 {
uint256 variableRewardTaken;
uint256 part;
}
struct IntVars {
uint256 rewardsUSDT;
uint256 rewardsKON;
uint256 amountPenalty;
uint256 amountKON;
uint256 amountUSDT;
}
event Deposit(address user, uint256 amount, uint256 lockUp, uint256 index);
event Withdraw(address user, uint256 index);
event Harvest(
address user,
uint256 amountKON,
uint256 amountUSDT,
uint256 index
);
event Reward(uint256 amount, uint256 time);
modifier update() {
updatePool();
_;
}
modifier creator(uint256 index) {
require(allDeposits[index].user == _msgSender(), "10");
_;
}
modifier depositeIndex(uint256 index) {
require(index < allDeposits.length, "0");
_;
}
modifier nonReentrant() {
require(_lock != 1, "1_");
_lock = 1;
_;
_lock = 0;
}
constructor(
address _kon,
address _owner,
address _sponsor
) {
require(_kon != address(0) && _owner != address(0), "1");
KON = IERC20(_kon);
rewardToken = IERC20(_kon);
weight.push(PERCENTS_BASE);
_setupRole(DEFAULT_ADMIN_ROLE, _owner);
_setupRole(SPONSOR, _sponsor);
deployTime = block.timestamp;
}
function indexes(address user) external view returns (uint256[] memory) {
return indexForUser[user];
}
function changeInternalVariables(
uint256 _maxPool,
uint256 _capacity,
uint256 _weight
) external update onlyRole(DEFAULT_ADMIN_ROLE) {
if (_maxPool != maxPool) {
require(_maxPool > 0 && _maxPool >= stakedSum(), "1");
maxPool = _maxPool;
}
if (_capacity != capacity) {
require(_capacity > 0, "2");
capacity = _capacity;
}
if (_weight != weight[weight.length - 1]) {
require(_weight <= PERCENTS_BASE, "3");
weight.push(_weight);
}
}
function updateAmountsOfRewards(uint256 reward)
external
update
onlyRole(SPONSOR)
nonReentrant
{
require(reward > 0, "1");
uint256 timestamp = block.timestamp;
for (uint256 j = 0; j < 4; j += 1) {
require(timestamp == lastUpdate[j], "2");
}
uint256 pool = stakedSum();
require(pool > 0, "3");
uint256 amount = reward;
if (penalty > 0) {
amount += penalty;
penalty = 0;
}
uint256 rewardRW = (amount * poolReducedWeight) / pool;
uint256 rewardFW = amount - rewardRW;
if (poolFullWeight == 0 && rewardFW != 0) excessOfRewards += rewardFW;
if (rewardToken == KON) {
if (rewardFW > 0 && poolFullWeight != 0)
globalKoeffKONFW += ((rewardFW * MULTIPLIER) / poolFullWeight);
if (rewardRW > 0)
globalKoeffKONRW += (rewardRW * MULTIPLIER) / poolReducedWeight;
} else {
if (rewardFW > 0 && poolFullWeight != 0)
globalKoeffUSDTFW += (rewardFW * MULTIPLIER) / poolFullWeight;
if (rewardRW > 0)
globalKoeffUSDTRW +=
(rewardRW * MULTIPLIER) /
poolReducedWeight;
}
rewardToken.safeTransferFrom(_msgSender(), address(this), reward);
emit Reward(reward, timestamp);
}
function setNewRewardToken(address usdt)
external
update
onlyRole(DEFAULT_ADMIN_ROLE)
{
require(usdt != address(0) && rewardToken == KON, "1");
uint256 timestamp = block.timestamp;
for (uint256 j = 0; j < 4; j += 1) {
require(timestamp == lastUpdate[j], "2");
}
uint256 pool = stakedSum();
if (penalty > 0 && pool > 0) {
uint256 rewardRW = (penalty * poolReducedWeight) / pool;
uint256 rewardFW = penalty - rewardRW;
if (rewardFW > 0 && poolFullWeight != 0)
globalKoeffKONFW += (rewardFW * MULTIPLIER) / poolFullWeight;
if (rewardRW > 0)
globalKoeffKONRW += (rewardRW * MULTIPLIER) / poolReducedWeight;
penalty = 0;
} else if(penalty > 0 && pool == 0) {
KON.safeTransfer(_msgSender(), penalty);
penalty = 0;
}
rewardToken = IERC20(usdt);
}
function getExcessToken()
external
update
onlyRole(DEFAULT_ADMIN_ROLE)
nonReentrant
{
require(penaltyInKONAfterChange > 0 || excessOfRewards > 0, "1");
if (penaltyInKONAfterChange > 0) {
KON.safeTransfer(_msgSender(), penaltyInKONAfterChange);
penaltyInKONAfterChange = 0;
}
if (excessOfRewards > 0) {
rewardToken.safeTransfer(_msgSender(), excessOfRewards);
excessOfRewards = 0;
}
}
function depositOrWLFromOwner(
uint256[] memory enteredAt,
uint256[] memory amount,
address[] memory addresses,
uint256[] memory lockUp,
bool[] memory isActualDeposit
) external update onlyRole(DEFAULT_ADMIN_ROLE) nonReentrant {
uint256 timestamp = block.timestamp;
require((timestamp - deployTime) / 1 days <= 3, "0");
uint256 len = addresses.length;
require(
len == amount.length &&
len == lockUp.length &&
len == enteredAt.length &&
len == isActualDeposit.length,
"1"
);
uint256 debt;
uint256 previousEnteredAt_;
address user;
WhiteListInfo memory wl;
Koeff memory koeff;
for (uint256 i = 0; i < len; i += 1) {
wl = WhiteListInfo(
allDeposits.length,
enteredAt[i],
amount[i],
lockUp[i]
);
user = addresses[i];
require(wl.amount > 0, "2");
require(wl.lockUpWL <= 2, "3");
require(user != address(0), "4");
require(
timestamp >= wl.enteredAt && wl.enteredAt >= previousEnteredAt_,
"5"
);
previousEnteredAt_ = wl.enteredAt;
indexForUser[user].push(wl.index);
weightForDeposit[wl.index] = 0;
if (!isActualDeposit[i]) {
whiteListForUser[user] = WhiteListInfo(
wl.index,
wl.enteredAt,
wl.amount,
wl.lockUpWL
);
koeff = Koeff(0, 0, 0, 0, 0, 0);
wl = WhiteListInfo(0, 0, 0, 0);
} else {
koeff = Koeff(globalKoeffKONFW, globalKoeffUSDTFW, 0, 0, 0, 0);
totalStaked[wl.lockUpWL] += wl.amount;
poolFullWeight += wl.amount;
debt += wl.amount;
}
allDeposits.push(
DepositInfo(
koeff,
user,
wl.lockUpWL,
wl.amount,
wl.enteredAt,
0,
0,
false
)
);
}
if (debt > 0) KON.safeTransferFrom(_msgSender(), address(this), debt);
}
function setLocks(uint256[] memory lockPeriod, uint256[] memory index) external onlyRole(DEFAULT_ADMIN_ROLE) {
require((block.timestamp - deployTime) / 1 days <= 3, "0");
uint256 len = index.length;
for (uint256 i = 0; i < len; i += 1) {
lockOwnersDeposits[index[i]] = lockPeriod[i];
}
}
function deposit(uint256 amount, uint256 lockUp)
external
update
nonReentrant
{
require(amount > 0, "1");
require(lockUp < 3, "2");
require(stakedSum() + amount <= maxPool, "3");
address user = _msgSender();
uint256 depLen = allDeposits.length;
uint256 weiLen = weight.length;
uint256 timestamp = block.timestamp;
uint256 globKon;
uint256 globUSDT;
WhiteListInfo memory whiteList_ = whiteListForUser[user];
if (
whiteList_.amount == amount &&
whiteList_.lockUpWL == lockUp &&
(timestamp - deployTime) / 14 days < 1
) {
timestamp = whiteList_.enteredAt;
depLen = whiteList_.index;
weightForDeposit[depLen] = 0;
} else {
weightForDeposit[depLen] = weiLen - 1;
}
if (weight[weightForDeposit[depLen]] == PERCENTS_BASE) {
poolFullWeight += amount;
globKon = globalKoeffKONFW;
globUSDT = globalKoeffUSDTFW;
} else {
poolReducedWeight += ((amount * weight[weiLen - 1]) /
PERCENTS_BASE);
globKon = globalKoeffKONRW;
globUSDT = globalKoeffUSDTRW;
}
totalStaked[lockUp] += amount;
DepositInfo memory dep = DepositInfo(
Koeff(globKon, globUSDT, 0, 0, 0, 0),
user,
lockUp,
amount,
timestamp,
0,
0,
false
);
if (depLen == allDeposits.length) {
indexForUser[user].push(depLen);
allDeposits.push(dep);
} else {
allDeposits[depLen] = dep;
delete whiteListForUser[user];
}
KON.safeTransferFrom(user, address(this), amount);
emit Deposit(user, amount, lockUp, depLen);
}
function harvest(uint256 index)
public
update
depositeIndex(index)
creator(index)
nonReentrant
{
(uint256 kon, uint256 usdt) = _harvest(index);
address user = _msgSender();
_transfers(user, kon, usdt);
emit Harvest(user, kon, usdt, index);
}
function withdraw(uint256 index)
external
update
depositeIndex(index)
creator(index)
nonReentrant
{
DepositInfo storage stake = allDeposits[index];
if(lockOwnersDeposits[index] != 0) {
require(block.timestamp >= lockOwnersDeposits[index], "00");
}
require(stake.sumInLock > 0, "1");
(uint256 year, uint256 months) = _amountOfYears(stake.enteredAt);
IntVars memory vars;
vars.rewardsKON = stake.sumInLock;
if (year <= stake.lockUp) {
uint256 rewKON;
if (
(stake.lockUp < 2 && year == stake.pool) ||
(stake.lockUp == 2 && year == stake.pool)
) {
vars.rewardsUSDT = stake.varKoeff.unreceivedRewardUSDT;
rewKON = stake.varKoeff.unreceivedRewardKON;
stake.varKoeff.receivedRewardKON += stake
.varKoeff
.unreceivedRewardKON;
stake.varKoeff.receivedRewardUSDT += stake
.varKoeff
.unreceivedRewardUSDT;
(vars.amountKON, vars.amountUSDT) = varPart(index);
} else {
(rewKON, vars.rewardsUSDT) = varPart(index);
}
vars.rewardsKON += currentFixedPart(index);
vars.amountPenalty = fixedPart(index) / 2;
vars.rewardsKON -= vars.amountPenalty;
vars.rewardsKON += rewKON;
if (rewardToken == KON) {
penalty += vars.amountKON;
penalty += vars.amountPenalty;
} else {
penaltyInKONAfterChange += vars.amountKON;
penaltyInKONAfterChange += vars.amountPenalty;
penalty += vars.amountUSDT;
}
} else if (
(!stake.gotFixed &&
(stake.lockUp < 2 ||
months >= reward3Info[index].variableRewardTaken))
) {
(vars.amountKON, vars.rewardsUSDT) = _harvest(index);
vars.rewardsKON += vars.amountKON;
}
if (
(stake.lockUp == 2 && stake.pool != 4) ||
(stake.lockUp < 2 && stake.pool < stake.lockUp + 1)
) _updateTotalStaked(stake.sumInLock, stake.lockUp, index);
else inactiveTokensInPool -= stake.sumInLock;
_transfers(stake.user, vars.rewardsKON, vars.rewardsUSDT);
emit Withdraw(stake.user, index);
delete allDeposits[index];
}
function stakedSum() public view returns (uint256 amount) {
for (uint256 i = 0; i < 3; i += 1) {
amount += totalStaked[i];
}
}
function currentFixedPart(uint256 index)
public
view
returns (uint256 amount)
{
DepositInfo memory stake = allDeposits[index];
(uint256 year, ) = _amountOfYears(stake.enteredAt);
uint256 i;
for (i; i < year && i <= stake.lockUp; i += 1) {
amount += percents[i];
}
amount += (amount * stake.sumInLock) / PERCENTS_BASE;
if (year < stake.lockUp + 1)
amount += (((15 + (5 * i)) *
stake.sumInLock *
(block.timestamp - (stake.enteredAt + YEAR * year))) /
(PERCENTS_BASE * YEAR));
}
function fixedPart(uint256 index) public view returns (uint256 amount) {
DepositInfo memory stake = allDeposits[index];
for (uint256 i = 0; i < 3 && i <= stake.lockUp; i += 1) {
amount += percents[i];
}
amount = (amount * stake.sumInLock) / PERCENTS_BASE;
}
function varPart(uint256 index)
public
view
returns (uint256 inKON, uint256 inUSDT)
{
DepositInfo memory stake = allDeposits[index];
uint256 weight_ = weight[weightForDeposit[index]];
if (
(stake.lockUp < 2 && stake.pool != stake.lockUp + 1) ||
(stake.lockUp == 2 && stake.pool != 4)
) {
if (weight_ == PERCENTS_BASE) {
inKON = ((stake.sumInLock *
(globalKoeffKONFW - stake.varKoeff.koeffBeforeDepositKON)) /
MULTIPLIER -
stake.varKoeff.receivedRewardKON);
if (globalKoeffUSDTFW != 0)
inUSDT = ((stake.sumInLock *
(globalKoeffUSDTFW -
stake.varKoeff.koeffBeforeDepositUSDT)) /
MULTIPLIER -
stake.varKoeff.receivedRewardUSDT);
} else {
uint256 amount = (stake.sumInLock * weight_) / PERCENTS_BASE;
inKON = ((amount *
(globalKoeffKONRW - stake.varKoeff.koeffBeforeDepositKON)) /
MULTIPLIER -
stake.varKoeff.receivedRewardKON);
if (globalKoeffUSDTRW != 0)
inUSDT = ((amount *
(globalKoeffUSDTRW -
stake.varKoeff.koeffBeforeDepositUSDT)) /
MULTIPLIER -
stake.varKoeff.receivedRewardUSDT);
}
} else
return (
stake.varKoeff.unreceivedRewardKON,
stake.varKoeff.unreceivedRewardUSDT
);
}
function updatePool() public {
uint256 len = allDeposits.length;
uint256 year;
DepositInfo storage stake;
uint256 i;
uint256 limit;
for (uint256 j = 0; j < 4; j += 1) {
i = updateIndexes[j];
limit = (i + capacity > len) ? len : i + capacity;
for (i; i < limit; i += 1) {
stake = allDeposits[i];
(year, ) = _amountOfYears(stake.enteredAt);
if (year > j) {
if (
stake.sumInLock > 0 &&
((stake.lockUp < 2 && stake.pool <= stake.lockUp) ||
(stake.lockUp == 2 && stake.pool < 4))
) {
(
stake.varKoeff.unreceivedRewardKON,
stake.varKoeff.unreceivedRewardUSDT
) = varPart(i);
if (
(j < 2 && stake.lockUp == j) ||
(j == 3 && stake.lockUp == 2)
) {
_updateTotalStaked(
stake.sumInLock,
stake.lockUp,
i
);
inactiveTokensInPool += stake.sumInLock;
}
stake.pool += 1;
}
updateIndexes[j] = i + 1;
} else {
lastUpdate[j] = block.timestamp;
break;
}
}
if (i == len) lastUpdate[j] = block.timestamp;
}
}
function _amountOfYears(uint256 start)
private
view
returns (uint256 amount, uint256 months)
{
amount = (block.timestamp - start) / YEAR;
if (amount >= 3)
months = (block.timestamp - (start + (3 * YEAR))) / 30 days;
}
function _transfers(
address user,
uint256 toTransferKON,
uint256 toTransferUSDT
) private {
require(toTransferKON > 0 || toTransferUSDT > 0, "01");
if (toTransferKON > 0) {
require(
KON.balanceOf(address(this)) -
stakedSum() -
inactiveTokensInPool >=
toTransferKON,
"02"
);
KON.safeTransfer(user, toTransferKON);
}
if (toTransferUSDT > 0) {
require(
rewardToken.balanceOf(address(this)) >= toTransferUSDT,
"03"
);
rewardToken.safeTransfer(user, toTransferUSDT);
}
}
function _updateTotalStaked(
uint256 amount,
uint256 lockUp,
uint256 index
) private {
totalStaked[lockUp] -= amount;
if (weight[weightForDeposit[index]] == PERCENTS_BASE)
poolFullWeight -= amount;
else
poolReducedWeight -= ((amount * weight[weightForDeposit[index]]) /
PERCENTS_BASE);
}
function _harvest(uint256 index) private returns (uint256, uint256) {
DepositInfo storage stake = allDeposits[index];
RewardPool_3 storage reward = reward3Info[index];
require(stake.sumInLock != 0, "1");
(uint256 year, uint256 months) = _amountOfYears(stake.enteredAt);
IntVars memory vars;
if (stake.lockUp == 2 && year >= 3) {
require(months >= reward.variableRewardTaken, "2");
if (reward.part == 0) reward.part = fixedPart(index) / 6;
if (reward.variableRewardTaken < 6) {
if (months > 5) {
vars.amountKON =
reward.part *
(6 - reward.variableRewardTaken);
} else
vars.amountKON =
reward.part *
(months + 1 - reward.variableRewardTaken);
}
reward.variableRewardTaken = months + 1;
}
if (
(stake.lockUp < 2 &&
(stake.pool >= stake.lockUp + 1 || year == stake.pool)) ||
(stake.lockUp == 2 &&
((year < 3 && year == stake.pool) || stake.pool == 4))
) {
vars.rewardsKON += stake.varKoeff.unreceivedRewardKON;
vars.rewardsUSDT += stake.varKoeff.unreceivedRewardUSDT;
} else {
(vars.rewardsKON, vars.rewardsUSDT) = varPart(index);
if (
stake.lockUp < 2 ||
(stake.lockUp == 2 && months > 11 && stake.pool != 4)
) stake.pool += 1;
if (
(stake.lockUp < 2 && stake.lockUp + 1 == stake.pool) ||
(stake.lockUp == 2 && stake.pool == 4)
) {
_updateTotalStaked(stake.sumInLock, stake.lockUp, index);
inactiveTokensInPool += stake.sumInLock;
}
}
if (
stake.lockUp < 2 &&
stake.pool >= stake.lockUp + 1 &&
!stake.gotFixed
) {
vars.amountKON = fixedPart(index);
stake.gotFixed = true;
}
stake.varKoeff.receivedRewardKON += vars.rewardsKON;
stake.varKoeff.receivedRewardUSDT += vars.rewardsUSDT;
stake.varKoeff.unreceivedRewardKON = 0;
stake.varKoeff.unreceivedRewardUSDT = 0;
if (year <= stake.lockUp + 1) stake.countHarvest = year;
else stake.countHarvest = stake.lockUp + 1;
vars.rewardsKON += vars.amountKON;
return (vars.rewardsKON, vars.rewardsUSDT);
}
}
文件 10 的 10:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
{
"compilationTarget": {
"contracts/Staking.sol": "Staking"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "none",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_kon","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_sponsor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockUp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountKON","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountUSDT","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"Harvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"}],"name":"Reward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KON","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MULTIPLIER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENTS_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SPONSOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"YEAR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allDeposits","outputs":[{"components":[{"internalType":"uint256","name":"koeffBeforeDepositKON","type":"uint256"},{"internalType":"uint256","name":"koeffBeforeDepositUSDT","type":"uint256"},{"internalType":"uint256","name":"unreceivedRewardKON","type":"uint256"},{"internalType":"uint256","name":"unreceivedRewardUSDT","type":"uint256"},{"internalType":"uint256","name":"receivedRewardKON","type":"uint256"},{"internalType":"uint256","name":"receivedRewardUSDT","type":"uint256"}],"internalType":"struct Staking.Koeff","name":"varKoeff","type":"tuple"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"lockUp","type":"uint256"},{"internalType":"uint256","name":"sumInLock","type":"uint256"},{"internalType":"uint256","name":"enteredAt","type":"uint256"},{"internalType":"uint256","name":"pool","type":"uint256"},{"internalType":"uint256","name":"countHarvest","type":"uint256"},{"internalType":"bool","name":"gotFixed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"capacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxPool","type":"uint256"},{"internalType":"uint256","name":"_capacity","type":"uint256"},{"internalType":"uint256","name":"_weight","type":"uint256"}],"name":"changeInternalVariables","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"currentFixedPart","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"lockUp","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"enteredAt","type":"uint256[]"},{"internalType":"uint256[]","name":"amount","type":"uint256[]"},{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"uint256[]","name":"lockUp","type":"uint256[]"},{"internalType":"bool[]","name":"isActualDeposit","type":"bool[]"}],"name":"depositOrWLFromOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"excessOfRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"fixedPart","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExcessToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalKoeffKONFW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalKoeffKONRW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalKoeffUSDTFW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalKoeffUSDTRW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inactiveTokensInPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"indexForUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"indexes","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"lastUpdate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"lockOwnersDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"penalty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"penaltyInKONAfterChange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"percents","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolFullWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolReducedWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"reward3Info","outputs":[{"internalType":"uint256","name":"variableRewardTaken","type":"uint256"},{"internalType":"uint256","name":"part","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"lockPeriod","type":"uint256[]"},{"internalType":"uint256[]","name":"index","type":"uint256[]"}],"name":"setLocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"usdt","type":"address"}],"name":"setNewRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakedSum","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"name":"updateAmountsOfRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"updateIndexes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updatePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"varPart","outputs":[{"internalType":"uint256","name":"inKON","type":"uint256"},{"internalType":"uint256","name":"inUSDT","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"weight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"weightForDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whiteListForUser","outputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"enteredAt","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"lockUpWL","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]