编译器
0.8.17+commit.8df45f5f
文件 1 的 6: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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 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 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 的 6:Ownable.sol
pragma solidity ^0.8.0;
import {Context} from "./Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 4 的 6:Pausable.sol
pragma solidity ^0.8.0;
import {Context} from "./Context.sol";
abstract contract Pausable is Context {
bool private _paused;
event Paused(address account);
event Unpaused(address account);
error EnforcedPause();
error ExpectedPause();
constructor() {
_paused = false;
}
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
return _paused;
}
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 5 的 6:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 6 的 6:Staking.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./Ownable.sol";
import "./ReentrancyGuard.sol";
import "./Pausable.sol";
contract InscribeStakingV1 is Ownable, ReentrancyGuard, Pausable {
IERC20 public insToken;
IERC20 public wethToken;
struct Deposit {
uint256 amountWETH;
uint256 totalStaked;
uint256 timestamp;
}
struct Stake {
StakeEntry[] entries;
uint256 amount;
uint256 lastClaimedDepositIndex;
uint256 unlockTime;
uint256 lastEntryTimestamp;
}
struct StakeEntry {
uint256 amount;
uint256 timestamp;
}
Deposit[] public deposits;
mapping(address => Stake) public stakes;
uint256 public totalStaked;
uint256 public nbDeposits;
bool public skipLock;
event Staked(address indexed user, uint256 amount);
event Unstaked(address indexed user, uint256 amount);
event RewardPaid(address indexed user, uint256 reward);
event WETHDeposited(uint256 amount, uint256 totalStaked, uint256 timestamp);
constructor() Ownable(msg.sender) {
insToken = IERC20(0xe9572938bcbf08adceE86Fd12A7C0D08dC4Ab841);
wethToken = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
}
function setSkipLock(bool _value) external onlyOwner {
skipLock = _value;
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
function depositWETH(uint256 _amount) external onlyOwner {
require(wethToken.transferFrom(msg.sender, address(this), _amount), "Transfer failed");
deposits.push(Deposit({
amountWETH: _amount,
totalStaked: totalStaked,
timestamp: block.timestamp
}));
nbDeposits += 1;
emit WETHDeposited(_amount, totalStaked, block.timestamp);
}
function stake(uint256 _amount) external nonReentrant whenNotPaused {
require(_amount > 0, "Cannot stake 0");
updateRewards(msg.sender);
stakes[msg.sender].amount += _amount;
stakes[msg.sender].entries.push(StakeEntry({
amount: _amount,
timestamp: block.timestamp
}));
stakes[msg.sender].lastEntryTimestamp = block.timestamp;
stakes[msg.sender].unlockTime = 0;
totalStaked += _amount;
require(insToken.transferFrom(msg.sender, address(this), _amount), "Stake transfer failed");
emit Staked(msg.sender, _amount);
}
function unstake(uint256 _amount) external nonReentrant {
require(_amount > 0, "Cannot unstake 0");
require(stakes[msg.sender].amount >= _amount, "Insufficient stake");
if (!skipLock) require(stakes[msg.sender].unlockTime > 0 && block.timestamp >= stakes[msg.sender].unlockTime, "Stake is locked");
updateRewards(msg.sender);
stakes[msg.sender].amount -= _amount;
delete stakes[msg.sender].entries;
stakes[msg.sender].entries.push(StakeEntry({
amount: stakes[msg.sender].amount,
timestamp: block.timestamp
}));
stakes[msg.sender].lastEntryTimestamp = block.timestamp;
totalStaked -= _amount;
require(insToken.transfer(msg.sender, _amount), "Unstake transfer failed");
emit Unstaked(msg.sender, _amount);
}
function requestUnstake() external {
require(stakes[msg.sender].amount > 0, "No stake");
require(stakes[msg.sender].unlockTime == 0, "Unstake already requested");
stakes[msg.sender].unlockTime = block.timestamp + 30 days;
}
function claimRewards() external nonReentrant {
updateRewards(msg.sender);
}
function updateRewards(address user) internal {
uint256 reward = 0;
Stake memory userStake = stakes[user];
uint256 userStakeAtDeposit = 0;
uint256 entryIndex = 0;
for (uint256 i = userStake.lastClaimedDepositIndex; i < deposits.length; i++) {
Deposit memory deposit = deposits[i];
if (userStake.lastEntryTimestamp < deposit.timestamp) {
userStakeAtDeposit = userStake.amount;
} else {
for (; entryIndex < userStake.entries.length; entryIndex++) {
StakeEntry memory entry = userStake.entries[entryIndex];
if (entry.timestamp < deposit.timestamp) userStakeAtDeposit += entry.amount;
else break;
}
}
reward += (userStakeAtDeposit * deposit.amountWETH) / deposit.totalStaked;
}
stakes[user].lastClaimedDepositIndex = deposits.length;
if (reward > 0) {
require(wethToken.transfer(user, reward), "Reward transfer failed");
emit RewardPaid(user, reward);
}
}
function calculateTotalStakeAt(address user, uint256 timestamp) external view returns (uint256) {
uint256 totalStake = 0;
StakeEntry[] memory entries = stakes[user].entries;
for (uint256 i = 0; i < entries.length; i++) {
if (entries[i].timestamp < timestamp) {
totalStake += entries[i].amount;
} else {
break;
}
}
return totalStake;
}
}
{
"compilationTarget": {
"Staking.sol": "InscribeStakingV1"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 800
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"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":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardPaid","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"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalStaked","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"WETHDeposited","type":"event"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"calculateTotalStakeAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"depositWETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"deposits","outputs":[{"internalType":"uint256","name":"amountWETH","type":"uint256"},{"internalType":"uint256","name":"totalStaked","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"insToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nbDeposits","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_value","type":"bool"}],"name":"setSkipLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"skipLock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakes","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"lastClaimedDepositIndex","type":"uint256"},{"internalType":"uint256","name":"unlockTime","type":"uint256"},{"internalType":"uint256","name":"lastEntryTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStaked","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":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wethToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}]