编译器
0.8.21+commit.d9974bed
文件 1 的 5:Context.sol
pragma solidity ^0.8.20;
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 的 5: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 的 5:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/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 的 5:ReentrancyGuard.sol
pragma solidity ^0.8.20;
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;
}
}
文件 5 的 5:RunicStake.sol
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract RunicStake is Ownable, ReentrancyGuard {
IERC20 public runic = IERC20(0xeb4F5c4bF62faC1be7e4BEf9c7C055BF1C52241D);
address public deployer;
uint256 public totalDistributed;
uint256 public totalStaked;
uint256 private magnifiedPerShare;
uint256 private constant magnitude = 2 ** 128;
uint256 public constant LOCK_TIME = 1 days;
mapping(address => uint256) public staked;
mapping(address => uint256) public claimed;
mapping(address => uint256) public magnifiedCorrections;
mapping(address => uint256) public lastStakeTime;
event Staked(address indexed user, uint256 amount);
event UnStake(address indexed user, uint256 amount);
event Claimed(address indexed user, uint256 amount);
event DepositeRewards(uint256 amount);
constructor() Ownable(_msgSender()) {
deployer = _msgSender();
}
function claimableOf(address _owner) public view returns (uint256) {
return accumulativeOf(_owner) - claimed[_owner];
}
function accumulativeOf(address _owner) public view returns (uint256) {
unchecked {
return
((magnifiedPerShare * staked[_owner]) +
magnifiedCorrections[_owner]) / magnitude;
}
}
function stake(uint256 _amount) external nonReentrant {
require(_amount > 0, "Runic: Cannot stake 0");
runic.transferFrom(_msgSender(), address(this), _amount);
_claim(_msgSender());
staked[_msgSender()] += _amount;
totalStaked += _amount;
unchecked {
magnifiedCorrections[_msgSender()] -= (magnifiedPerShare * _amount);
}
emit Staked(_msgSender(), _amount);
}
function unStake() external nonReentrant {
require(staked[_msgSender()] > 0, "Runic: Cannot withdraw 0");
require(
block.timestamp > lastStakeTime[_msgSender()] + LOCK_TIME,
"Runic: Locked for 1 day after staking"
);
_claim(_msgSender());
totalStaked -= staked[_msgSender()];
unchecked {
magnifiedCorrections[_msgSender()] += (magnifiedPerShare *
staked[_msgSender()]);
}
runic.transfer(_msgSender(), staked[_msgSender()]);
staked[_msgSender()] = 0;
emit UnStake(_msgSender(), staked[_msgSender()]);
}
function claim() external nonReentrant {
uint256 claimable = claimableOf(_msgSender());
require(claimable > 0, "Runic: Nothing to claim");
_claim(_msgSender());
}
function _claim(address _user) internal {
lastStakeTime[_user] = block.timestamp;
uint256 claimable = claimableOf(_user);
if (claimable > 0) {
claimed[_user] += claimable;
_safeTransferETH(_user, claimable);
}
emit Claimed(_user, claimable);
}
function _depositReward(uint256 amount) private {
if (totalStaked > 0) {
totalDistributed += amount;
unchecked {
magnifiedPerShare =
magnifiedPerShare +
((amount * magnitude) / totalStaked);
}
emit DepositeRewards(amount);
} else {
_safeTransferETH(deployer, amount);
}
}
function _safeTransferETH(address to, uint256 amount) private {
(bool sent, ) = payable(to).call{value: amount}("");
require(sent, "Runic: Transfer ETH failed");
}
function rescueETH() external onlyOwner {
(bool _sent, ) = payable(_msgSender()).call{
value: address(this).balance
}("");
require(_sent);
}
receive() external payable {
_depositReward(msg.value);
}
}
{
"compilationTarget": {
"src/RunicStake.sol": "RunicStake"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"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":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DepositeRewards","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":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UnStake","type":"event"},{"inputs":[],"name":"LOCK_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"accumulativeOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"claimableOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastStakeTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"magnifiedCorrections","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":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rescueETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"runic","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"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":"staked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDistributed","outputs":[{"internalType":"uint256","name":"","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":"unStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]