编译器
0.8.17+commit.8df45f5f
文件 1 的 4:IERC20.sol
pragma solidity =0.8.17;
interface IERC20 {
function transfer(
address recipient,
uint256 amount
)
external
returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
)
external
returns (bool);
}
文件 2 的 4:SafeERC20.sol
pragma solidity =0.8.17;
import "./IERC20.sol";
contract SafeERC20 {
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 callOptionalReturn(
IERC20 _token,
bytes memory _data
)
private
{
(
bool success,
bytes memory returndata
) = address(_token).call(_data);
require(
success,
"SafeERC20: CALL_FAILED"
);
if (returndata.length > 0) {
require(
abi.decode(
returndata,
(bool)
),
"SafeERC20: OPERATION_FAILED"
);
}
}
}
文件 3 的 4:SimpleFarm.sol
pragma solidity =0.8.17;
import "./TokenWrapper.sol";
contract SimpleFarm is TokenWrapper {
IERC20 public immutable stakeToken;
IERC20 public immutable rewardToken;
uint256 public rewardRate;
uint256 public periodFinished;
uint256 public rewardDuration;
uint256 public lastUpdateTime;
uint256 public perTokenStored;
uint256 constant PRECISION = 1E18;
mapping(address => uint256) public userRewards;
mapping(address => uint256) public perTokenPaid;
address public ownerAddress;
address public proposedOwner;
address public managerAddress;
modifier onlyOwner() {
require(
msg.sender == ownerAddress,
"SimpleFarm: INVALID_OWNER"
);
_;
}
modifier onlyManager() {
require(
msg.sender == managerAddress,
"SimpleFarm: INVALID_MANAGER"
);
_;
}
modifier updateFarm() {
perTokenStored = rewardPerToken();
lastUpdateTime = lastTimeRewardApplicable();
_;
}
modifier updateUser() {
userRewards[msg.sender] = earned(msg.sender);
perTokenPaid[msg.sender] = perTokenStored;
_;
}
modifier updateSender(address sender) {
userRewards[sender] = earned(sender);
perTokenPaid[sender] = perTokenStored;
_;
}
event Staked(
address indexed user,
uint256 tokenAmount
);
event Withdrawn(
address indexed user,
uint256 tokenAmount
);
event RewardAdded(
uint256 rewardRate,
uint256 tokenAmount
);
event RewardPaid(
address indexed user,
uint256 tokenAmount
);
event Recovered(
IERC20 indexed token,
uint256 tokenAmount
);
event RewardsDurationUpdated(
uint256 newRewardDuration
);
event OwnerProposed(
address proposedOwner
);
event OwnerChanged(
address newOwner
);
event ManagerChanged(
address newManager
);
constructor(
IERC20 _stakeToken,
IERC20 _rewardToken,
uint256 _defaultDuration
) {
require(
_defaultDuration > 0,
"SimpleFarm: INVALID_DURATION"
);
stakeToken = _stakeToken;
rewardToken = _rewardToken;
ownerAddress = msg.sender;
managerAddress = msg.sender;
rewardDuration = _defaultDuration;
}
function lastTimeRewardApplicable()
public
view
returns (uint256 res)
{
res = block.timestamp < periodFinished
? block.timestamp
: periodFinished;
}
function rewardPerToken()
public
view
returns (uint256)
{
if (_totalStaked == 0) {
return perTokenStored;
}
uint256 timeFrame = lastTimeRewardApplicable()
- lastUpdateTime;
uint256 extraFund = timeFrame
* rewardRate
* PRECISION
/ _totalStaked;
return perTokenStored
+ extraFund;
}
function earned(
address _walletAddress
)
public
view
returns (uint256)
{
uint256 difference = rewardPerToken()
- perTokenPaid[_walletAddress];
return _balances[_walletAddress]
* difference
/ PRECISION
+ userRewards[_walletAddress];
}
function farmDeposit(
uint256 _stakeAmount
)
external
updateFarm()
updateUser()
{
address senderAddress = msg.sender;
_stake(
_stakeAmount,
senderAddress
);
safeTransferFrom(
stakeToken,
senderAddress,
address(this),
_stakeAmount
);
emit Staked(
senderAddress,
_stakeAmount
);
}
function farmWithdraw(
uint256 _withdrawAmount
)
public
updateFarm()
updateUser()
{
if (block.timestamp < periodFinished) {
require(
_totalStaked > _withdrawAmount,
"SimpleFarm: STILL_EARNING"
);
}
address senderAddress = msg.sender;
_withdraw(
_withdrawAmount,
senderAddress
);
safeTransfer(
stakeToken,
senderAddress,
_withdrawAmount
);
emit Withdrawn(
senderAddress,
_withdrawAmount
);
}
function exitFarm()
external
{
uint256 withdrawAmount = _balances[
msg.sender
];
farmWithdraw(
withdrawAmount
);
claimReward();
}
function claimReward()
public
updateFarm()
updateUser()
returns (uint256 rewardAmount)
{
address senderAddress = msg.sender;
rewardAmount = earned(
senderAddress
);
require(
rewardAmount > 0,
"SimpleFarm: NOTHING_TO_CLAIM"
);
userRewards[senderAddress] = 0;
safeTransfer(
rewardToken,
senderAddress,
rewardAmount
);
emit RewardPaid(
senderAddress,
rewardAmount
);
}
function proposeNewOwner(
address _newOwner
)
external
onlyOwner
{
if (_newOwner == ZERO_ADDRESS) {
revert("SimpleFarm: WRONG_ADDRESS");
}
proposedOwner = _newOwner;
emit OwnerProposed(
_newOwner
);
}
function claimOwnership()
external
{
require(
msg.sender == proposedOwner,
"SimpleFarm: INVALID_CANDIDATE"
);
ownerAddress = proposedOwner;
emit OwnerChanged(
ownerAddress
);
}
function changeManager(
address _newManager
)
external
onlyOwner
{
if (_newManager == ZERO_ADDRESS) {
revert("SimpleFarm: WRONG_ADDRESS");
}
managerAddress = _newManager;
emit ManagerChanged(
_newManager
);
}
function recoverToken(
IERC20 tokenAddress,
uint256 tokenAmount
)
external
{
if (tokenAddress == stakeToken) {
revert("SimpleFarm: INVALID_TOKEN");
}
if (tokenAddress == rewardToken) {
revert("SimpleFarm: INVALID_TOKEN");
}
safeTransfer(
tokenAddress,
ownerAddress,
tokenAmount
);
emit Recovered(
tokenAddress,
tokenAmount
);
}
function setRewardDuration(
uint256 _rewardDuration
)
external
onlyManager
{
require(
_rewardDuration > 0,
"SimpleFarm: INVALID_DURATION"
);
require(
block.timestamp > periodFinished,
"SimpleFarm: ONGOING_DISTRIBUTION"
);
rewardDuration = _rewardDuration;
emit RewardsDurationUpdated(
_rewardDuration
);
}
function setRewardRate(
uint256 _newRewardRate
)
external
onlyManager
updateFarm()
{
require(
_totalStaked > 0,
"SimpleFarm: NO_STAKERS"
);
require(
_newRewardRate > 0,
"SimpleFarm: INVALID_RATE"
);
uint256 currentPeriodFinish = periodFinished;
lastUpdateTime = block.timestamp;
periodFinished = block.timestamp
+ rewardDuration;
if (block.timestamp < currentPeriodFinish) {
require(
_newRewardRate >= rewardRate,
"SimpleFarm: RATE_CANT_DECREASE"
);
uint256 remainingTime = currentPeriodFinish
- block.timestamp;
uint256 rewardRemains = remainingTime
* rewardRate;
safeTransfer(
rewardToken,
managerAddress,
rewardRemains
);
}
rewardRate = _newRewardRate;
uint256 newRewardAmount = rewardDuration
* _newRewardRate;
safeTransferFrom(
rewardToken,
managerAddress,
address(this),
newRewardAmount
);
emit RewardAdded(
_newRewardRate,
newRewardAmount
);
}
function transfer(
address _recipient,
uint256 _amount
)
external
updateFarm()
updateUser()
updateSender(_recipient)
returns (bool)
{
_transfer(
msg.sender,
_recipient,
_amount
);
return true;
}
function transferFrom(
address _sender,
address _recipient,
uint256 _amount
)
external
updateFarm()
updateSender(_sender)
updateSender(_recipient)
returns (bool)
{
if (_allowances[_sender][msg.sender] != type(uint256).max) {
_allowances[_sender][msg.sender] -= _amount;
}
_transfer(
_sender,
_recipient,
_amount
);
return true;
}
}
文件 4 的 4:TokenWrapper.sol
pragma solidity =0.8.17;
import "./SafeERC20.sol";
contract TokenWrapper is SafeERC20 {
string public constant name = "VerseFarm";
string public constant symbol = "VFARM";
uint8 public constant decimals = 18;
uint256 _totalStaked;
mapping(address => uint256) _balances;
mapping(address => mapping(address => uint256)) _allowances;
address constant ZERO_ADDRESS = address(0x0);
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)
{
return _totalStaked;
}
function balanceOf(
address _walletAddress
)
external
view
returns (uint256)
{
return _balances[_walletAddress];
}
function _stake(
uint256 _amount,
address _address
)
internal
{
_totalStaked =
_totalStaked + _amount;
unchecked {
_balances[_address] =
_balances[_address] + _amount;
}
emit Transfer(
ZERO_ADDRESS,
_address,
_amount
);
}
function _withdraw(
uint256 _amount,
address _address
)
internal
{
unchecked {
_totalStaked =
_totalStaked - _amount;
}
_balances[_address] =
_balances[_address] - _amount;
emit Transfer(
_address,
ZERO_ADDRESS,
_amount
);
}
function _transfer(
address _sender,
address _recipient,
uint256 _amount
)
internal
{
_balances[_sender] =
_balances[_sender] - _amount;
unchecked {
_balances[_recipient] =
_balances[_recipient] + _amount;
}
emit Transfer(
_sender,
_recipient,
_amount
);
}
function approve(
address _spender,
uint256 _amount
)
external
returns (bool)
{
_approve(
msg.sender,
_spender,
_amount
);
return true;
}
function allowance(
address _owner,
address _spender
)
external
view
returns (uint256)
{
return _allowances[_owner][_spender];
}
function _approve(
address _owner,
address _spender,
uint256 _amount
)
internal
{
_allowances[_owner][_spender] = _amount;
emit Approval(
_owner,
_spender,
_amount
);
}
function increaseAllowance(
address _spender,
uint256 _addedValue
)
external
returns (bool)
{
_approve(
msg.sender,
_spender,
_allowances[msg.sender][_spender] + _addedValue
);
return true;
}
function decreaseAllowance(
address _spender,
uint256 _subtractedValue
)
external
returns (bool)
{
_approve(
msg.sender,
_spender,
_allowances[msg.sender][_spender] - _subtractedValue
);
return true;
}
}
{
"compilationTarget": {
"SimpleFarm.sol": "SimpleFarm"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC20","name":"_stakeToken","type":"address"},{"internalType":"contract IERC20","name":"_rewardToken","type":"address"},{"internalType":"uint256","name":"_defaultDuration","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newManager","type":"address"}],"name":"ManagerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"proposedOwner","type":"address"}],"name":"OwnerProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rewardRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newRewardDuration","type":"uint256"}],"name":"RewardsDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_walletAddress","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newManager","type":"address"}],"name":"changeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimReward","outputs":[{"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_walletAddress","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exitFarm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stakeAmount","type":"uint256"}],"name":"farmDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_withdrawAmount","type":"uint256"}],"name":"farmWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"res","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"managerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"perTokenPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"perTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"periodFinished","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"proposeNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proposedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_rewardDuration","type":"uint256"}],"name":"setRewardDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newRewardRate","type":"uint256"}],"name":"setRewardRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]