编译器
0.8.19+commit.7dd6d404
文件 1 的 6:Address.sol
pragma solidity ^0.8.1;
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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 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 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 from, address to, uint256 amount) external returns (bool);
}
文件 3 的 6:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 4 的 6:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 5 的 6:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/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 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
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");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit 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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
文件 6 的 6:ZeroLiquidStaking.sol
pragma solidity 0.8.19;
import { SafeERC20 } from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20Metadata } from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
error AmountLessThanStakedAmountOrZero();
error CallerNotGovernance();
error EtherNotAccepted();
error InsufficientFunds();
error InsufficientZeroFunds();
error InputLengthMismatch();
error NoPendingRewardsToClaim();
error NoStakeFound();
error ZeroAddressInput();
error RewardDistributionPeriodHasExpired();
error RewardPerBlockIsNotSet();
error SameRewardToken();
error ZeroAddress();
error ZeroInput();
contract ZeroLiquidStaking {
using SafeERC20 for IERC20Metadata;
struct UserInfo {
uint256 lastUpdateRewardToken;
uint256 amount;
uint256 rewardDebt;
}
enum TxType {
STAKE,
UNSTAKE,
CLAIM,
EMERGENCY
}
IERC20Metadata public immutable zeroToken;
IERC20Metadata public rewardToken;
address public governance;
uint256 public constant ONE = 1e18;
uint256 public accRewardPerZero;
uint256 public lastUpdateBlock;
uint256 public totalZeroStaked;
uint256 public currentRewardPerBlock;
uint256 public periodEndBlock;
uint256 public lastUpdateRewardToken;
mapping(address => UserInfo) public userInfo;
event StakeOrUnstakeOrClaim(address indexed user, uint256 amount, uint256 pendingReward, TxType txType);
event NewRewardPeriod(
uint256 numberBlocksToDistributeRewards,
uint256 newRewardPerBlock,
uint256 rewardToDistribute,
uint256 rewardExpirationBlock
);
event GovernanceChanged(address indexed oldGovernance, address indexed newGovernance);
event RewardTokenChanged(address indexed oldRewardToken, address indexed newRewardToken);
event FundsMigrated(address indexed _newVersion, IERC20Metadata[] _tokens, uint256[] _amounts);
event PeriodEndBlockUpdate(uint256 numberBlocksToDistributeRewards, uint256 rewardExpirationBlock);
constructor(address _governance, address _rewardToken, address _zeroToken) {
if (_governance == address(0) || _rewardToken == address(0) || _zeroToken == address(0)) revert ZeroAddress();
governance = _governance;
rewardToken = IERC20Metadata(_rewardToken);
zeroToken = IERC20Metadata(_zeroToken);
emit GovernanceChanged(address(0), _governance);
emit RewardTokenChanged(address(0), _rewardToken);
}
receive() external payable {
revert EtherNotAccepted();
}
modifier onlyGovernance() {
if (msg.sender != governance) revert CallerNotGovernance();
_;
}
function setGovernance(address _newGovernance) external onlyGovernance {
if (_newGovernance == address(0)) revert ZeroAddress();
governance = _newGovernance;
emit GovernanceChanged(governance, _newGovernance);
}
function updateRewardToken(address _newRewardToken) external onlyGovernance {
if (_newRewardToken == address(rewardToken)) revert SameRewardToken();
if (_newRewardToken == address(0)) revert ZeroAddress();
accRewardPerZero = 0;
lastUpdateBlock = _lastRewardBlock();
lastUpdateRewardToken = block.timestamp;
rewardToken = IERC20Metadata(_newRewardToken);
emit RewardTokenChanged(address(rewardToken), _newRewardToken);
}
function updateRewards(uint256 _reward, uint256 _rewardDurationInBlocks) external onlyGovernance {
if (_rewardDurationInBlocks == 0) revert ZeroInput();
_updateRewardPerZeroAndLastBlock();
if (block.number >= periodEndBlock) {
if (_reward == 0) revert ZeroInput();
currentRewardPerBlock = _reward / _rewardDurationInBlocks;
}
else {
currentRewardPerBlock =
(_reward + ((periodEndBlock - block.number) * currentRewardPerBlock)) / _rewardDurationInBlocks;
}
lastUpdateBlock = block.number;
periodEndBlock = block.number + _rewardDurationInBlocks;
emit NewRewardPeriod(_rewardDurationInBlocks, currentRewardPerBlock, _reward, periodEndBlock);
}
function updateRewardEndBlock(uint256 _expireDurationInBlocks) external onlyGovernance {
_updateRewardPerZeroAndLastBlock();
lastUpdateBlock = block.number;
periodEndBlock = block.number + _expireDurationInBlocks;
emit PeriodEndBlockUpdate(_expireDurationInBlocks, periodEndBlock);
}
function migrateFunds(
address _newVersion,
IERC20Metadata[] calldata _tokens,
uint256[] calldata _amounts,
bool _isZeroMigrate
)
external
onlyGovernance
{
if (_newVersion == address(0)) revert ZeroAddress();
if (_tokens.length != _amounts.length) revert InputLengthMismatch();
IERC20Metadata tokenAddress;
uint256 amount;
for (uint256 i; i < _tokens.length;) {
tokenAddress = _tokens[i];
amount = _amounts[i];
if (tokenAddress == zeroToken) revert ZeroAddressInput();
if (address(tokenAddress) == address(0)) revert ZeroAddress();
if (amount == 0) revert ZeroInput();
if (amount > tokenAddress.balanceOf(address(this))) {
revert InsufficientFunds();
}
tokenAddress.safeTransfer(_newVersion, amount);
unchecked {
++i;
}
}
if (_isZeroMigrate) {
uint256 protocolZeroBalance = zeroToken.balanceOf(address(this)) - totalZeroStaked;
if (protocolZeroBalance > 0) {
zeroToken.safeTransfer(_newVersion, protocolZeroBalance);
} else {
revert InsufficientZeroFunds();
}
}
emit FundsMigrated(_newVersion, _tokens, _amounts);
}
function stake(address _to, uint256 _amount) external {
if (_amount == 0) revert ZeroInput();
if (_to == address(0)) revert ZeroAddress();
if (currentRewardPerBlock == 0) revert RewardPerBlockIsNotSet();
if (block.number >= periodEndBlock) {
revert RewardDistributionPeriodHasExpired();
}
if (rewardToken.balanceOf(address(this)) == 0) {
revert InsufficientFunds();
}
_stakeOrUnstakeOrClaim(_to, _amount, TxType.STAKE);
}
function unstake(uint256 _amount) external {
if ((_amount > userInfo[msg.sender].amount) || _amount == 0) {
revert AmountLessThanStakedAmountOrZero();
}
_stakeOrUnstakeOrClaim(msg.sender, _amount, TxType.UNSTAKE);
}
function emergencyUnstake() external {
if (userInfo[msg.sender].amount > 0) {
_stakeOrUnstakeOrClaim(msg.sender, userInfo[msg.sender].amount, TxType.EMERGENCY);
} else {
revert NoStakeFound();
}
}
function claim() external {
_stakeOrUnstakeOrClaim(msg.sender, userInfo[msg.sender].amount, TxType.CLAIM);
}
function calculatePendingRewards(address _user) external view returns (uint256) {
uint256 newAccRewardPerZero;
if (totalZeroStaked != 0) {
newAccRewardPerZero = accRewardPerZero
+ (((_lastRewardBlock() - lastUpdateBlock) * (currentRewardPerBlock * ONE)) / totalZeroStaked);
if (newAccRewardPerZero == 0) return 0;
} else {
return 0;
}
uint256 rewardDebt = userInfo[_user].rewardDebt;
if (userInfo[_user].lastUpdateRewardToken < lastUpdateRewardToken) {
rewardDebt = 0;
}
uint256 pendingRewards = ((userInfo[_user].amount * newAccRewardPerZero) / ONE) - rewardDebt;
if (_computeScalingFactor(rewardToken) != 1) {
pendingRewards = _downscale(pendingRewards);
}
return pendingRewards;
}
function lastRewardBlock() external view returns (uint256) {
return _lastRewardBlock();
}
function _stakeOrUnstakeOrClaim(address _to, uint256 _amount, TxType _txType) private {
_updateRewardPerZeroAndLastBlock();
_resetDebtIfNewRewardToken(_to);
UserInfo storage user = userInfo[_to];
uint256 pendingRewards;
if (TxType.EMERGENCY != _txType) {
if (user.amount > 0) {
pendingRewards = _calculatePendingRewards(_to);
if (_computeScalingFactor(rewardToken) != 1) {
pendingRewards = _downscale(pendingRewards);
}
if (pendingRewards > 0) {
if (pendingRewards > rewardToken.balanceOf(address(this))) {
revert InsufficientFunds();
}
rewardToken.safeTransfer(_to, pendingRewards);
}
else if (TxType.CLAIM == _txType) {
revert NoPendingRewardsToClaim();
}
}
else if (TxType.CLAIM == _txType) {
revert NoPendingRewardsToClaim();
}
}
if (TxType.STAKE == _txType) {
zeroToken.safeTransferFrom(msg.sender, address(this), _amount);
user.amount += _amount;
totalZeroStaked += _amount;
} else if (TxType.UNSTAKE == _txType || TxType.EMERGENCY == _txType) {
user.amount -= _amount;
totalZeroStaked -= _amount;
zeroToken.safeTransfer(_to, _amount);
}
user.rewardDebt = (user.amount * accRewardPerZero) / ONE;
emit StakeOrUnstakeOrClaim(_to, _amount, pendingRewards, _txType);
}
function _resetDebtIfNewRewardToken(address _to) private {
if (userInfo[_to].lastUpdateRewardToken < lastUpdateRewardToken) {
userInfo[_to].rewardDebt = 0;
userInfo[_to].lastUpdateRewardToken = lastUpdateRewardToken;
}
}
function _updateRewardPerZeroAndLastBlock() private {
if (totalZeroStaked == 0) {
lastUpdateBlock = block.number;
return;
}
accRewardPerZero += ((_lastRewardBlock() - lastUpdateBlock) * (currentRewardPerBlock * ONE)) / totalZeroStaked;
if (block.number != lastUpdateBlock) {
lastUpdateBlock = _lastRewardBlock();
}
}
function _calculatePendingRewards(address _user) private view returns (uint256) {
return ((userInfo[_user].amount * accRewardPerZero) / ONE) - userInfo[_user].rewardDebt;
}
function _lastRewardBlock() private view returns (uint256) {
return block.number < periodEndBlock ? block.number : periodEndBlock;
}
function _computeScalingFactor(IERC20Metadata _token) private view returns (uint256) {
uint256 tokenDecimals = _token.decimals();
uint256 decimalsDifference = 18 - tokenDecimals;
return 10 ** decimalsDifference;
}
function _downscale(uint256 _amount) private view returns (uint256) {
return _amount / _computeScalingFactor(rewardToken);
}
}
{
"compilationTarget": {
"src/ZeroLiquidStaking.sol": "ZeroLiquidStaking"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"appendCBOR": false,
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 10000
},
"remappings": [
":@prb/test/=lib/prb-test/src/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/openzeppelin-contracts/contracts/",
":prb-test/=lib/prb-test/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_governance","type":"address"},{"internalType":"address","name":"_rewardToken","type":"address"},{"internalType":"address","name":"_zeroToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AmountLessThanStakedAmountOrZero","type":"error"},{"inputs":[],"name":"CallerNotGovernance","type":"error"},{"inputs":[],"name":"EtherNotAccepted","type":"error"},{"inputs":[],"name":"InputLengthMismatch","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InsufficientZeroFunds","type":"error"},{"inputs":[],"name":"NoPendingRewardsToClaim","type":"error"},{"inputs":[],"name":"NoStakeFound","type":"error"},{"inputs":[],"name":"RewardDistributionPeriodHasExpired","type":"error"},{"inputs":[],"name":"RewardPerBlockIsNotSet","type":"error"},{"inputs":[],"name":"SameRewardToken","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAddressInput","type":"error"},{"inputs":[],"name":"ZeroInput","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_newVersion","type":"address"},{"indexed":false,"internalType":"contract IERC20Metadata[]","name":"_tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"FundsMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldGovernance","type":"address"},{"indexed":true,"internalType":"address","name":"newGovernance","type":"address"}],"name":"GovernanceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"numberBlocksToDistributeRewards","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newRewardPerBlock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardToDistribute","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardExpirationBlock","type":"uint256"}],"name":"NewRewardPeriod","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"numberBlocksToDistributeRewards","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardExpirationBlock","type":"uint256"}],"name":"PeriodEndBlockUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRewardToken","type":"address"},{"indexed":true,"internalType":"address","name":"newRewardToken","type":"address"}],"name":"RewardTokenChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pendingReward","type":"uint256"},{"indexed":false,"internalType":"enum ZeroLiquidStaking.TxType","name":"txType","type":"uint8"}],"name":"StakeOrUnstakeOrClaim","type":"event"},{"inputs":[],"name":"ONE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accRewardPerZero","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"calculatePendingRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentRewardPerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyUnstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRewardBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateRewardToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newVersion","type":"address"},{"internalType":"contract IERC20Metadata[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"bool","name":"_isZeroMigrate","type":"bool"}],"name":"migrateFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"periodEndBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"contract IERC20Metadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newGovernance","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalZeroStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_expireDurationInBlocks","type":"uint256"}],"name":"updateRewardEndBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newRewardToken","type":"address"}],"name":"updateRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_reward","type":"uint256"},{"internalType":"uint256","name":"_rewardDurationInBlocks","type":"uint256"}],"name":"updateRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"lastUpdateRewardToken","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"zeroToken","outputs":[{"internalType":"contract IERC20Metadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]