编译器
0.8.20+commit.a1b79de6
文件 1 的 12:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 2 的 12:IERC20.sol
pragma solidity ^0.8.20;
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 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 3 的 12:IERC20Permit.sol
pragma solidity ^0.8.20;
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);
}
文件 4 的 12:IStakePool.sol
pragma solidity ^0.8.20;
interface IStakePool {
event Staked(
address indexed token,
address indexed staker_,
uint256 requestedAmount_,
uint256 stakedAmount_
);
event PaidOut(
address indexed token,
address indexed rewardToken,
address indexed staker_,
uint256 amount_,
uint256 reward_
);
function stake(uint256 amount) external returns (bool);
function stakeFor(address staker, uint256 amount) external returns (bool);
function stakeOf(address account) external view returns (uint256);
function tokenAddress() external view returns (address);
function stakedTotal() external view returns (uint256);
function stakedBalance() external view returns (uint256);
function stakingStarts() external view returns (uint256);
function stakingEnds() external view returns (uint256);
}
文件 5 的 12:IStakePoolFactory.sol
pragma solidity ^0.8.20;
interface IStakePoolFactory {
function owner() external view returns (address);
function addTokensTo(address to, uint256 amount) external returns (uint256);
function removeTokensFrom(
address from,
uint256 amount
) external returns (uint256);
}
文件 6 的 12:SafeAmount.sol
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
library SafeAmount {
using SafeERC20 for IERC20;
function safeTransferFrom(
address token,
address from,
address to,
uint256 amount
) internal returns (uint256) {
uint256 preBalance = IERC20(token).balanceOf(to);
IERC20(token).transferFrom(from, to, amount);
uint256 postBalance = IERC20(token).balanceOf(to);
return postBalance - preBalance;
}
}
文件 7 的 12:SafeERC20.sol
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
error SafeERC20FailedOperation(address token);
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
文件 8 的 12:StakeLibrary.sol
pragma solidity ^0.8.20;
import "../common/SafeAmount.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
library StakeLibrary {
using SafeERC20 for IERC20;
event Staked(
address indexed token,
address indexed staker_,
uint256 requestedAmount_,
uint256 stakedAmount_
);
event PaidOut(
address indexed token,
address indexed rewardToken,
address indexed staker_,
uint256 amount_,
uint256 reward_
);
struct StakeState {
uint256 stakedTotal;
uint256 stakedBalance;
uint256 withdrawnEarly;
mapping(address => uint256) _stakes;
}
struct StakeRewardState {
uint256 rewardBalance;
uint256 rewardsTotal;
uint256 earlyWithdrawReward;
mapping(address => uint256) _claimed;
}
function VERSION() external pure returns (uint) {
return 2001;
}
function tryStake(
address payer,
address staker,
uint256 amount,
uint256 stakingStarts,
uint256 stakingEnds,
uint256 stakingCap,
address tokenAddress,
StakeState storage state
)
public
_after(stakingStarts)
_before(stakingEnds)
_positive(amount)
returns (uint256)
{
uint256 remaining = amount;
{
uint256 stakedBalance = state.stakedBalance;
if (stakingCap > 0 && remaining > (stakingCap - stakedBalance)) {
remaining = stakingCap - stakedBalance;
}
}
require(remaining > 0, "StakeLibrary: Staking cap is filled");
remaining = _payMe(payer, remaining, tokenAddress);
emit Staked(tokenAddress, staker, amount, remaining);
return remaining;
}
function stake(
address payer,
address staker,
uint256 amount,
uint256 stakingStarts,
uint256 stakingEnds,
uint256 stakingCap,
address tokenAddress,
StakeState storage state
) external returns (uint256) {
uint256 remaining = tryStake(
payer,
staker,
amount,
stakingStarts,
stakingEnds,
stakingCap,
tokenAddress,
state
);
state.stakedBalance = state.stakedBalance + remaining;
state.stakedTotal = state.stakedTotal + remaining;
state._stakes[staker] = state._stakes[staker] + remaining;
return remaining;
}
function addReward(
uint256 rewardAmount,
uint256 withdrawableAmount,
address rewardTokenAddress,
StakeRewardState storage state
) external returns (bool) {
require(rewardAmount > 0, "StakeLibrary: reward must be positive");
require(
withdrawableAmount >= 0,
"StakeLibrary: withdrawable amount cannot be negative"
);
require(
withdrawableAmount <= rewardAmount,
"StakeLibrary: withdrawable amount must be less than or equal to the reward amount"
);
address from = msg.sender;
rewardAmount = _payMe(from, rewardAmount, rewardTokenAddress);
state.rewardsTotal += rewardAmount;
state.rewardBalance += rewardAmount;
state.earlyWithdrawReward += withdrawableAmount;
return true;
}
function addMarginalReward(
address rewardTokenAddress,
address tokenAddress,
address me,
uint256 stakedBalance,
uint256 earlyWithdrawReward,
StakeRewardState storage state
) external returns (bool) {
uint256 amount = IERC20(rewardTokenAddress).balanceOf(me) -
state.rewardsTotal;
if (rewardTokenAddress == tokenAddress) {
amount = amount - stakedBalance;
}
require(amount >= 0, "StakeLibrary: negative reward");
if (amount == 0) {
return true;
}
require(
amount >= earlyWithdrawReward,
"StakeLibrary: reward must be greater than or equal to the early withdraw reward"
);
state.rewardsTotal += amount;
state.rewardBalance += amount;
state.earlyWithdrawReward += earlyWithdrawReward;
return true;
}
function tryClaimReward(
address from,
address rewardTokenAddress,
uint256 withdrawStarts,
uint256 withdrawEnds,
uint256 stakingEnds,
StakeState storage state,
StakeRewardState storage rewardState
) public _after(withdrawStarts) _realAddress(msg.sender) returns (uint256) {
uint256 staked = state._stakes[from];
require(staked > 0, "StakeLibrary: no staked balance");
return
_claimReward(
rewardTokenAddress,
from,
withdrawEnds,
stakingEnds,
state,
rewardState
);
}
function claimReward(
address from,
address rewardTokenAddress,
uint256 withdrawStarts,
uint256 withdrawEnds,
uint256 stakingEnds,
StakeState storage state,
StakeRewardState storage rewardState
) public returns (bool) {
tryClaimReward(
from,
rewardTokenAddress,
withdrawStarts,
withdrawEnds,
stakingEnds,
state,
rewardState
);
return true;
}
function tryWithdraw(
address from,
address tokenAddress,
address rewardTokenAddress,
uint256 amount,
uint256 withdrawStarts,
uint256 withdrawEnds,
uint256 stakingEnds,
StakeState storage state,
StakeRewardState storage rewardState
)
public
_after(withdrawStarts)
_positive(amount)
_realAddress(msg.sender)
returns (uint256)
{
require(
amount <= state._stakes[from],
"StakeLibrary: not enough balance"
);
return
_withdraw(
tokenAddress,
rewardTokenAddress,
from,
amount,
withdrawEnds,
stakingEnds,
state,
rewardState
);
}
function withdraw(
address from,
address tokenAddress,
address rewardTokenAddress,
uint256 amount,
uint256 withdrawStarts,
uint256 withdrawEnds,
uint256 stakingEnds,
StakeState storage state,
StakeRewardState storage rewardState
) public returns (bool) {
uint256 wdAmount = tryWithdraw(
from,
tokenAddress,
rewardTokenAddress,
amount,
withdrawStarts,
withdrawEnds,
stakingEnds,
state,
rewardState
);
state.stakedBalance = state.stakedBalance - wdAmount;
state._stakes[from] = state._stakes[from] - wdAmount;
return true;
}
function calculateReward(
address from,
uint256 amount,
uint256 withdrawEnds,
uint256 stakingEnds,
StakeState storage state,
StakeRewardState storage rewardState
) public view returns (uint256) {
if (amount == 0) {
return 0;
}
uint256 claimed = rewardState._claimed[from];
uint256 reward = 0;
if (block.timestamp < withdrawEnds) {
reward = _calculateRewardEarly(
amount,
withdrawEnds,
stakingEnds,
state,
rewardState
);
} else {
reward = _calculateRewardAfterClose(amount, state, rewardState);
}
return reward - claimed;
}
function _calculateRewardEarly(
uint256 amount,
uint256 withdrawEnds,
uint256 stakingEnds,
StakeState storage state,
StakeRewardState storage rewardState
) private view returns (uint256) {
if (stakingEnds >= block.timestamp) {
return 0;
}
uint256 denom = (withdrawEnds - stakingEnds) * state.stakedTotal;
uint256 reward = ((block.timestamp - stakingEnds) *
rewardState.earlyWithdrawReward *
amount) / denom;
return reward;
}
function _calculateRewardAfterClose(
uint256 amount,
StakeState storage state,
StakeRewardState storage rewardState
) private view returns (uint256) {
uint256 rewBal = rewardState.rewardBalance;
uint256 reward = (rewBal * amount) / state.stakedBalance;
return reward;
}
function _claimReward(
address rewardTokenAddress,
address from,
uint256 withdrawEnds,
uint256 stakingEnds,
StakeState storage state,
StakeRewardState storage rewardState
) private _realAddress(from) returns (uint256) {
uint256 amount = state._stakes[from];
uint256 reward = calculateReward(
from,
amount,
withdrawEnds,
stakingEnds,
state,
rewardState
);
rewardState.rewardBalance -= reward;
rewardState._claimed[from] += reward;
bool paid = _payDirect(from, reward, rewardTokenAddress);
require(paid, "StakeLibrary: error paying");
emit PaidOut(
rewardTokenAddress,
rewardTokenAddress,
from,
amount,
reward
);
return reward;
}
function _withdraw(
address tokenAddress,
address rewardTokenAddress,
address from,
uint256 amount,
uint256 withdrawEnds,
uint256 stakingEnds,
StakeState storage state,
StakeRewardState storage rewardState
) private _realAddress(from) returns (uint256) {
uint256 reward = calculateReward(
from,
amount,
withdrawEnds,
stakingEnds,
state,
rewardState
);
rewardState.rewardBalance -= reward;
rewardState._claimed[from] += reward;
bool principalPaid = _payDirect(from, amount, tokenAddress);
bool rewardPaid = _payDirect(from, reward, rewardTokenAddress);
require(principalPaid && rewardPaid, "StakeLibrary: error paying");
emit PaidOut(tokenAddress, rewardTokenAddress, from, amount, reward);
return amount;
}
function _payMe(
address payer,
uint256 amount,
address token
) internal returns (uint256) {
return _payTo(payer, address(this), amount, token);
}
function _payTo(
address allower,
address receiver,
uint256 amount,
address token
) internal returns (uint256) {
return SafeAmount.safeTransferFrom(token, allower, receiver, amount);
}
function _payDirect(
address to,
uint256 amount,
address token
) internal returns (bool) {
if (amount == 0) {
return true;
}
IERC20(token).safeTransfer(to, amount);
return true;
}
modifier _realAddress(address addr) {
require(addr != address(0), "StakeLibrary: zero address");
_;
}
modifier _positive(uint256 amount) {
require(amount != 0, "StakeLibrary: negative amount");
_;
}
modifier _after(uint eventTime) {
require(
block.timestamp >= eventTime,
"StakeLibrary: bad after timing for the request"
);
_;
}
modifier _before(uint eventTime) {
require(
block.timestamp < eventTime,
"StakeLibrary: bad before timing for the request"
);
_;
}
}
文件 9 的 12:StakePool.sol
pragma solidity ^0.8.20;
import "./StakePoolRewardable.sol";
import "./StakePoolOptimized.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "./interfaces/IStakePoolFactory.sol";
contract StakePool is StakePoolOptimized, StakePoolRewardable {
address public factory;
constructor(
string memory name_,
address tokenAddress_,
address rewardTokenAddress_,
uint stakingStarts_,
uint stakingEnds_,
uint withdrawStarts_,
uint withdrawEnds_,
uint256 stakingCap_
)
StakePoolOptimized(
name_,
tokenAddress_,
stakingStarts_,
stakingEnds_,
withdrawStarts_,
withdrawEnds_,
stakingCap_
)
{
require(
rewardTokenAddress_ != address(0),
"StakingPool: 0 reward address"
);
rewardTokenAddress = rewardTokenAddress_;
factory = msg.sender;
}
modifier onlyOwner() {
require(
msg.sender == IStakePoolFactory(factory).owner(),
"StakePool: caller is not the owner"
);
_;
}
function rewardOf(address account) external view returns (uint256) {
return
StakeLibrary.calculateReward(
account,
stakeState._stakes[account],
withdrawEnds,
stakingEnds,
stakeState,
rewardState
);
}
function _annualize(
uint256 reward,
uint256 year,
uint256 dt
) internal pure returns (uint256) {
return (reward * year) / dt;
}
function minimumRewardMaturity() external view returns (uint256) {
uint256 dt = withdrawEnds - stakingEnds;
uint256 amount = 1e18;
if (stakingCap > 0) {
amount = stakingCap;
} else {
amount = stakeState.stakedTotal;
}
if (amount == 0) {
return 0;
}
uint256 reward = StakeLibrary.calculateReward(
address(0),
amount,
withdrawEnds,
stakingEnds,
stakeState,
rewardState
);
return _annualize(reward, 365 days, dt);
}
function calculatedReward() external view returns (uint256) {
if (rewardState.rewardsTotal == 0) {
return 0;
}
if (stakingCap > 0) {
return
StakeLibrary.calculateReward(
address(0),
stakingCap,
withdrawEnds,
stakingEnds,
stakeState,
rewardState
);
} else {
return
StakeLibrary.calculateReward(
address(0),
stakeState.stakedTotal,
withdrawEnds,
stakingEnds,
stakeState,
rewardState
);
}
}
function addReward(
uint256 rewardAmount,
uint256 withdrawableAmount
) external onlyOwner returns (bool) {
return
StakeLibrary.addReward(
rewardAmount,
withdrawableAmount,
rewardTokenAddress,
rewardState
);
}
function addMarginalReward(
uint256 withdrawableAmount
) external onlyOwner returns (bool) {
return
StakeLibrary.addMarginalReward(
rewardTokenAddress,
tokenAddress,
address(this),
stakedBalance(),
withdrawableAmount,
rewardState
);
}
function stakeFor(
address staker,
uint256 amount
) external override returns (bool) {
uint256 amountStaked = _stake(msg.sender, staker, amount);
IStakePoolFactory(factory).addTokensTo(staker, amountStaked);
return amountStaked > 0;
}
function stake(uint256 amount) external override returns (bool) {
uint256 amountStaked = _stake(msg.sender, msg.sender, amount);
IStakePoolFactory(factory).addTokensTo(msg.sender, amountStaked);
return amountStaked > 0;
}
function exitPool() external returns (bool) {
IStakePoolFactory(factory).removeTokensFrom(
msg.sender,
stakeState._stakes[msg.sender]
);
return
StakeLibrary.withdraw(
msg.sender,
tokenAddress,
rewardTokenAddress,
stakeState._stakes[msg.sender],
withdrawStarts,
withdrawEnds,
stakingEnds,
stakeState,
rewardState
);
}
function claimReward() public returns (bool) {
return
StakeLibrary.claimReward(
msg.sender,
rewardTokenAddress,
withdrawStarts,
withdrawEnds,
stakingEnds,
stakeState,
rewardState
);
}
function emergencyWithdraw(address _token) public onlyOwner {
if (_token == address(0)) {
payable(msg.sender).transfer(address(this).balance);
} else {
uint256 balance = IERC20(_token).balanceOf(address(this));
IERC20(_token).transfer(msg.sender, balance);
}
}
}
文件 10 的 12:StakePoolFactory.sol
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "./StakePool.sol";
contract StakePoolFactory {
uint256 constant BP_DIVISOR = 10000;
event StakePoolCreated(address indexed stakePool, address indexed owner);
event Transfer(address indexed from, address indexed to, uint256 amount);
address[] public allPools;
mapping(address => bool) public isPool;
address public owner;
string public name = "DuckDAO Farm";
string public symbol = "DDF";
uint8 public decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => uint256) public ratios;
constructor(address owner_) {
owner = owner_;
}
modifier onlyOwner() {
require(
msg.sender == owner,
"StakePoolFactory: caller is not the owner"
);
_;
}
modifier onlyPool() {
require(isPool[msg.sender], "StakePoolFactory: caller is not a pool");
_;
}
function allowance(address, address) external pure returns (uint256) {
return 0;
}
function approve(address, uint256) external pure returns (bool) {
return false;
}
function allPoolsLength() external view returns (uint256) {
return allPools.length;
}
function getPool(uint256 index) external view returns (address) {
return allPools[index];
}
function getPools(
uint256 index,
uint256 pageSize
) external view returns (address[] memory) {
address[] memory pools = new address[](pageSize);
for (uint256 i = 0; i < pageSize; i++) {
if (index + i < allPools.length) {
pools[i] = allPools[index + i];
} else {
break;
}
}
return pools;
}
function createStakePool(
string memory name_,
address tokenAddress,
address rewardTokenAddress,
uint256 stakingStarts,
uint256 stakingEnds,
uint256 withdrawStarts,
uint256 withdrawEnds,
uint256 stakingCap,
uint256 ratio
) external onlyOwner returns (address) {
StakePool stakePool = new StakePool{
salt: keccak256(abi.encode(name, tokenAddress, rewardTokenAddress))
}(
name_,
tokenAddress,
rewardTokenAddress,
stakingStarts,
stakingEnds,
withdrawStarts,
withdrawEnds,
stakingCap
);
require(
isPool[address(stakePool)] == false,
"StakePoolFactory: pool already exists"
);
allPools.push(address(stakePool));
isPool[address(stakePool)] = true;
ratios[address(stakePool)] = ratio;
emit StakePoolCreated(address(stakePool), msg.sender);
return address(stakePool);
}
function addTokensTo(
address to,
uint256 amount
) public onlyPool returns (uint256) {
if (amount == 0 || to == address(0) || ratios[msg.sender] == 0) {
return 0;
}
uint256 amountToTransfer = (amount * ratios[msg.sender]) / BP_DIVISOR;
balanceOf[to] += amountToTransfer;
totalSupply += amountToTransfer;
emit Transfer(address(0), to, amountToTransfer);
return amountToTransfer;
}
function removeTokensFrom(
address from,
uint256 amount
) public onlyPool returns (uint256) {
if (amount == 0 || from == address(0) || ratios[msg.sender] == 0) {
return 0;
}
uint256 amountToRemove = (amount * ratios[msg.sender]) / BP_DIVISOR;
balanceOf[from] -= amountToRemove;
totalSupply -= amountToRemove;
emit Transfer(from, address(0), amountToRemove);
return amountToRemove;
}
function emergencyWithdraw(address _token) public onlyOwner {
if (_token == address(0)) {
payable(msg.sender).transfer(address(this).balance);
} else {
uint256 balance = IERC20(_token).balanceOf(address(this));
IERC20(_token).transfer(msg.sender, balance);
}
}
}
文件 11 的 12:StakePoolOptimized.sol
pragma solidity ^0.8.20;
import "./interfaces/IStakePool.sol";
import "./libraries/StakeLibrary.sol";
abstract contract StakePoolOptimized is IStakePool {
string private _name;
address public override tokenAddress;
uint public override stakingStarts;
uint public override stakingEnds;
uint public withdrawStarts;
uint public withdrawEnds;
uint public stakingCap;
StakeLibrary.StakeState public stakeState;
uint constant LIB_VERSION = 2001;
constructor(
string memory name_,
address tokenAddress_,
uint stakingStarts_,
uint stakingEnds_,
uint withdrawStarts_,
uint withdrawEnds_,
uint256 stakingCap_
) {
require(
StakeLibrary.VERSION() == LIB_VERSION,
"Bad linked library version"
);
_name = name_;
require(tokenAddress_ != address(0), "StakingPool: 0 address");
tokenAddress = tokenAddress_;
if (stakingStarts_ < block.timestamp) {
stakingStarts = block.timestamp;
} else {
stakingStarts = stakingStarts_;
}
require(
stakingEnds_ >= stakingStarts,
"StakingPool: staking end must be after staking starts"
);
stakingEnds = stakingEnds_;
require(
withdrawStarts_ >= stakingEnds,
"StakingPool: withdrawStarts must be after staking ends"
);
withdrawStarts = withdrawStarts_;
require(
withdrawEnds_ >= withdrawStarts,
"StakingPool: withdrawEnds must be after withdraw starts"
);
withdrawEnds = withdrawEnds_;
require(stakingCap_ >= 0, "StakingPool: stakingCap cannot be negative");
stakingCap = stakingCap_;
}
function name() external view returns (string memory) {
return _name;
}
function stakedTotal() external view override returns (uint256) {
return stakeState.stakedTotal;
}
function stakedBalance() public view override returns (uint256) {
return stakeState.stakedBalance;
}
function stakeOf(address account) external view override returns (uint256) {
return stakeState._stakes[account];
}
function _stake(
address payer,
address staker,
uint256 amount
) internal virtual returns (uint256) {
return
StakeLibrary.stake(
payer,
staker,
amount,
stakingStarts,
stakingEnds,
stakingCap,
tokenAddress,
stakeState
);
}
}
文件 12 的 12:StakePoolRewardable.sol
pragma solidity ^0.8.20;
import "./interfaces/IStakePool.sol";
import "./libraries/StakeLibrary.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract StakePoolRewardable is IStakePool {
using SafeERC20 for IERC20;
address public rewardTokenAddress;
StakeLibrary.StakeRewardState public rewardState;
function earlyWithdrawReward() external view returns (uint256) {
return rewardState.earlyWithdrawReward;
}
function rewardBalance() external view returns (uint256) {
return rewardState.rewardBalance;
}
function rewardsTotal() external view returns (uint256) {
return rewardState.rewardsTotal;
}
}
{
"compilationTarget": {
"contracts/StakePoolFactory.sol": "StakePoolFactory"
},
"evmVersion": "paris",
"libraries": {
"contracts/libraries/StakeLibrary.sol:StakeLibrary": "0x611fbdead231cce1481565d6dd9ec9c8f4c052f9"
},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stakePool","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"StakePoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addTokensTo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allPools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allPoolsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"rewardTokenAddress","type":"address"},{"internalType":"uint256","name":"stakingStarts","type":"uint256"},{"internalType":"uint256","name":"stakingEnds","type":"uint256"},{"internalType":"uint256","name":"withdrawStarts","type":"uint256"},{"internalType":"uint256","name":"withdrawEnds","type":"uint256"},{"internalType":"uint256","name":"stakingCap","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"name":"createStakePool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"pageSize","type":"uint256"}],"name":"getPools","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ratios","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"removeTokensFrom","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]