编译器
0.8.12+commit.f00d7308
文件 1 的 5:Address.sol
pragma solidity ^0.8.12;
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 的 5:EnumerableSet.sol
pragma solidity ^0.8.12;
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) {
return _values(set._inner);
}
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;
}
}
文件 3 的 5:IERC20.sol
pragma solidity ^0.8.12;
interface IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function factory() external view returns (address);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function nonces(address account) external view returns (uint256);
function mint(address to, uint256 amount) external;
function burn(uint256 amount) external;
function approve(address spender, uint256 amount) external returns (bool);
function transfer(address recipient, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 4 的 5:SafeERC20.sol
pragma solidity ^0.8.12;
import "../interfaces/IERC20.sol";
import "./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(
IERC20 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");
}
}
}
文件 5 的 5:Staking.sol
pragma solidity ^0.8.12;
import "./interfaces/IERC20.sol";
import "./libraries/SafeERC20.sol";
import "./libraries/EnumerableSet.sol";
contract Staking {
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.UintSet;
mapping (address => EnumerableSet.UintSet) orderList;
uint256 idx;
bool private entered;
address public owner;
IERC20 public L;
struct Order {
address owner;
uint256 amount;
uint256 shares;
uint256 debt;
uint256 multiplier;
uint256 stakeTime;
uint256 unstakeTime;
}
mapping (uint256 => Order) public orders;
mapping (uint256 => uint256) public multipliers;
uint256 public totalAmountStaked;
uint256 public totalShares;
uint256 public rewardPerSecond;
uint256 public accRewardPerShare;
uint256 public totalReward;
uint256 public lastRewardTime;
uint256 public deadline;
event MultiplierEnabled(uint256 indexed day, uint256 indexed multiplier);
event OwnershipTransferred(address indexed user, address indexed newOwner);
event RewardPerSecondChanged(uint256 indexed rewardPerSecond, uint256 startTime, uint256 endTime);
event Staked(address indexed account, uint256 indexed amount);
event Unstaked(address indexed account, uint256 indexed amount, uint256 reward);
constructor(address token) {
L = IERC20(token);
multipliers[0] = 1 ether;
emit MultiplierEnabled(0, 1 ether);
multipliers[30] = 1.2 ether;
emit MultiplierEnabled(30, 1.2 ether);
multipliers[60] = 1.5 ether;
emit MultiplierEnabled(60, 1.5 ether);
multipliers[90] = 2 ether;
emit MultiplierEnabled(90, 2 ether);
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
modifier nonReentrant() {
require(!entered, "reentrant");
entered = true;
_;
entered = false;
}
modifier onlyOwner() {
require(msg.sender == owner, "unauthorized");
_;
}
function stake(uint256 amount, uint256 day) external nonReentrant {
require(amount > 0, "amount must be greater than 0");
require(L.balanceOf(msg.sender) >= amount, "insufficient balance");
require(L.allowance(msg.sender, address(this)) >= amount, "insufficient allowance");
update();
uint256 multiplier = multipliers[day];
require(multiplier != 0, "multiplier does not exist");
L.safeTransferFrom(msg.sender, address(this), amount);
idx++;
Order storage order = orders[idx];
order.owner = msg.sender;
order.amount += amount;
order.shares = amount * multiplier / 10**18;
order.debt = order.shares * accRewardPerShare / 10**18;
order.multiplier = multiplier;
order.stakeTime = block.timestamp;
order.unstakeTime = block.timestamp + day * 86400;
addOrder(msg.sender, idx);
totalAmountStaked += amount;
totalShares += order.shares;
emit Staked(msg.sender, amount);
}
function unstake(uint256 id) external nonReentrant {
Order storage order = orders[id];
uint256 amount = order.amount;
require(amount > 0, "not exist");
require(order.owner == msg.sender, "only owner");
require(order.unstakeTime < block.timestamp, "not due");
update();
uint256 reward = order.shares * accRewardPerShare / 10**18 - order.debt;
totalAmountStaked -= amount;
totalShares -= order.shares;
delOrder(msg.sender, id);
delete orders[id];
L.safeTransfer(msg.sender, amount + reward);
emit Unstaked(msg.sender, amount, reward);
}
function transferOwnership(address newOwner) external onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
function changeRewardPerSecond(uint256 newRewardPerSecond, uint256 newDeadline) external onlyOwner {
update();
require(newDeadline > lastRewardTime, "forbid");
uint256 balance = (newDeadline - lastRewardTime) * newRewardPerSecond;
if (totalReward < balance) {
balance -= totalReward;
totalReward += balance;
L.safeTransferFrom(msg.sender, address(this), balance);
}
rewardPerSecond = newRewardPerSecond;
deadline = newDeadline;
emit RewardPerSecondChanged(rewardPerSecond, lastRewardTime, deadline);
}
function enabledMultiplier(uint256 day, uint256 multiplier) external onlyOwner {
multipliers[day] = multiplier;
emit MultiplierEnabled(day, multiplier);
}
function earned(uint256 id) external view returns (uint256) {
Order memory order = orders[id];
if (order.amount == 0) {
return 0;
}
uint256 ts;
if (block.timestamp >= deadline) {
if (deadline > lastRewardTime) {
ts = deadline - lastRewardTime;
}
} else {
ts = block.timestamp - lastRewardTime;
}
uint256 reward = rewardPerSecond * ts;
if (totalReward < reward) {
reward = totalReward;
}
uint256 _accRewardPerShare = accRewardPerShare + reward * 10**18 / totalShares;
return order.shares * _accRewardPerShare / 10**18 - order.debt;
}
function getOrderLength(address account) public view returns (uint256) {
return EnumerableSet.length(orderList[account]);
}
function getOrderList(address account) public view returns (uint256[] memory) {
return EnumerableSet.values(orderList[account]);
}
function isOrderExist(address account, uint256 id) public view returns (bool) {
return EnumerableSet.contains(orderList[account], id);
}
function getOrder(address account, uint256 index) public view returns (uint256) {
require(index <= getOrderLength(account) - 1, "order index out of bounds");
return EnumerableSet.at(orderList[account], index);
}
function getBlockTimestamp() external view returns (uint256) {
return block.timestamp;
}
function addOrder(address account, uint256 id) internal returns (bool) {
require(!isOrderExist(account, id), "order exist");
return EnumerableSet.add(orderList[account], id);
}
function delOrder(address account, uint256 id) internal returns (bool) {
require(isOrderExist(account, id), "order not exist");
return EnumerableSet.remove(orderList[account], id);
}
function update() internal {
if (block.timestamp <= lastRewardTime) {
return;
}
if (totalShares == 0) {
lastRewardTime = block.timestamp;
return;
}
uint256 ts;
if (block.timestamp >= deadline) {
if (deadline > lastRewardTime) {
ts = deadline - lastRewardTime;
}
} else {
ts = block.timestamp - lastRewardTime;
}
if (ts > 0) {
uint256 reward = rewardPerSecond * ts;
if (totalReward >= reward) {
totalReward -= reward;
} else {
reward = totalReward;
totalReward = 0;
}
accRewardPerShare += reward * 10**18 / totalShares;
lastRewardTime = block.timestamp;
}
}
}
{
"compilationTarget": {
"src/Staking.sol": "Staking"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/"
]
}
[{"inputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"day","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"MultiplierEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"rewardPerSecond","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"RewardPerSecondChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"Unstaked","type":"event"},{"inputs":[],"name":"L","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accRewardPerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newRewardPerSecond","type":"uint256"},{"internalType":"uint256","name":"newDeadline","type":"uint256"}],"name":"changeRewardPerSecond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deadline","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"day","type":"uint256"},{"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"enabledMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBlockTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getOrder","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getOrderLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getOrderList","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"isOrderExist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRewardTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"multipliers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"orders","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"multiplier","type":"uint256"},{"internalType":"uint256","name":"stakeTime","type":"uint256"},{"internalType":"uint256","name":"unstakeTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerSecond","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"day","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAmountStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalShares","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":"id","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"}]