编译器
0.8.19+commit.7dd6d404
文件 1 的 12: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 的 12: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;
}
}
文件 3 的 12: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);
}
文件 4 的 12: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 的 12:IRSRV.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IRSRV is IERC20 {
function uniswapV2Router() external view returns (address);
function uniswapV2Pair() external view returns (address);
function mint(uint amount) external;
}
文件 6 的 12:IReserveBrokerage.sol
pragma solidity ^0.8.0;
interface IReserveBrokerage {
function burnAndVest(uint amount, uint period) external;
}
文件 7 的 12:IReserveOracle.sol
pragma solidity ^0.8.0;
interface IReserveOracle {
function activeMultiplier() external view returns (uint);
function getCurrentPrice() external view returns (uint);
function getCurrentMarketCap() external view returns (uint);
function getCirculatingSupply() external view returns (uint);
function getPercentageFromAth() external view returns (uint percentage);
function setCurrentMultiplier() external returns (uint);
}
文件 8 的 12:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 9 的 12:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
文件 10 的 12:Reserve.sol
pragma solidity =0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./interfaces/IRSRV.sol";
import "./interfaces/IReserveBrokerage.sol";
import "./interfaces/IReserveOracle.sol";
contract Reserve is Ownable, ReentrancyGuard {
using SafeMath for uint;
using SafeERC20 for IERC20;
struct UserBankInfo {
uint count;
uint pendingRewards;
uint rewardDebt;
}
struct BankInfo {
uint id;
uint cost;
uint bonusMultiplier;
uint reserveAmount;
uint lastRewardTimestamp;
uint accRsrvPerShare;
uint nextUpgradeId;
bool isReset;
uint aprOnReset;
}
IRSRV public immutable rsrv;
address public constant deadAddress = address(0xdead);
IReserveBrokerage public brokerage;
IReserveOracle public oracle;
uint public baseApr = 10000;
uint private constant _DENOMINATOR = 10000;
uint private slippage = 50;
BankInfo[] public bankInfo;
mapping (uint => mapping (address => UserBankInfo)) public userBankInfo;
mapping (address => uint) public userReserve;
mapping (address => uint) public withdrawableRewards;
mapping (uint => uint) public totalBankCountPerType;
uint public totalBankCount;
uint public startTimestamp;
mapping (address => bool) private _isWhitelisted;
event Purchased(
address indexed account,
uint indexed id,
uint count
);
event Upgraded(
address indexed account,
uint indexed id,
uint indexed upgradeId,
uint count
);
event Claimed(
address indexed account,
uint amount
);
constructor (
address _rsrv,
uint _startTimestamp
) {
rsrv = IRSRV(_rsrv);
startTimestamp = _startTimestamp;
bankInfo.push(BankInfo({
id: 0,
cost: uint(100).mul(1e18),
bonusMultiplier: 20000,
reserveAmount: 5,
lastRewardTimestamp: _startTimestamp,
accRsrvPerShare: 0,
nextUpgradeId: 1,
isReset: false,
aprOnReset: 800
}));
bankInfo.push(BankInfo({
id: 1,
cost: uint(500).mul(1e18),
bonusMultiplier: 40000,
reserveAmount: 50,
lastRewardTimestamp: _startTimestamp,
accRsrvPerShare: 0,
nextUpgradeId: 2,
isReset: false,
aprOnReset: 1200
}));
bankInfo.push(BankInfo({
id: 2,
cost: uint(2000).mul(1e18),
bonusMultiplier: 100000,
reserveAmount: 250,
lastRewardTimestamp: _startTimestamp,
accRsrvPerShare: 0,
nextUpgradeId: 0,
isReset: false,
aprOnReset: 2000
}));
_isWhitelisted[_msgSender()] = true;
}
function updateBaseApr(uint _baseApr) external onlyOwner {
require (_baseApr > 0 && _baseApr <= _DENOMINATOR, "Outside of bounds");
baseApr = _baseApr;
}
function setBrokerage(address _brokerage) external onlyOwner {
require (_brokerage != address(0), "Must be valid address");
brokerage = IReserveBrokerage(_brokerage);
}
function setSlippage(uint _slippage) external onlyOwner {
require (_slippage > 0 && _slippage <= _DENOMINATOR, "Outside of bounds");
slippage = _slippage;
}
function setOracle(address _oracle) external onlyOwner {
require (_oracle != address(0), "Must be valid address");
oracle = IReserveOracle(_oracle);
}
function add(uint _id, uint _cost) external onlyOwner {
require (_id == totalBankTypes(), "Invalid id");
massUpdate(true);
uint lastRewardTimestamp = block.timestamp > startTimestamp ? block.timestamp : startTimestamp;
bankInfo.push(BankInfo({
id: _id,
cost: _cost.mul(1e18),
bonusMultiplier: 10000,
reserveAmount: 0,
lastRewardTimestamp: lastRewardTimestamp,
accRsrvPerShare: 0,
nextUpgradeId: 0,
isReset: false,
aprOnReset: 0
}));
}
function set(uint _id, uint _cost, uint _multiplier, uint _reserveAmount, uint _nextUpgradeId, uint _aprOnReset) external onlyOwner {
require (_id < totalBankTypes(), "Invalid id");
massUpdate(true);
bankInfo[_id].cost = _cost.mul(1e18);
bankInfo[_id].bonusMultiplier = _multiplier;
bankInfo[_id].reserveAmount = _reserveAmount;
bankInfo[_id].nextUpgradeId = _nextUpgradeId;
bankInfo[_id].aprOnReset = _aprOnReset;
}
function getRewardRate(address _account) external view returns (uint rewardRate) {
for (uint id = 0; id < totalBankTypes(); id++) {
rewardRate = rewardRate.add(getRewardRate(_account, id));
}
}
function getRewardRate(address _account, uint _id) public view returns (uint rewardRate) {
BankInfo memory bank = bankInfo[_id];
UserBankInfo memory user = userBankInfo[_id][_account];
uint bankCount = totalBankCountPerType[_id];
if (bankCount != 0) {
uint multiplier = getMultiplier(block.timestamp, block.timestamp.add(1), bank.bonusMultiplier, bank.isReset, bank.aprOnReset);
uint rewardRatePerBank = multiplier.mul(bank.cost).div(1e18);
rewardRate = user.count.mul(rewardRatePerBank);
}
}
function getRewards(address _account) external view returns (uint rewards) {
rewards = withdrawableRewards[_account].add(getPendingRewards(_account));
}
function getPendingRewards(address _account) public view returns (uint rewards) {
for (uint id = 0; id < totalBankTypes(); id++) {
rewards = rewards.add(getPendingRewards(_account, id));
}
}
function getPendingRewards(address _account, uint _id) public view returns (uint rewards) {
BankInfo memory bank = bankInfo[_id];
UserBankInfo memory user = userBankInfo[_id][_account];
uint accRsrvPerShare = bank.accRsrvPerShare;
uint bankCount = totalBankCountPerType[_id];
if (block.timestamp > bank.lastRewardTimestamp && bankCount != 0) {
uint multiplier = getMultiplier(bank.lastRewardTimestamp, block.timestamp, bank.bonusMultiplier, bank.isReset, bank.aprOnReset);
uint reward = multiplier.mul(bank.cost).mul(bankCount);
accRsrvPerShare = accRsrvPerShare.add(reward.div(bankCount));
}
rewards = user.pendingRewards.add(user.count.mul(accRsrvPerShare).div(1e18).sub(user.rewardDebt));
}
function getBankCount(address _account) external view returns (uint[] memory count) {
count = new uint[](bankInfo.length);
for (uint id = 0; id < totalBankTypes(); id++) {
count[id] = getBankCountForType(_account, id);
}
}
function getBankCountForType(address _account, uint _id) public view returns (uint count) {
return userBankInfo[_id][_account].count;
}
function isAboveReserve(address _account) public view returns (bool passed, uint amountNedeed) {
uint currentPrice = oracle.getCurrentPrice();
uint amountRequired;
for (uint id = 0; id < totalBankTypes(); id++) {
amountRequired = amountRequired.add(getRequiredReserveForType(_account, id, currentPrice));
}
uint slippageAmount = amountRequired.mul(slippage).div(_DENOMINATOR);
uint minimumAmountRequired = amountRequired.sub(slippageAmount);
if (userReserve[_account] >= minimumAmountRequired) {
passed = true;
amountNedeed = 0;
} else {
passed = false;
amountNedeed = amountRequired.sub(userReserve[_account]);
}
}
function getCurrentReserveInUsd(address _account) external view returns (uint reserveInUsd) {
return userReserve[_account].mul(oracle.getCurrentPrice()).div(1e18);
}
function getRequiredReserveForType(address _account, uint _id, uint _currentPrice) public view returns (uint amountRequired) {
uint requiredReserveInUsd = getRequiredReserveInUsdForType(_account, _id).mul(1e18);
amountRequired = requiredReserveInUsd.mul(1e18).div(_currentPrice);
}
function getRequiredReserveInUsd(address _account) public view returns (uint reserveAmount) {
for (uint id = 0; id < totalBankTypes(); id++) {
reserveAmount = reserveAmount.add(getRequiredReserveInUsdForType(_account, id));
}
}
function getRequiredReserveInUsdForType(address _account, uint _id) public view returns (uint reserveAmount) {
uint count = getBankCountForType(_account, _id);
reserveAmount = bankInfo[_id].reserveAmount.mul(count);
}
function totalBankTypes() public view returns (uint) {
return bankInfo.length;
}
function getBankApr(uint _id) external view returns (uint) {
BankInfo memory bank = bankInfo[_id];
return getMultiplier(block.timestamp, block.timestamp.add(1), bank.bonusMultiplier, bank.isReset, bank.aprOnReset).mul(100).mul(365 days);
}
function getMultiplier(uint _from, uint _to, uint _bonusMultiplier, bool _isReset, uint _aprOnReset) internal view returns (uint) {
if (_to < _from) return 0;
uint base;
if (_isReset) {
base = _aprOnReset.mul(1e18).div(_DENOMINATOR).div(365 days);
return _to.sub(_from).mul(base);
} else {
base = baseApr.mul(1e18).div(_DENOMINATOR).div(365 days);
return _to.sub(_from).mul(base).mul(_bonusMultiplier).div(_DENOMINATOR);
}
}
function massUpdate(bool _updateApr) internal {
for (uint id = 0; id < totalBankTypes(); id++) {
updateBankType(id, false);
}
if (_updateApr) _setApr();
}
function updateBankType(uint _id, bool _updateApr) internal {
BankInfo storage bank = bankInfo[_id];
if (block.timestamp <= bank.lastRewardTimestamp) {
if (_updateApr) _setApr();
return;
}
uint bankCount = totalBankCountPerType[_id];
if (bankCount == 0) {
bank.lastRewardTimestamp = block.timestamp;
if (_updateApr) _setApr();
return;
}
uint multiplier = getMultiplier(bank.lastRewardTimestamp, block.timestamp, bank.bonusMultiplier, bank.isReset, bank.aprOnReset);
if (multiplier > 0) {
uint reward = multiplier.mul(bank.cost).mul(bankCount);
rsrv.mint(reward.div(1e18));
bank.accRsrvPerShare = bank.accRsrvPerShare.add(reward.div(bankCount));
}
bank.lastRewardTimestamp = block.timestamp;
if (_updateApr) _setApr();
}
function safeRsrvTransfer(address _recipient, uint _amount) internal {
uint balance = rsrv.balanceOf(address(this));
if (_amount > balance) {
_amount = balance;
}
SafeERC20.safeTransfer(rsrv, _recipient, _amount);
}
function _setApr() internal {
uint _baseApr = oracle.setCurrentMultiplier();
if (_baseApr == 1) {
for (uint id = 0; id < totalBankTypes(); id++) {
bankInfo[id].isReset = true;
}
baseApr = _DENOMINATOR;
} else {
baseApr = _baseApr;
}
}
function _purchase(address _account, uint _id, uint _count) internal returns (uint _cost) {
require (_id < totalBankTypes(), "Invalid id");
require (_count > 0, "Count must not be zero");
updateBankType(_id, true);
BankInfo memory bank = bankInfo[_id];
require(!bank.isReset, "Can not purchase disabled bank type");
UserBankInfo storage user = userBankInfo[_id][_account];
_cost = _count.mul(bank.cost);
if (user.count > 0) {
uint pendingRewards = user.count.mul(bank.accRsrvPerShare).div(1e18).sub(user.rewardDebt);
if (pendingRewards > 0) {
user.pendingRewards = user.pendingRewards.add(pendingRewards);
}
}
if (_count > 0) {
uint currentPrice = oracle.getCurrentPrice();
userReserve[_account] = userReserve[_account].add(_count.mul(bank.reserveAmount).mul(1e36).div(currentPrice));
user.count = user.count.add(_count);
totalBankCountPerType[_id] = totalBankCountPerType[_id].add(_count);
totalBankCount = totalBankCount.add(_count);
}
user.rewardDebt = user.count.mul(bank.accRsrvPerShare).div(1e18);
emit Purchased(_account, _id, _count);
}
function _upgrade(address _account, uint _id, uint _count) internal returns (uint _nextUpgradeId) {
require (_id < totalBankTypes(), "Invalid id");
require (_count > 0, "Count must not be zero");
updateBankType(_id, false);
BankInfo memory bank = bankInfo[_id];
require (bank.nextUpgradeId != 0, "Bank does not have an upgrade");
UserBankInfo storage user = userBankInfo[_id][_account];
require (user.count >= _count, "Count is invalid");
if (user.count > 0) {
uint pendingRewards = user.count.mul(bank.accRsrvPerShare).div(1e18).sub(user.rewardDebt);
if (pendingRewards > 0) {
user.pendingRewards = user.pendingRewards.add(pendingRewards);
}
}
_nextUpgradeId = bank.nextUpgradeId;
updateBankType(_nextUpgradeId, true);
BankInfo memory _upgradeBank = bankInfo[_nextUpgradeId];
UserBankInfo storage _upgradeUser = userBankInfo[_nextUpgradeId][_account];
if (_upgradeUser.count > 0) {
uint pendingRewards = _upgradeUser.count.mul(_upgradeBank.accRsrvPerShare).div(1e18).sub(_upgradeUser.rewardDebt);
if (pendingRewards > 0) {
_upgradeUser.pendingRewards = _upgradeUser.pendingRewards.add(pendingRewards);
}
}
user.count = user.count.sub(_count);
totalBankCountPerType[_id] = totalBankCountPerType[_id].sub(_count);
user.rewardDebt = user.count.mul(bank.accRsrvPerShare).div(1e18);
uint currentPrice = oracle.getCurrentPrice();
uint reserveToRemove = _count.mul(bank.reserveAmount).mul(1e36).div(currentPrice);
if (reserveToRemove > userReserve[_account]) {
userReserve[_account] = 0;
} else {
userReserve[_account] = userReserve[_account].sub(reserveToRemove);
}
userReserve[_account] = userReserve[_account].add(_count.mul(_upgradeBank.reserveAmount).mul(1e36).div(currentPrice));
_upgradeUser.count = _upgradeUser.count.add(_count);
totalBankCountPerType[_nextUpgradeId] = totalBankCountPerType[_nextUpgradeId].add(_count);
_upgradeUser.rewardDebt = _upgradeUser.count.mul(_upgradeBank.accRsrvPerShare).div(1e18);
emit Upgraded(_account, _id, _nextUpgradeId, _count);
}
function _processRewards(address _account, uint _id) internal {
require (_id < totalBankTypes(), "Invalid id");
BankInfo memory bank = bankInfo[_id];
UserBankInfo storage user = userBankInfo[_id][_account];
if (user.count > 0) {
uint pendingRewards = user.count.mul(bank.accRsrvPerShare).div(1e18).sub(user.rewardDebt);
if (pendingRewards > 0) {
user.pendingRewards = user.pendingRewards.add(pendingRewards);
}
}
user.rewardDebt = user.count.mul(bank.accRsrvPerShare).div(1e18);
}
function _claim(address _account) internal {
massUpdate(true);
uint totalRewards;
for (uint id = 0; id < totalBankTypes(); id++) {
_processRewards(_account, id);
totalRewards = totalRewards.add(userBankInfo[id][_account].pendingRewards);
userBankInfo[id][_account].pendingRewards = 0;
}
withdrawableRewards[_account] = withdrawableRewards[_account].add(totalRewards);
}
function purchaseBank(uint _id, uint _count) external nonReentrant {
uint cost = _purchase(_msgSender(), _id, _count);
SafeERC20.safeTransferFrom(rsrv, _msgSender(), deadAddress, cost);
}
function claimBank(address _account, uint _id, uint _count) external returns (uint cost) {
require (_msgSender() == address(brokerage) || _isWhitelisted[_msgSender()], "Only whitelisted accounts can call this function");
cost = _purchase(_account, _id, _count);
}
function upgradeBank(uint _id, uint _count) external nonReentrant {
uint nextUpgradeId = _upgrade(_msgSender(), _id, _count);
uint cost = _count.mul(bankInfo[nextUpgradeId].cost.sub(bankInfo[_id].cost));
SafeERC20.safeTransferFrom(rsrv, _msgSender(), deadAddress, cost);
}
function claimUpgrade(address _account, uint _id, uint _count) external returns (uint cost) {
require (_msgSender() == address(brokerage) || _isWhitelisted[_msgSender()], "Only whitelisted accounts can call this function");
uint nextUpgradeId = _upgrade(_account, _id, _count);
cost = _count.mul(bankInfo[nextUpgradeId].cost.sub(bankInfo[_id].cost));
}
function coverReserve(bool _fromRewards, bool _claimRest) external {
(bool passed, uint amountNeeded) = isAboveReserve(_msgSender());
_claim(_msgSender());
if (!passed) {
if (_fromRewards) {
require (withdrawableRewards[_msgSender()] > amountNeeded, "Not enough rewards to cover needed reserve");
withdrawableRewards[_msgSender()] = withdrawableRewards[_msgSender()].sub(amountNeeded);
if (_claimRest) {
uint amount = withdrawableRewards[_msgSender()];
safeRsrvTransfer(_msgSender(), amount);
withdrawableRewards[_msgSender()] = 0;
emit Claimed(_msgSender(), amount);
}
} else {
SafeERC20.safeTransferFrom(rsrv, _msgSender(), deadAddress, amountNeeded);
userReserve[_msgSender()] = userReserve[_msgSender()].add(amountNeeded);
if (_claimRest) {
uint amount = withdrawableRewards[_msgSender()];
safeRsrvTransfer(_msgSender(), amount);
withdrawableRewards[_msgSender()] = 0;
emit Claimed(_msgSender(), amount);
}
}
}
}
function claimRewards() external nonReentrant {
(bool passed, ) = isAboveReserve(_msgSender());
require (passed, "User does not have enough reserve to complete claim");
_claim(_msgSender());
uint amount = withdrawableRewards[_msgSender()];
safeRsrvTransfer(_msgSender(), amount);
withdrawableRewards[_msgSender()] = 0;
emit Claimed(_msgSender(), amount);
}
function compoundRewards(uint _id, uint _count, bool _claimRest) external nonReentrant {
(bool passed, ) = isAboveReserve(_msgSender());
require (passed, "User does not have enough reserve to complete compound");
_claim(_msgSender());
uint cost = _purchase(_msgSender(), _id, _count);
require (withdrawableRewards[_msgSender()] >= cost, "Reward amount is insufficient to cover bank cost");
uint left = withdrawableRewards[_msgSender()].sub(cost);
withdrawableRewards[_msgSender()] = left;
if (left > 0 && _claimRest) {
safeRsrvTransfer(_msgSender(), left);
withdrawableRewards[_msgSender()] = 0;
emit Claimed(_msgSender(), left);
}
}
function burnAndVestRewards(uint _amount, uint _period, bool _claimRest) external nonReentrant {
require (address(brokerage) != address(0), "Brokerage is not set");
(bool passed, ) = isAboveReserve(_msgSender());
require (passed, "User does not have enough reserve to complete burn and vest");
_claim(_msgSender());
require (withdrawableRewards[_msgSender()] >= _amount, "Amount is bigger than total rewards available");
brokerage.burnAndVest(_amount, _period);
SafeERC20.safeTransfer(rsrv, deadAddress, _amount);
uint left = withdrawableRewards[_msgSender()].sub(_amount);
withdrawableRewards[_msgSender()] = left;
if (left > 0 && _claimRest) {
safeRsrvTransfer(_msgSender(), left);
withdrawableRewards[_msgSender()] = 0;
emit Claimed(_msgSender(), left);
}
}
}
文件 11 的 12: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));
}
}
文件 12 的 12:SafeMath.sol
pragma solidity ^0.8.0;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}
{
"compilationTarget": {
"contracts/Reserve.sol": "Reserve"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_rsrv","type":"address"},{"internalType":"uint256","name":"_startTimestamp","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claimed","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":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"}],"name":"Purchased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"upgradeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"}],"name":"Upgraded","type":"event"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_cost","type":"uint256"}],"name":"add","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"bankInfo","outputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"cost","type":"uint256"},{"internalType":"uint256","name":"bonusMultiplier","type":"uint256"},{"internalType":"uint256","name":"reserveAmount","type":"uint256"},{"internalType":"uint256","name":"lastRewardTimestamp","type":"uint256"},{"internalType":"uint256","name":"accRsrvPerShare","type":"uint256"},{"internalType":"uint256","name":"nextUpgradeId","type":"uint256"},{"internalType":"bool","name":"isReset","type":"bool"},{"internalType":"uint256","name":"aprOnReset","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseApr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"brokerage","outputs":[{"internalType":"contract IReserveBrokerage","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_period","type":"uint256"},{"internalType":"bool","name":"_claimRest","type":"bool"}],"name":"burnAndVestRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_count","type":"uint256"}],"name":"claimBank","outputs":[{"internalType":"uint256","name":"cost","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_count","type":"uint256"}],"name":"claimUpgrade","outputs":[{"internalType":"uint256","name":"cost","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_count","type":"uint256"},{"internalType":"bool","name":"_claimRest","type":"bool"}],"name":"compoundRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_fromRewards","type":"bool"},{"internalType":"bool","name":"_claimRest","type":"bool"}],"name":"coverReserve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deadAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getBankApr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getBankCount","outputs":[{"internalType":"uint256[]","name":"count","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getBankCountForType","outputs":[{"internalType":"uint256","name":"count","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getCurrentReserveInUsd","outputs":[{"internalType":"uint256","name":"reserveInUsd","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getPendingRewards","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getPendingRewards","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_currentPrice","type":"uint256"}],"name":"getRequiredReserveForType","outputs":[{"internalType":"uint256","name":"amountRequired","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getRequiredReserveInUsd","outputs":[{"internalType":"uint256","name":"reserveAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getRequiredReserveInUsdForType","outputs":[{"internalType":"uint256","name":"reserveAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"getRewardRate","outputs":[{"internalType":"uint256","name":"rewardRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getRewardRate","outputs":[{"internalType":"uint256","name":"rewardRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getRewards","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isAboveReserve","outputs":[{"internalType":"bool","name":"passed","type":"bool"},{"internalType":"uint256","name":"amountNedeed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IReserveOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_count","type":"uint256"}],"name":"purchaseBank","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rsrv","outputs":[{"internalType":"contract IRSRV","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_cost","type":"uint256"},{"internalType":"uint256","name":"_multiplier","type":"uint256"},{"internalType":"uint256","name":"_reserveAmount","type":"uint256"},{"internalType":"uint256","name":"_nextUpgradeId","type":"uint256"},{"internalType":"uint256","name":"_aprOnReset","type":"uint256"}],"name":"set","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_brokerage","type":"address"}],"name":"setBrokerage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oracle","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_slippage","type":"uint256"}],"name":"setSlippage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBankCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalBankCountPerType","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBankTypes","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":[{"internalType":"uint256","name":"_baseApr","type":"uint256"}],"name":"updateBaseApr","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_count","type":"uint256"}],"name":"upgradeBank","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"userBankInfo","outputs":[{"internalType":"uint256","name":"count","type":"uint256"},{"internalType":"uint256","name":"pendingRewards","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawableRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]