编译器
0.8.19+commit.7dd6d404
文件 1 的 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);
}
}
}
}
文件 2 的 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;
}
}
文件 3 的 10:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
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);
}
文件 4 的 10:IPlatformAdminPanel.sol
pragma solidity 0.8.19;
interface IPlatformAdminPanel {
function isAdmin(address wallet) external view returns (bool);
}
文件 5 的 10:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 6 的 10:PROPCStakingV2.sol
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "../access_controller/PlatformAccessController.sol";
interface IPropcToken {
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address account) external returns (uint256);
function allowance(address owner, address spender) external returns (uint256);
}
contract PROPCStakingV2 is PlatformAccessController {
using SafeERC20 for IERC20;
struct UserPoolInfo {
uint256 totalAmountInPool;
}
struct APYInfo {
uint256 apyPercent;
uint256 startTime;
uint256 stopTime;
}
struct PoolInfo {
uint256 startTime;
IERC20 rewardsToken;
uint256 totalStaked;
uint256 maxTotalStake;
bool active;
uint256 claimTimeLimit;
uint256 minStakeAmount;
uint256 penaltyFee;
uint256 penaltyTimeLimit;
address penaltyWallet;
bool isVIPPool;
mapping (address => bool) isVIPAddress;
mapping (uint256 => APYInfo) apyInfo;
uint256 lastAPYIndex;
bool decreasingPenalty;
}
struct Stake {
uint256 amount;
uint256 stakeTimestamp;
uint256 totalRedeemed;
uint256 lastClaimTimestamp;
}
IPropcToken public immutable propcToken;
address public rewardsWallet;
uint256 public totalPools;
mapping(uint256 => PoolInfo) private poolInfo;
mapping (uint256 => mapping (address => UserPoolInfo)) private userPoolInfo;
mapping (uint256 => mapping(address => Stake[])) public stakes;
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
event Redeem(address indexed user, uint256 indexed pid, uint256 amount);
event RewardsWalletUpdate(address indexed wallet);
event PoolSet(
address indexed admin,
uint256 indexed pid,
uint256 _apyPercent,
uint256 _claimTimeLimit,
uint256 _penaltyFee,
uint256 _penaltyTimeLimit,
bool _active,
bool _isVIPPool,
bool _hasDecreasingPenalty
);
event NewVIP(address indexed user);
event NoVIP(address indexed user);
event DeleteStake(address indexed user, uint256 indexed pid);
error PoolNotOpened();
error IndexOutOfBounds();
error PoolInactive();
error NotVIPQualified();
error BelowPoolMinimum();
error NotEnoughStaked();
error NoRewardsAvailable();
error ZeroAddress();
error InsufficientBalance(uint256);
error InsufficientRewards();
error PoolDoesNotExist();
error InsufficientStakeLimit();
error APYInvalid();
error PenaltyTooHigh();
error NotVIPPool();
error TooManyStakes(uint256);
error ZeroAmount();
uint256 public constant YEAR = 365 days;
uint256 internal constant APY_DIVIDER = 10_000;
constructor(
IPropcToken _propc,
address _rewardsWallet,
address adminPanel
) {
if(adminPanel == address(0))
revert ZeroAddress();
if(_rewardsWallet == address(0))
revert ZeroAddress();
propcToken = _propc;
rewardsWallet = _rewardsWallet;
_initiatePlatformAccessController(adminPanel);
}
function updateRewardsWallet(address _wallet) external onlyPlatformAdmin {
if(_wallet == address(0))
revert ZeroAddress();
rewardsWallet = _wallet;
emit RewardsWalletUpdate(_wallet);
}
function poolLength() public view returns (uint256) {
return totalPools;
}
function setPool(
uint256 _idToChange,
uint256 _startTime,
IERC20 _rewardsToken,
uint256 _apyPercent,
uint256 _claimTimeLimit,
uint256 _penaltyFee,
uint256 _penaltyTimeLimit,
bool _active,
address _penaltyWallet,
bool _isVIPPool,
bool _hasDecreasingPenalty,
uint256 _minStakeAmount,
uint256 _maxTotalStake
) external onlyPlatformAdmin {
uint256 pid = _idToChange == 0 ? ++totalPools : _idToChange;
PoolInfo storage pool = poolInfo[pid];
if(_idToChange > 0 && pool.lastAPYIndex == 0)
revert PoolDoesNotExist();
if(_idToChange == 0 && _apyPercent == 0)
revert APYInvalid();
if(_penaltyFee > 3500)
revert PenaltyTooHigh();
if(_maxTotalStake == 0)
revert InsufficientStakeLimit();
if(_penaltyWallet == address(0))
revert ZeroAddress();
if (_idToChange == 0) {
pool.startTime = _startTime;
}
if (_apyPercent != pool.apyInfo[pool.lastAPYIndex].apyPercent) {
pool.apyInfo[pool.lastAPYIndex].stopTime = block.timestamp;
pool.lastAPYIndex ++;
APYInfo storage apyInfo = pool.apyInfo[pool.lastAPYIndex];
apyInfo.apyPercent = _apyPercent;
apyInfo.startTime = block.timestamp;
}
pool.rewardsToken = _rewardsToken;
pool.minStakeAmount = _minStakeAmount;
pool.claimTimeLimit = _claimTimeLimit;
pool.penaltyFee = _penaltyFee;
pool.penaltyTimeLimit = _penaltyTimeLimit;
pool.active = _active;
pool.penaltyWallet = _penaltyWallet;
pool.isVIPPool = _isVIPPool;
pool.decreasingPenalty = _hasDecreasingPenalty;
pool.maxTotalStake = _maxTotalStake;
emit PoolSet(
msgSender(),
pid,
_apyPercent,
_claimTimeLimit,
_penaltyFee,
_penaltyTimeLimit,
_active,
_isVIPPool,
_hasDecreasingPenalty
);
}
function addVIPAddress(uint256 _pid, address _vipAddress) external onlyPlatformAdmin {
PoolInfo storage pool = poolInfo[_pid];
if(!pool.isVIPPool)
revert NotVIPPool();
if(_vipAddress == address(0))
revert ZeroAddress();
pool.isVIPAddress[_vipAddress] = true;
emit NewVIP(_vipAddress);
}
function addVIPAddresses(uint256 _pid, address[] memory _vipAddresses) external onlyPlatformAdmin {
PoolInfo storage pool = poolInfo[_pid];
if(!pool.isVIPPool)
revert NotVIPPool();
for (uint256 i = 0; i < _vipAddresses.length; i++) {
if(_vipAddresses[i] == address(0))
revert ZeroAddress();
pool.isVIPAddress[_vipAddresses[i]] = true;
emit NewVIP(_vipAddresses[i]);
}
}
function removeVIPAddress(uint256 _pid, address _vipAddress) external onlyPlatformAdmin {
PoolInfo storage pool = poolInfo[_pid];
if(!pool.isVIPPool)
revert NotVIPPool();
if(_vipAddress == address(0))
revert ZeroAddress();
pool.isVIPAddress[_vipAddress] = false;
emit NoVIP(_vipAddress);
}
function removeVIPAddresses(uint256 _pid, address[] memory _vipAddresses) external onlyPlatformAdmin {
PoolInfo storage pool = poolInfo[_pid];
if(!pool.isVIPPool)
revert NotVIPPool();
for (uint256 i = 0; i < _vipAddresses.length; i++) {
if(_vipAddresses[i] == address(0))
revert ZeroAddress();
pool.isVIPAddress[_vipAddresses[i]] = false;
emit NoVIP(_vipAddresses[i]);
}
}
function getTimespanInSeconds(uint256 _from, uint256 _to) public pure returns (uint256) {
return _to - _from;
}
function _max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function rewardsForPool(uint _pid, address _user) public view returns (uint256) {
Stake[] memory _stakes = stakes[_pid][_user];
uint256 rewards = 0;
for(uint256 i = 0; i < _stakes.length; i++) {
rewards = rewards + _pendingRewardsForStake(_pid, _stakes[i]);
}
return rewards;
}
function pendingRewardsForStake(uint256 _pid, address _user, uint256 _stakeId) external view returns (uint256) {
Stake[] memory _stakes = stakes[_pid][_user];
if(_stakes.length <= _stakeId)
revert IndexOutOfBounds();
return _pendingRewardsForStake(_pid, _stakes[_stakeId]);
}
function _pendingRewardsForStake(uint256 _pid, Stake memory _stake) internal view returns (uint256) {
PoolInfo storage pool = poolInfo[_pid];
uint256 pendingRewards = 0;
for (uint256 apyIndex = pool.lastAPYIndex; apyIndex > 0; apyIndex--) {
if (pool.apyInfo[apyIndex].stopTime > 0 && _stake.lastClaimTimestamp >= pool.apyInfo[apyIndex].stopTime) {
continue;
}
if(pool.claimTimeLimit + _stake.lastClaimTimestamp > block.timestamp) {
continue;
}
if (pool.apyInfo[apyIndex].apyPercent == 0) {
continue;
}
uint256 _fromTime = _max(_stake.lastClaimTimestamp, pool.apyInfo[apyIndex].startTime);
uint256 _toTime = block.timestamp;
if (pool.apyInfo[apyIndex].stopTime > 0 && block.timestamp > pool.apyInfo[apyIndex].stopTime) {
_toTime = pool.apyInfo[apyIndex].stopTime;
}
if (_fromTime >= _toTime) {
continue;
}
uint256 timespanInPool = getTimespanInSeconds(_fromTime, _toTime);
uint256 rewardsPerAPYBlock = (timespanInPool * pool.apyInfo[apyIndex].apyPercent * _stake.amount) / (YEAR * APY_DIVIDER);
pendingRewards = pendingRewards + rewardsPerAPYBlock;
}
return pendingRewards;
}
function _penalty(Stake memory _stake, PoolInfo storage pool, uint256 _amount) internal view returns (uint256) {
uint256 penaltyAmount = 0;
if (_stake.stakeTimestamp + pool.penaltyTimeLimit <= block.timestamp)
return penaltyAmount;
if(pool.decreasingPenalty) {
uint256 appliedPenalty = block.timestamp - _stake.stakeTimestamp;
appliedPenalty = (pool.penaltyTimeLimit - appliedPenalty) * APY_DIVIDER / pool.penaltyTimeLimit;
penaltyAmount = _amount * pool.penaltyFee * appliedPenalty / (APY_DIVIDER * APY_DIVIDER);
} else {
penaltyAmount = _amount * pool.penaltyFee / APY_DIVIDER;
}
return penaltyAmount;
}
function allPendingRewardsToken(address _user) external view returns (uint256[] memory) {
uint256 length = poolLength();
uint256[] memory pendingRewards = new uint256[](length);
for(uint256 _pid = 1; _pid <= length; _pid++) {
pendingRewards[_pid - 1] = rewardsForPool(_pid, _user);
}
return pendingRewards;
}
function stake(uint256 _pid, uint256 _amount) external {
PoolInfo storage pool = poolInfo[_pid];
UserPoolInfo storage user = userPoolInfo[_pid][msg.sender];
Stake[] storage _stakes = stakes[_pid][msg.sender];
if(_amount == 0)
revert ZeroAmount();
if(_stakes.length >= 10)
revert TooManyStakes(_pid);
if(pool.startTime > block.timestamp)
revert PoolNotOpened();
if(!pool.active)
revert PoolInactive();
if(pool.isVIPPool && !pool.isVIPAddress[msg.sender])
revert NotVIPQualified();
if(user.totalAmountInPool + _amount < pool.minStakeAmount)
revert BelowPoolMinimum();
if(_amount + pool.totalStaked > pool.maxTotalStake)
revert InsufficientStakeLimit();
propcToken.transferFrom(msg.sender, address(this), _amount);
_stakes.push(
Stake(
_amount,
block.timestamp,
0,
block.timestamp
)
);
user.totalAmountInPool = user.totalAmountInPool + _amount;
pool.totalStaked = pool.totalStaked + _amount;
emit Deposit(msg.sender, _pid, _amount);
}
function leavePool(uint256 _pid) external {
PoolInfo storage pool = poolInfo[_pid];
UserPoolInfo storage user = userPoolInfo[_pid][msg.sender];
(uint256 amount, uint256 penaltyAmount, uint256 pendingRewards) = _userPoolMetrics(_pid);
uint256 availableRewards = IERC20(pool.rewardsToken).allowance(rewardsWallet, address(this));
if(pendingRewards > availableRewards) {
revert InsufficientRewards();
}
if(pendingRewards > 0) {
safeRewardTransfer(_pid, msg.sender, pendingRewards);
}
propcToken.transfer(msg.sender, amount - penaltyAmount);
propcToken.transfer(pool.penaltyWallet, penaltyAmount);
user.totalAmountInPool = user.totalAmountInPool - amount;
pool.totalStaked = pool.totalStaked - amount;
emit Withdraw(msg.sender, _pid, amount);
}
function emergencyWithdrawal(uint256 _pid) external {
PoolInfo storage pool = poolInfo[_pid];
UserPoolInfo storage user = userPoolInfo[_pid][msg.sender];
(uint256 amount, uint256 penaltyAmount, uint256 pendingRewards) = _userPoolMetrics(_pid);
uint256 availableRewards = IERC20(pool.rewardsToken).allowance(rewardsWallet, address(this));
if(pendingRewards > availableRewards) {
pendingRewards = availableRewards;
}
if(pendingRewards > 0) {
safeRewardTransfer(_pid, msg.sender, pendingRewards);
}
propcToken.transfer(msg.sender, amount - penaltyAmount);
propcToken.transfer(pool.penaltyWallet, penaltyAmount);
user.totalAmountInPool = user.totalAmountInPool - amount;
pool.totalStaked = pool.totalStaked - amount;
emit Withdraw(msg.sender, _pid, amount);
}
function _userPoolMetrics(uint256 _pid) internal returns(uint256, uint256, uint256){
PoolInfo storage pool = poolInfo[_pid];
Stake[] storage _stakes = stakes[_pid][msg.sender];
uint256 pendingRewards = 0;
uint256 penaltyAmount = 0;
uint256 amount = 0;
for(uint256 stakeId = _stakes.length; stakeId > 0; stakeId--) {
Stake memory _stake = _stakes[stakeId-1];
uint256 pendingRewardsStake = _pendingRewardsForStake(_pid, _stake);
amount = amount + _stake.amount;
pendingRewards = pendingRewards + pendingRewardsStake;
penaltyAmount = penaltyAmount + _penalty(_stake, pool, _stake.amount);
_stakes.pop();
}
return (amount, penaltyAmount, pendingRewards);
}
function unstake(uint256 _pid, uint256 _amount, uint256 _stakeId) external {
PoolInfo storage pool = poolInfo[_pid];
UserPoolInfo storage user = userPoolInfo[_pid][msg.sender];
Stake[] storage _stakes = stakes[_pid][msg.sender];
if(_stakeId >= _stakes.length)
revert IndexOutOfBounds();
if(_amount == 0)
revert ZeroAmount();
Stake storage _stake = _stakes[_stakeId];
if(_amount > _stake.amount)
revert NotEnoughStaked();
uint256 penaltyAmount = _penalty(_stake, pool, _amount);
uint256 pendingRewards = _pendingRewardsForStake(_pid, _stake);
if(pendingRewards > 0) {
safeRewardTransfer(_pid, msg.sender, pendingRewards);
}
if(_amount == _stake.amount)
deleteStake(_stakes, _stakeId);
else {
_stake.lastClaimTimestamp = block.timestamp;
_stake.totalRedeemed = _stake.totalRedeemed + pendingRewards;
_stake.amount = _stake.amount - _amount;
}
propcToken.transfer(msg.sender, _amount - penaltyAmount);
propcToken.transfer(pool.penaltyWallet, penaltyAmount);
user.totalAmountInPool = user.totalAmountInPool - _amount;
pool.totalStaked = pool.totalStaked - _amount;
emit Withdraw(msg.sender, _pid, _amount);
}
function deleteStake(Stake[] storage _stakes, uint256 index) private {
_stakes[index] = _stakes[_stakes.length-1];
_stakes.pop();
emit DeleteStake(msgSender(), index);
}
function redeem(uint256 _pid) public {
PoolInfo storage pool = poolInfo[_pid];
Stake[] storage _stakes = stakes[_pid][msg.sender];
uint256 pendingRewards = 0;
for(uint256 stakeId = _stakes.length; stakeId > 0; stakeId--) {
Stake storage _stake = _stakes[stakeId-1];
if(_stake.lastClaimTimestamp + pool.claimTimeLimit > block.timestamp)
continue;
uint256 pendingRewardsStake = _pendingRewardsForStake(_pid, _stake);
pendingRewards = pendingRewards + pendingRewardsStake;
_stake.lastClaimTimestamp = block.timestamp;
_stake.totalRedeemed = _stake.totalRedeemed + pendingRewardsStake;
}
if(pendingRewards == 0)
revert NoRewardsAvailable();
safeRewardTransfer(_pid, msg.sender, pendingRewards);
emit Redeem(msg.sender, _pid, pendingRewards);
}
function redeemAll() public {
for(uint _pid = 1; _pid <= poolLength(); _pid++) {
redeem(_pid);
}
}
function safeRewardTransfer(uint256 _pid, address _to, uint256 _amount) internal {
IERC20(poolInfo[_pid].rewardsToken).safeTransferFrom(rewardsWallet, _to, _amount);
}
function getUserInfo(uint256 _pid, address _account) external view returns(uint256 amount) {
UserPoolInfo storage user = userPoolInfo[_pid][_account];
return (
user.totalAmountInPool
);
}
function getStakesInfo(uint256 _pid, address _account) external view returns(Stake[] memory) {
Stake[] memory _stakes = stakes[_pid][_account];
return _stakes;
}
function getPoolInfo(uint256 _pid) external view returns(
uint256 startTime,
address rewardsToken,
address penaltyWallet,
uint256 apyPercent,
uint256 totalStaked,
bool active,
uint256 claimTimeLimit,
uint256 minStakeAmount,
uint256 penaltyFee,
uint256 penaltyTimeLimit,
bool isVIPPool
) {
PoolInfo storage pool = poolInfo[_pid];
startTime = pool.startTime;
penaltyWallet = pool.penaltyWallet;
isVIPPool = pool.isVIPPool;
rewardsToken = address(pool.rewardsToken);
apyPercent = pool.apyInfo[pool.lastAPYIndex].apyPercent;
totalStaked = pool.totalStaked;
active = pool.active;
claimTimeLimit = pool.claimTimeLimit;
minStakeAmount = pool.minStakeAmount;
penaltyFee = pool.penaltyFee;
penaltyTimeLimit = pool.penaltyTimeLimit;
}
}
文件 7 的 10:PlatformAccessController.sol
pragma solidity 0.8.19;
import "../admin_panel/PlatformAdminPanel.sol";
abstract contract PlatformAccessController {
address public _panel;
error CallerNotAdmin();
error AlreadyInitialized();
function _initiatePlatformAccessController(address adminPanel) internal {
if(address(_panel) != address(0))
revert AlreadyInitialized();
_panel = adminPanel;
}
modifier onlyPlatformAdmin() {
if(!PlatformAdminPanel(_panel).isAdmin(msgSender()))
revert CallerNotAdmin();
_;
}
function _isAdmin() internal view returns (bool) {
return PlatformAdminPanel(_panel).isAdmin(msgSender());
}
function msgSender() internal view virtual returns (address) {
return msg.sender;
}
}
文件 8 的 10:PlatformAdminPanel.sol
pragma solidity 0.8.19;
import "./Iplatform_admin_panel/IPlatformAdminPanel.sol";
contract PlatformAdminPanel is IPlatformAdminPanel {
event SetRootAdmin(address indexed wallet);
event InsertAdminList(address[] adminList);
event RemoveAdminList(address[] adminList);
mapping(address => bool) private _adminMap;
address private _rootAdmin;
modifier onlyRootAdmin() {
require(_rootAdmin == msg.sender, "sender is not root admin");
_;
}
constructor(address rootAdminWallet) {
_setRootAdmin(rootAdminWallet);
}
function isAdmin(address wallet)
external
view
virtual
override
returns (bool)
{
return wallet == _rootAdmin || _adminMap[wallet];
}
function rootAdmin() external view returns (address) {
return _rootAdmin;
}
function insertAdminList(address[] calldata adminList)
external
onlyRootAdmin
{
require(0 < adminList.length, "empty admin list");
uint256 index = adminList.length;
while (0 < index) {
--index;
_adminMap[adminList[index]] = true;
}
emit InsertAdminList(adminList);
}
function removeAdminList(address[] calldata adminList)
external
onlyRootAdmin
{
require(0 < adminList.length, "empty admin list");
uint256 index = adminList.length;
while (0 < index) {
--index;
_adminMap[adminList[index]] = false;
}
emit RemoveAdminList(adminList);
}
function setRootAdmin(address rootAdminWallet) external onlyRootAdmin {
_setRootAdmin(rootAdminWallet);
}
function _setRootAdmin(address wallet) private {
require(wallet != address(0), "wallet is zero address");
_rootAdmin = wallet;
emit SetRootAdmin(wallet);
}
}
文件 9 的 10:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.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 safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
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");
}
}
}
文件 10 的 10:draft-IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
{
"compilationTarget": {
"contracts/platform/staking/PROPCStakingV2.sol": "PROPCStakingV2"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 4000
},
"remappings": [],
"viaIR": true
}
[{"inputs":[{"internalType":"contract IPropcToken","name":"_propc","type":"address"},{"internalType":"address","name":"_rewardsWallet","type":"address"},{"internalType":"address","name":"adminPanel","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"APYInvalid","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BelowPoolMinimum","type":"error"},{"inputs":[],"name":"CallerNotAdmin","type":"error"},{"inputs":[],"name":"IndexOutOfBounds","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientRewards","type":"error"},{"inputs":[],"name":"InsufficientStakeLimit","type":"error"},{"inputs":[],"name":"NoRewardsAvailable","type":"error"},{"inputs":[],"name":"NotEnoughStaked","type":"error"},{"inputs":[],"name":"NotVIPPool","type":"error"},{"inputs":[],"name":"NotVIPQualified","type":"error"},{"inputs":[],"name":"PenaltyTooHigh","type":"error"},{"inputs":[],"name":"PoolDoesNotExist","type":"error"},{"inputs":[],"name":"PoolInactive","type":"error"},{"inputs":[],"name":"PoolNotOpened","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"TooManyStakes","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"}],"name":"DeleteStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"NewVIP","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"NoVIP","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_apyPercent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_claimTimeLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_penaltyFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_penaltyTimeLimit","type":"uint256"},{"indexed":false,"internalType":"bool","name":"_active","type":"bool"},{"indexed":false,"internalType":"bool","name":"_isVIPPool","type":"bool"},{"indexed":false,"internalType":"bool","name":"_hasDecreasingPenalty","type":"bool"}],"name":"PoolSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"}],"name":"RewardsWalletUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"YEAR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_panel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_vipAddress","type":"address"}],"name":"addVIPAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address[]","name":"_vipAddresses","type":"address[]"}],"name":"addVIPAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"allPendingRewardsToken","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"emergencyWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"getPoolInfo","outputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"address","name":"rewardsToken","type":"address"},{"internalType":"address","name":"penaltyWallet","type":"address"},{"internalType":"uint256","name":"apyPercent","type":"uint256"},{"internalType":"uint256","name":"totalStaked","type":"uint256"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"claimTimeLimit","type":"uint256"},{"internalType":"uint256","name":"minStakeAmount","type":"uint256"},{"internalType":"uint256","name":"penaltyFee","type":"uint256"},{"internalType":"uint256","name":"penaltyTimeLimit","type":"uint256"},{"internalType":"bool","name":"isVIPPool","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_account","type":"address"}],"name":"getStakesInfo","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakeTimestamp","type":"uint256"},{"internalType":"uint256","name":"totalRedeemed","type":"uint256"},{"internalType":"uint256","name":"lastClaimTimestamp","type":"uint256"}],"internalType":"struct PROPCStakingV2.Stake[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"}],"name":"getTimespanInSeconds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_account","type":"address"}],"name":"getUserInfo","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"leavePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_stakeId","type":"uint256"}],"name":"pendingRewardsForStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"propcToken","outputs":[{"internalType":"contract IPropcToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeemAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_vipAddress","type":"address"}],"name":"removeVIPAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address[]","name":"_vipAddresses","type":"address[]"}],"name":"removeVIPAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"rewardsForPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_idToChange","type":"uint256"},{"internalType":"uint256","name":"_startTime","type":"uint256"},{"internalType":"contract IERC20","name":"_rewardsToken","type":"address"},{"internalType":"uint256","name":"_apyPercent","type":"uint256"},{"internalType":"uint256","name":"_claimTimeLimit","type":"uint256"},{"internalType":"uint256","name":"_penaltyFee","type":"uint256"},{"internalType":"uint256","name":"_penaltyTimeLimit","type":"uint256"},{"internalType":"bool","name":"_active","type":"bool"},{"internalType":"address","name":"_penaltyWallet","type":"address"},{"internalType":"bool","name":"_isVIPPool","type":"bool"},{"internalType":"bool","name":"_hasDecreasingPenalty","type":"bool"},{"internalType":"uint256","name":"_minStakeAmount","type":"uint256"},{"internalType":"uint256","name":"_maxTotalStake","type":"uint256"}],"name":"setPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakes","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakeTimestamp","type":"uint256"},{"internalType":"uint256","name":"totalRedeemed","type":"uint256"},{"internalType":"uint256","name":"lastClaimTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPools","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_stakeId","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"updateRewardsWallet","outputs":[],"stateMutability":"nonpayable","type":"function"}]