编译器
0.8.23+commit.f704f362
文件 1 的 11: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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 11: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 的 11:EnumerableSet.sol
pragma solidity ^0.8.0;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping(bytes32 => uint256) _indexes;
}
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
function _remove(Set storage set, bytes32 value) private returns (bool) {
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
set._values[toDeleteIndex] = lastValue;
set._indexes[lastValue] = valueIndex;
}
set._values.pop();
delete set._indexes[value];
return true;
} else {
return false;
}
}
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
struct Bytes32Set {
Set _inner;
}
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly {
result := store
}
return result;
}
struct AddressSet {
Set _inner;
}
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
struct UintSet {
Set _inner;
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
}
文件 4 的 11: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);
}
文件 5 的 11:IERC721Receiver.sol
pragma solidity ^0.8.0;
import "../token/ERC721/IERC721Receiver.sol";
文件 6 的 11:IVesting.sol
pragma solidity 0.8.23;
interface IVesting {
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function hasRole(bytes32 role, address account) external view returns (bool);
function rfrm() external view returns (address);
function isStakingContract(address _address) external view returns (bool);
function lockPeriod() external view returns (uint256);
function supportsInterface(bytes4 interfaceId) external view returns (bool);
function unlockDisabledUntil() external view returns (uint256);
function vesting(address, uint256) external view returns (uint256 time, uint256 amount, bool claimed);
function removeStakingContract(address _address) external;
function grantRole(bytes32 role, address account) external;
function removeStuckToken(address _address) external;
function renounceRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function transfer(address to, uint256 amount) external returns (bool);
function transferAdmin(address _newAdmin) external;
function claimUserVesting(uint256 _id) external;
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function addStakingContract(address _address) external;
function addVesting(address _wallet, uint256 _amount) external;
function mint(address _wallet, uint256 _amount) external;
function burn(address _wallet, uint256 _amount) external;
}
文件 7 的 11: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);
}
}
文件 8 的 11:RFRMStaking.sol
pragma solidity 0.8.23;
import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import { IERC721Receiver } from "@openzeppelin/contracts/interfaces/IERC721Receiver.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { IVesting } from "./interface/IVesting.sol";
interface IERC721 {
function safeTransferFrom(address from, address to, uint256 tokenId) external;
}
contract RFRMStaking is Ownable, ReentrancyGuard, IERC721Receiver {
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.UintSet;
struct UserInfo {
uint256 totalDeposit;
uint256 rewardDebt;
uint256 totalClaimed;
uint256 depositTime;
EnumerableSet.UintSet deposits;
}
struct PoolInfo {
bool isInputNFT;
bool isVested;
uint32 totalInvestors;
address input;
uint256 allocPoint;
uint256 lastRewardBlock;
uint256 accTknPerShare;
uint256 startIdx;
uint256 endIdx;
uint256 totalDeposit;
EnumerableSet.UintSet deposits;
}
struct PoolLockInfo {
uint32 multi;
uint32 claimFee;
uint32 lockPeriodInSeconds;
bool forcedUnlockEnabled;
}
struct UserLockInfo {
bool isWithdrawed;
uint32 depositTime;
uint256 actualDeposit;
}
IERC20 public immutable reward;
uint32 public percPerDay = 1;
address public rewardWallet;
address public feeWallet;
IVesting public vestingCont;
uint16 internal constant BLOCKS_PER_DAY = 7150;
uint16 internal constant DIVISOR = 10000;
PoolInfo[] internal pools;
mapping(uint256 => PoolLockInfo) public poolLockInfo;
mapping(uint256 => mapping(address => UserInfo)) internal users;
mapping(uint8 => mapping(address => UserLockInfo[])) public userLockInfo;
uint256 public totalAllocPoint = 0;
uint256 public totalActualDeposit;
uint256 public startBlock;
event Deposit(address indexed user, uint256 indexed pid, uint8 indexed lid, uint256[] amounts);
event Withdraw(address indexed user, uint256 indexed pid, uint8 indexed lid, uint256[] amounts);
event RewardClaimed(address indexed user, uint256 indexed pid, uint256 amount);
event PoolAdded(
bool _isInputNFT,
bool _isVested,
uint256 _allocPoint,
address _input,
uint256 _startIdx,
uint256 _endIdx
);
event PoolChanged(uint256 pid, uint256 allocPoint, bool isVested, uint256 startIdx, uint256 endIdx);
event PoolLockChanged(uint256 lid, uint32 multi, uint32 claimFee, uint32 lockPeriod);
event PoolUpdated(uint256 pid);
event WalletsChanged(address reward, address feeWallet);
event RewardChanged(uint32 perc);
event VestingContractChanged(address vesting);
error ZeroAddress();
error InvalidNFTId();
error InvalidAmount();
error InvalidLockId();
error AlreadyWithdrawed();
error ForcedUnlockDisabled();
error InvalidInput();
error DepositNotFound();
constructor(address _reward, address _rewardWallet, address _feeWallet, uint256 _startBlock) {
if (_reward == address(0) || _rewardWallet == address(0) || _feeWallet == address(0)) revert ZeroAddress();
reward = IERC20(_reward);
rewardWallet = _rewardWallet;
feeWallet = _feeWallet;
startBlock = _startBlock;
}
function poolLength() external view returns (uint256) {
return pools.length;
}
function add(
bool _isInputNFT,
bool _isVested,
uint256 _allocPoint,
address _input,
uint256 _startIdx,
uint256 _endIdx
) external onlyOwner {
if (_input == address(0)) revert ZeroAddress();
massUpdatePools();
uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
totalAllocPoint = totalAllocPoint + _allocPoint;
PoolInfo storage newPool = pools.push();
newPool.allocPoint = _allocPoint;
newPool.input = _input;
newPool.isInputNFT = _isInputNFT;
newPool.isVested = _isVested;
newPool.lastRewardBlock = lastRewardBlock;
if (_isInputNFT) {
newPool.startIdx = _startIdx;
newPool.endIdx = _endIdx;
}
emit PoolAdded(_isInputNFT, _isVested, _allocPoint, _input, _startIdx, _endIdx);
}
function set(
uint256 _pid,
uint256 _allocPoint,
bool _isVested,
uint256 _startIdx,
uint256 _endIdx
) external onlyOwner {
massUpdatePools();
PoolInfo storage pool = pools[_pid];
totalAllocPoint = totalAllocPoint - pool.allocPoint + _allocPoint;
pool.allocPoint = _allocPoint;
pool.isVested = _isVested;
if (pool.isInputNFT) {
pool.startIdx = _startIdx;
pool.endIdx = _endIdx;
}
emit PoolChanged(_pid, _allocPoint, _isVested, _startIdx, _endIdx);
}
function setPoolLock(uint256 _lid, uint32 _multi, uint32 _claimFee, uint32 _lockPeriod) external onlyOwner {
PoolLockInfo storage pool = poolLockInfo[_lid];
pool.claimFee = _claimFee;
pool.lockPeriodInSeconds = _lockPeriod;
pool.multi = _multi;
emit PoolLockChanged(_lid, _multi, _claimFee, _lockPeriod);
}
function pendingTkn(uint256 _pid, address _user) external view returns (uint256) {
PoolInfo storage pool = pools[_pid];
UserInfo storage user = users[_pid][_user];
uint256 accTknPerShare = pool.accTknPerShare;
uint256 total = pool.totalDeposit;
if (block.number > pool.lastRewardBlock && total != 0) {
uint256 multi = block.number - pool.lastRewardBlock;
uint256 rewardPerBlock = getRewardPerBlock();
uint256 tknReward = (multi * rewardPerBlock * pool.allocPoint) / totalAllocPoint;
accTknPerShare = accTknPerShare + ((tknReward * 1e12) / total);
}
return (user.totalDeposit * accTknPerShare) / 1e12 - user.rewardDebt;
}
function deposit(
uint256 _pid,
uint8 _lid,
address _benificiary,
uint256[] calldata _amounts
) external nonReentrant {
PoolInfo storage pool = pools[_pid];
UserInfo storage user = users[_pid][_benificiary];
updatePool(_pid);
if (user.totalDeposit > 0) {
_claimReward(_pid, _benificiary);
} else {
pool.totalInvestors++;
}
if (pool.isInputNFT) {
IERC721 nft = IERC721(pool.input);
uint256 len = _amounts.length;
uint256 id;
for (uint256 i = 0; i < len; ) {
id = _amounts[i];
if (id < pool.startIdx || id > pool.endIdx) revert InvalidNFTId();
nft.safeTransferFrom(msg.sender, address(this), id);
pool.deposits.add(id);
user.deposits.add(id);
unchecked {
i++;
}
}
user.totalDeposit = user.totalDeposit + len;
pool.totalDeposit = pool.totalDeposit + len;
} else {
if (_amounts.length != 1) revert InvalidAmount();
uint256 amount = _amounts[0];
IERC20(pool.input).safeTransferFrom(msg.sender, address(this), amount);
if (_pid == 0) {
PoolLockInfo storage poolLock = poolLockInfo[_lid];
UserLockInfo storage userLock = userLockInfo[_lid][_benificiary].push();
if (poolLock.multi <= 0) revert InvalidLockId();
userLock.depositTime = uint32(block.timestamp);
userLock.actualDeposit = amount;
totalActualDeposit += amount;
uint256 weightedAmount = (amount * poolLock.multi) / DIVISOR;
user.totalDeposit += weightedAmount;
pool.totalDeposit += weightedAmount;
vestingCont.mint(_benificiary, amount);
} else {
user.totalDeposit = user.totalDeposit + amount;
pool.totalDeposit = pool.totalDeposit + amount;
}
}
user.rewardDebt = (user.totalDeposit * pool.accTknPerShare) / 1e12;
user.depositTime = block.timestamp;
emit Deposit(_benificiary, _pid, _lid, _amounts);
}
function withdraw(uint256 _pid, uint8 _lid, uint256 _did, uint256[] calldata _amounts) external nonReentrant {
PoolInfo storage pool = pools[_pid];
UserInfo storage user = users[_pid][msg.sender];
updatePool(_pid);
_claimReward(_pid, msg.sender);
if (pool.isInputNFT) {
IERC721 nft = IERC721(pool.input);
uint256 len = _amounts.length;
for (uint256 i = 0; i < len; ) {
uint256 id = _amounts[i];
if (!user.deposits.contains(id)) revert InvalidNFTId();
nft.safeTransferFrom(address(this), msg.sender, id);
user.deposits.remove(id);
pool.deposits.remove(id);
unchecked {
i++;
}
}
user.totalDeposit = user.totalDeposit - _amounts.length;
pool.totalDeposit = pool.totalDeposit - _amounts.length;
} else {
IERC20 token = IERC20(pool.input);
uint256 amount = _amounts[0];
if (_pid == 0) {
PoolLockInfo storage poolLock = poolLockInfo[_lid];
UserLockInfo storage userLock = userLockInfo[_lid][msg.sender][_did];
amount = userLock.actualDeposit;
if (userLock.isWithdrawed) revert AlreadyWithdrawed();
uint256 weightedAmount = (amount * poolLock.multi) / DIVISOR;
user.totalDeposit -= weightedAmount;
pool.totalDeposit -= weightedAmount;
userLock.isWithdrawed = true;
totalActualDeposit -= amount;
vestingCont.burn(msg.sender, amount);
if (canWithdraw(_lid, _did, msg.sender)) {
token.safeTransfer(msg.sender, amount);
} else {
if (!poolLock.forcedUnlockEnabled) revert ForcedUnlockDisabled();
uint256 feeAmount = (amount * poolLock.claimFee) / DIVISOR;
token.safeTransfer(feeWallet, feeAmount);
amount = amount - feeAmount;
token.safeTransfer(msg.sender, amount);
}
} else {
if (user.totalDeposit < amount) revert InvalidAmount();
user.totalDeposit = user.totalDeposit - amount;
pool.totalDeposit = pool.totalDeposit - amount;
token.safeTransfer(msg.sender, amount);
}
}
user.rewardDebt = (user.totalDeposit * pool.accTknPerShare) / 1e12;
emit Withdraw(msg.sender, _pid, _lid, _amounts);
}
function claimReward(uint256 _pid) external {
_claimReward(_pid, msg.sender);
}
function setWallets(address _reward, address _feeWallet) external onlyOwner {
if (_reward == address(0) || _feeWallet == address(0)) revert ZeroAddress();
rewardWallet = _reward;
feeWallet = _feeWallet;
emit WalletsChanged(_reward, _feeWallet);
}
function setPercentagePerDay(uint32 _perc) external onlyOwner {
percPerDay = _perc;
emit RewardChanged(_perc);
}
function setVesting(address _vesting) external onlyOwner {
if (_vesting == address(0)) revert ZeroAddress();
vestingCont = IVesting(_vesting);
emit VestingContractChanged(_vesting);
}
function setForcedUnlockState(uint256[] calldata _lid, bool[] calldata _state) external onlyOwner {
if (_lid.length != _state.length) revert InvalidInput();
uint256 length = _lid.length;
for (uint256 i = 0; i < length; i++) {
poolLockInfo[_lid[i]].forcedUnlockEnabled = _state[i];
}
}
function setBulkAllocPoints(uint256[] calldata _pids, uint256[] calldata _allocPoints) external onlyOwner {
if (_pids.length != _allocPoints.length || _pids.length != pools.length) revert InvalidInput();
uint256 length = _pids.length;
uint256 total = 0;
massUpdatePools();
for (uint256 i = 0; i < length; i++) {
total += _allocPoints[i];
pools[_pids[i]].allocPoint = _allocPoints[i];
}
totalAllocPoint = total;
}
function getDepositedIdsOfUser(uint256 _pid, address _user) external view returns (uint256[] memory) {
return users[_pid][_user].deposits.values();
}
function getLockTermsOfUser(
address _user,
uint8 _lid
) external view returns (uint256 count, UserLockInfo[] memory) {
return (userLockInfo[_lid][_user].length, userLockInfo[_lid][_user]);
}
function poolInfo(
uint256 _pid
)
external
view
returns (
bool isInputNFT,
bool isVested,
uint32 totalInvestors,
address input,
uint256 allocPoint,
uint256 lastRewardBlock,
uint256 accTknPerShare,
uint256 startIdx,
uint256 endIdx,
uint256 totalDeposit
)
{
PoolInfo storage pool = pools[_pid];
isInputNFT = pool.isInputNFT;
isVested = pool.isVested;
allocPoint = pool.allocPoint;
lastRewardBlock = pool.lastRewardBlock;
accTknPerShare = pool.accTknPerShare;
totalDeposit = pool.totalDeposit;
startIdx = pool.startIdx;
endIdx = pool.endIdx;
input = pool.input;
totalInvestors = pool.totalInvestors;
}
function userInfo(
uint256 _pid,
address _user
) external view returns (uint256 totalDeposit, uint256 rewardDebt, uint256 totalClaimed, uint256 depositTime) {
UserInfo storage user = users[_pid][_user];
totalDeposit = user.totalDeposit;
rewardDebt = user.rewardDebt;
totalClaimed = user.totalClaimed;
depositTime = user.depositTime;
}
function massUpdatePools() public {
uint256 length = pools.length;
for (uint256 pid = 0; pid < length; ++pid) {
updatePool(pid);
}
}
function updatePool(uint256 _pid) public {
PoolInfo storage pool = pools[_pid];
if (block.number <= pool.lastRewardBlock) {
return;
}
uint256 total = pool.totalDeposit;
if (total == 0 || pool.allocPoint == 0) {
pool.lastRewardBlock = block.number;
return;
}
uint256 multi = block.number - pool.lastRewardBlock;
uint256 rewardPerBlock = getRewardPerBlock();
uint256 tknReward = (multi * rewardPerBlock * pool.allocPoint) / totalAllocPoint;
reward.safeTransferFrom(rewardWallet, address(this), tknReward);
pool.accTknPerShare = pool.accTknPerShare + ((tknReward * 1e12) / total);
pool.lastRewardBlock = block.number;
emit PoolUpdated(_pid);
}
function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
return this.onERC721Received.selector;
}
function getRewardPerBlock() public view returns (uint256 rpb) {
uint256 total = reward.balanceOf(rewardWallet);
uint256 rewardPerDay = (total * percPerDay) / DIVISOR;
rewardPerDay = rewardPerDay / 10;
rpb = rewardPerDay / BLOCKS_PER_DAY;
}
function canWithdraw(uint8 _lid, uint256 _did, address _user) public view returns (bool) {
return (block.timestamp >=
userLockInfo[_lid][_user][_did].depositTime + poolLockInfo[_lid].lockPeriodInSeconds);
}
function _claimReward(uint256 _pid, address _user) internal {
updatePool(_pid);
UserInfo storage user = users[_pid][_user];
if (user.totalDeposit == 0) {
return;
}
uint256 pending = (user.totalDeposit * pools[_pid].accTknPerShare) / 1e12 - user.rewardDebt;
if (pending > 0) {
user.totalClaimed = user.totalClaimed + pending;
user.rewardDebt = (user.totalDeposit * pools[_pid].accTknPerShare) / 1e12;
if (pools[_pid].isVested) {
vestingCont.addVesting(_user, pending);
reward.safeTransfer(address(vestingCont), pending);
} else {
reward.safeTransfer(_user, pending);
}
}
emit RewardClaimed(_user, _pid, pending);
}
}
文件 9 的 11:ReentrancyGuard.sol
pragma solidity ^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() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
}
文件 10 的 11: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");
}
}
}
文件 11 的 11: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": {
"src/RFRMStaking.sol": "RFRMStaking"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 10000
},
"remappings": [
":@chainlink/=lib/chainlink/",
":@openzeppelin/=lib/openzeppelin-contracts/",
":@prb/test/=lib/prb-test/src/",
":@uniswap/=node_modules/@uniswap/",
":chainlink/=lib/chainlink/integration-tests/contracts/ethereum/src/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":prb-test/=lib/prb-test/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_reward","type":"address"},{"internalType":"address","name":"_rewardWallet","type":"address"},{"internalType":"address","name":"_feeWallet","type":"address"},{"internalType":"uint256","name":"_startBlock","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyWithdrawed","type":"error"},{"inputs":[],"name":"DepositNotFound","type":"error"},{"inputs":[],"name":"ForcedUnlockDisabled","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidInput","type":"error"},{"inputs":[],"name":"InvalidLockId","type":"error"},{"inputs":[],"name":"InvalidNFTId","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"uint8","name":"lid","type":"uint8"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_isInputNFT","type":"bool"},{"indexed":false,"internalType":"bool","name":"_isVested","type":"bool"},{"indexed":false,"internalType":"uint256","name":"_allocPoint","type":"uint256"},{"indexed":false,"internalType":"address","name":"_input","type":"address"},{"indexed":false,"internalType":"uint256","name":"_startIdx","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_endIdx","type":"uint256"}],"name":"PoolAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"allocPoint","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isVested","type":"bool"},{"indexed":false,"internalType":"uint256","name":"startIdx","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endIdx","type":"uint256"}],"name":"PoolChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"lid","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"multi","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"claimFee","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"lockPeriod","type":"uint32"}],"name":"PoolLockChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pid","type":"uint256"}],"name":"PoolUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"perc","type":"uint32"}],"name":"RewardChanged","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":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vesting","type":"address"}],"name":"VestingContractChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"address","name":"feeWallet","type":"address"}],"name":"WalletsChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":true,"internalType":"uint8","name":"lid","type":"uint8"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"bool","name":"_isInputNFT","type":"bool"},{"internalType":"bool","name":"_isVested","type":"bool"},{"internalType":"uint256","name":"_allocPoint","type":"uint256"},{"internalType":"address","name":"_input","type":"address"},{"internalType":"uint256","name":"_startIdx","type":"uint256"},{"internalType":"uint256","name":"_endIdx","type":"uint256"}],"name":"add","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_lid","type":"uint8"},{"internalType":"uint256","name":"_did","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"canWithdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"claimReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"uint8","name":"_lid","type":"uint8"},{"internalType":"address","name":"_benificiary","type":"address"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"getDepositedIdsOfUser","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint8","name":"_lid","type":"uint8"}],"name":"getLockTermsOfUser","outputs":[{"internalType":"uint256","name":"count","type":"uint256"},{"components":[{"internalType":"bool","name":"isWithdrawed","type":"bool"},{"internalType":"uint32","name":"depositTime","type":"uint32"},{"internalType":"uint256","name":"actualDeposit","type":"uint256"}],"internalType":"struct RFRMStaking.UserLockInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardPerBlock","outputs":[{"internalType":"uint256","name":"rpb","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"massUpdatePools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"pendingTkn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"percPerDay","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"poolInfo","outputs":[{"internalType":"bool","name":"isInputNFT","type":"bool"},{"internalType":"bool","name":"isVested","type":"bool"},{"internalType":"uint32","name":"totalInvestors","type":"uint32"},{"internalType":"address","name":"input","type":"address"},{"internalType":"uint256","name":"allocPoint","type":"uint256"},{"internalType":"uint256","name":"lastRewardBlock","type":"uint256"},{"internalType":"uint256","name":"accTknPerShare","type":"uint256"},{"internalType":"uint256","name":"startIdx","type":"uint256"},{"internalType":"uint256","name":"endIdx","type":"uint256"},{"internalType":"uint256","name":"totalDeposit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolLockInfo","outputs":[{"internalType":"uint32","name":"multi","type":"uint32"},{"internalType":"uint32","name":"claimFee","type":"uint32"},{"internalType":"uint32","name":"lockPeriodInSeconds","type":"uint32"},{"internalType":"bool","name":"forcedUnlockEnabled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reward","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"uint256","name":"_allocPoint","type":"uint256"},{"internalType":"bool","name":"_isVested","type":"bool"},{"internalType":"uint256","name":"_startIdx","type":"uint256"},{"internalType":"uint256","name":"_endIdx","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_pids","type":"uint256[]"},{"internalType":"uint256[]","name":"_allocPoints","type":"uint256[]"}],"name":"setBulkAllocPoints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_lid","type":"uint256[]"},{"internalType":"bool[]","name":"_state","type":"bool[]"}],"name":"setForcedUnlockState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_perc","type":"uint32"}],"name":"setPercentagePerDay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lid","type":"uint256"},{"internalType":"uint32","name":"_multi","type":"uint32"},{"internalType":"uint32","name":"_claimFee","type":"uint32"},{"internalType":"uint32","name":"_lockPeriod","type":"uint32"}],"name":"setPoolLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vesting","type":"address"}],"name":"setVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_reward","type":"address"},{"internalType":"address","name":"_feeWallet","type":"address"}],"name":"setWallets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalActualDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAllocPoint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"}],"name":"updatePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"totalDeposit","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"},{"internalType":"uint256","name":"totalClaimed","type":"uint256"},{"internalType":"uint256","name":"depositTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userLockInfo","outputs":[{"internalType":"bool","name":"isWithdrawed","type":"bool"},{"internalType":"uint32","name":"depositTime","type":"uint32"},{"internalType":"uint256","name":"actualDeposit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingCont","outputs":[{"internalType":"contract IVesting","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pid","type":"uint256"},{"internalType":"uint8","name":"_lid","type":"uint8"},{"internalType":"uint256","name":"_did","type":"uint256"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]