文件 1 的 2:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, 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 sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 2 的 2:MuseStaker.sol
pragma solidity 0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract MuseStaker {
IERC20 public MUSE = IERC20(0xB6Ca7399B4F9CA56FC27cBfF44F4d2e4Eef1fc81);
mapping(address => uint256) public shares;
mapping(address => uint256) public timeLock;
mapping(address => uint256) public amountLocked;
uint256 public totalShares;
uint256 public unlockPeriod = 10 days;
address public owner;
constructor() {
owner = msg.sender;
}
function changeUnlockPeriod(uint256 _period) external {
require(msg.sender == owner, "forbidden");
unlockPeriod = _period;
}
function stake(uint256 _amount) public {
timeLock[msg.sender] = 0;
amountLocked[msg.sender] = amountLocked[msg.sender] + _amount;
uint256 totalMuse = MUSE.balanceOf(address(this));
if (totalShares == 0 || totalMuse == 0) {
shares[msg.sender] = _amount;
totalShares += _amount;
} else {
uint256 bal = (_amount * totalShares) / (totalMuse);
shares[msg.sender] += bal;
totalShares += bal;
}
MUSE.transferFrom(msg.sender, address(this), _amount);
}
function startUnstake() public {
timeLock[msg.sender] = block.timestamp + unlockPeriod;
}
function unstake() public {
uint256 lockedUntil = timeLock[msg.sender];
timeLock[msg.sender] = 0;
require(
lockedUntil != 0 &&
block.timestamp >= lockedUntil &&
block.timestamp <= lockedUntil + 2 days,
"!still locked"
);
_unstake();
}
function _unstake() internal {
uint256 bal =
(shares[msg.sender] * MUSE.balanceOf(address(this))) /
(totalShares);
totalShares -= shares[msg.sender];
shares[msg.sender] = 0;
amountLocked[msg.sender] = 0;
MUSE.transfer(msg.sender, bal);
}
function claim() public {
uint256 amount = amountLocked[msg.sender];
_unstake();
stake(amount);
}
function balance(address _user) public view returns (uint256) {
if (totalShares == 0) {
return 0;
}
uint256 bal =
(shares[_user] * MUSE.balanceOf(address(this))) / (totalShares);
return bal;
}
function userInfo(address _user)
public
view
returns (
uint256 bal,
uint256 claimable,
uint256 deposited,
uint256 timelock,
bool isClaimable,
uint256 globalShares,
uint256 globalBalance
)
{
bal = balance(_user);
if (bal > amountLocked[_user]) {
claimable = bal - amountLocked[_user];
}
deposited = amountLocked[_user];
timelock = timeLock[_user];
isClaimable = (timelock != 0 &&
block.timestamp >= timelock &&
block.timestamp <= timelock + 2 days);
globalShares = totalShares;
globalBalance = MUSE.balanceOf(address(this));
}
}
{
"compilationTarget": {
"contracts/MuseStaker.sol": "MuseStaker"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"MUSE","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"amountLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"balance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_period","type":"uint256"}],"name":"changeUnlockPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"shares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"timeLock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unlockPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"bal","type":"uint256"},{"internalType":"uint256","name":"claimable","type":"uint256"},{"internalType":"uint256","name":"deposited","type":"uint256"},{"internalType":"uint256","name":"timelock","type":"uint256"},{"internalType":"bool","name":"isClaimable","type":"bool"},{"internalType":"uint256","name":"globalShares","type":"uint256"},{"internalType":"uint256","name":"globalBalance","type":"uint256"}],"stateMutability":"view","type":"function"}]