编译器
0.8.19+commit.7dd6d404
文件 1 的 6: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 的 6: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);
}
文件 3 的 6: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);
}
文件 4 的 6:Ownable.sol
pragma solidity 0.8.19;
abstract contract Ownable {
address public owner;
address public pendingOwner;
event OwnershipTransferred(address indexed user, address indexed newOner);
event OwnershipTransferStarted(address indexed user, address indexed newOwner);
event OwnershipTransferCanceled(address indexed pendingOwner);
error Unauthorized();
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
function transferOwnership(address newOwner) external onlyOwner {
pendingOwner = newOwner;
emit OwnershipTransferStarted(msg.sender, pendingOwner);
}
function acceptOwnership() external {
if (msg.sender != pendingOwner) revert Unauthorized();
address oldOwner = owner;
owner = pendingOwner;
delete pendingOwner;
emit OwnershipTransferred(oldOwner, owner);
}
function cancelTransferOwnership() external onlyOwner {
emit OwnershipTransferCanceled(pendingOwner);
delete pendingOwner;
}
modifier onlyOwner() {
if (msg.sender != owner) revert Unauthorized();
_;
}
}
文件 5 的 6:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/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 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
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");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
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.isContract(address(token));
}
}
文件 6 的 6:StakingETH.sol
pragma solidity 0.8.19;
import {Ownable} from "src/utils/Ownable.sol";
import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
contract StakingETH is Ownable {
using SafeERC20 for IERC20;
event Staked(address indexed user, uint256 amount);
error ZeroAmount();
error NotAllowed();
uint256 constant PRECISION = 1e30;
uint256 public constant REWARD_RATE = 10e18;
uint256 public constant FEE_RATE = 1e18;
uint256 public feeToCollect;
uint256 public ethToCollect;
IERC20 public immutable WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
address public multisig;
uint64 public start_date;
uint64 public period_finish;
uint64 public lastUpdateTime;
uint256 public rewardPerTokenStored;
uint256 public rewardPerTokenStoredFyde;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => uint256) public userRewardPerTokenPaid;
mapping(address => uint256) public userRewardPerTokenPaidFyde;
mapping(address => uint256) public rewards;
mapping(address => uint256) public rewardsFyde;
struct BoostPeriod {
uint32 timeStamp;
uint16 multiplier;
}
BoostPeriod[] public boostPeriods;
function collectFee(address _recipient) external onlyOwner {
uint256 feeToTransfer = feeToCollect;
feeToCollect = 0;
WETH.safeTransfer(_recipient, feeToTransfer);
}
function collectEth() external {
require(msg.sender == multisig, "Not multisig");
uint256 ethToTransfer = ethToCollect;
ethToCollect = 0;
WETH.safeTransfer(multisig, ethToTransfer);
}
function setBoostPeriods(BoostPeriod[] calldata _boostPeriods) external onlyOwner {
delete boostPeriods;
for (uint256 i; i < _boostPeriods.length; ++i) {
boostPeriods.push(_boostPeriods[i]);
}
}
constructor(address _owner, address _multisig) Ownable(_owner) {
lastUpdateTime = uint64(block.timestamp);
multisig = _multisig;
}
function setStartAndEndTime(uint64 _start_date, uint64 _period_finish) external onlyOwner {
start_date = _start_date;
period_finish = _period_finish;
}
function stake(uint256 amount) external {
if (block.timestamp < start_date || block.timestamp > period_finish) revert NotAllowed();
if (amount == 0) revert ZeroAmount();
uint256 fee = amount * FEE_RATE / 100e18;
uint256 amountAfterFee = amount - fee;
uint256 rewardPerETH = rewardPerToken();
uint256 rewardPerETHFyde = rewardPerTokenFyde();
rewardPerTokenStored = rewardPerETH;
rewards[msg.sender] =
earned(msg.sender, balanceOf[msg.sender], rewardPerETH, rewards[msg.sender]);
userRewardPerTokenPaid[msg.sender] = rewardPerETH;
rewardPerTokenStoredFyde = rewardPerETHFyde;
rewardsFyde[msg.sender] =
earnedFyde(msg.sender, balanceOf[msg.sender], rewardPerETHFyde, rewardsFyde[msg.sender]);
userRewardPerTokenPaidFyde[msg.sender] = rewardPerETHFyde;
lastUpdateTime = lastTimeRewardApplicable();
totalSupply += amountAfterFee;
balanceOf[msg.sender] += amountAfterFee;
feeToCollect += fee;
ethToCollect += amountAfterFee;
WETH.safeTransferFrom(msg.sender, address(this), amount);
emit Staked(msg.sender, amount);
}
function lastTimeRewardApplicable() public view returns (uint64) {
return block.timestamp < period_finish ? uint64(block.timestamp) : period_finish;
}
function rewardPerToken() public view returns (uint256) {
return totalSupply == 0
? rewardPerTokenStored
: rewardPerTokenStored
+ (PRECISION * (lastTimeRewardApplicable() - lastUpdateTime) * REWARD_RATE / 1e18);
}
function earned(address _user, uint256 _userBalance, uint256 _rewardPerToken, uint256 _userReward)
public
view
returns (uint256)
{
return
_userReward + _userBalance * (_rewardPerToken - userRewardPerTokenPaid[_user]) / PRECISION;
}
function getReward(address user) external view returns (uint256) {
return earned(user, balanceOf[user], rewardPerToken(), rewards[user]);
}
function rewardPerTokenFyde() public view returns (uint256) {
return totalSupply == 0
? rewardPerTokenStoredFyde
: rewardPerTokenStoredFyde
+ (PRECISION * (lastTimeRewardApplicable() - lastUpdateTime) * rewardRateFyde() / totalSupply);
}
function earnedFyde(
address _user,
uint256 _userBalance,
uint256 _rewardPerToken,
uint256 _userReward
) public view returns (uint256) {
return
_userReward + _userBalance * (_rewardPerToken - userRewardPerTokenPaidFyde[_user]) / PRECISION;
}
function getRewardFyde(address user) external view returns (uint256) {
return earnedFyde(user, balanceOf[user], rewardPerTokenFyde(), rewardsFyde[user]);
}
function rewardRateFyde() public view returns (uint256) {
uint256 rateMultiplier;
uint256 totalTime;
uint256 timeInPeriod;
uint256 currTime = block.timestamp;
for (uint256 i; i < boostPeriods.length; ++i) {
currTime = currTime - timeInPeriod;
uint256 nextTimeStamp =
currTime > boostPeriods[i].timeStamp ? currTime : boostPeriods[i].timeStamp;
uint256 lastUpdate =
boostPeriods[i].timeStamp > lastUpdateTime ? boostPeriods[i].timeStamp : lastUpdateTime;
timeInPeriod = nextTimeStamp - lastUpdate;
totalTime += timeInPeriod;
rateMultiplier += timeInPeriod * boostPeriods[i].multiplier;
if (lastUpdateTime > boostPeriods[i].timeStamp) break;
}
return totalTime == 0 ? 0 : REWARD_RATE * rateMultiplier / totalTime / 1000;
}
}
{
"compilationTarget": {
"src/periphery/StakingETH.sol": "StakingETH"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@uniswap/v3-core/=lib/v3-core/",
":@uniswap/v3-periphery/=lib/v3-periphery/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
":openzeppelin/=lib/openzeppelin-contracts/contracts/",
":solmate/=lib/solmate/src/",
":synthetix-v3/=lib/synthetix-v3/",
":v3-core/=lib/v3-core/contracts/",
":v3-periphery/=lib/v3-periphery/contracts/"
]
}
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_multisig","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"NotAllowed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipTransferCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"inputs":[],"name":"FEE_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARD_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"boostPeriods","outputs":[{"internalType":"uint32","name":"timeStamp","type":"uint32"},{"internalType":"uint16","name":"multiplier","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelTransferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"collectFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_userBalance","type":"uint256"},{"internalType":"uint256","name":"_rewardPerToken","type":"uint256"},{"internalType":"uint256","name":"_userReward","type":"uint256"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_userBalance","type":"uint256"},{"internalType":"uint256","name":"_rewardPerToken","type":"uint256"},{"internalType":"uint256","name":"_userReward","type":"uint256"}],"name":"earnedFyde","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ethToCollect","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeToCollect","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getRewardFyde","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateTime","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multisig","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"period_finish","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerTokenFyde","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerTokenStoredFyde","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRateFyde","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardsFyde","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"timeStamp","type":"uint32"},{"internalType":"uint16","name":"multiplier","type":"uint16"}],"internalType":"struct StakingETH.BoostPeriod[]","name":"_boostPeriods","type":"tuple[]"}],"name":"setBoostPeriods","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"_start_date","type":"uint64"},{"internalType":"uint64","name":"_period_finish","type":"uint64"}],"name":"setStartAndEndTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"start_date","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","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":"address","name":"","type":"address"}],"name":"userRewardPerTokenPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRewardPerTokenPaidFyde","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]