File 1 of 1: veRise.sol
pragma solidity 0.8.13;
error NotZeroAddress();
error CallerNotApproved();
error InvalidAddress();
error CallerNotOwner();
error WalletLocked();
error DoesNotExist();
error AmountMustBeGreaterThanZero();
error AmountOutOfRange();
error StakeStillLocked();
error NotStakerAddress();
error AmountLargerThanAvailable();
error StakeCanOnlyBeExtended();
error ArrayLengthsMismatch();
error NotSetup();
error NotAllowedToCreateRewards();
error NotAllowedToDeliverRewards();
error ERC721ReceiverReject();
error ERC721ReceiverNotImplemented();
error NotEnoughToCoverStakeFee();
error AmountLargerThanAllowance();
error AchievementNotClaimed();
error AchievementAlreadyClaimed();
error AmountMustBeAnInteger();
error BrokenStatusesDiffer();
error AchievementClaimStatusesDiffer();
error UnlockedStakesMustBeSametimePeriod();
error CannotMergeLockedAndUnlockedStakes();
error StakeUnlocked();
error NoRewardsToClaim();
error NotTransferrable();
error Overflow();
error MergeNotEnabled();
interface IOwnable {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function owner() external view returns (address);
function transferOwnership(address newOwner) external;
}
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return payable(msg.sender);
}
}
contract Ownable is IOwnable, Context {
address public owner;
function _onlyOwner() private view {
if (owner != _msgSender()) revert CallerNotOwner();
}
modifier onlyOwner() {
_onlyOwner();
_;
}
constructor() {
address msgSender = _msgSender();
owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function transferOwnership(address newOwner) external virtual onlyOwner {
if (newOwner == address(0)) revert NotZeroAddress();
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
struct ApprovalChecks {
uint64 nonce;
uint32 nftCheck;
uint32 tokenCheck;
uint16 autoRevokeNftHours;
uint16 autoRevokeTokenHours;
uint48 unlockTimestamp;
}
struct Allowance {
uint128 tokenAmount;
uint32 nftCheck;
uint32 tokenCheck;
uint48 timestamp;
uint8 nftApproval;
uint8 tokenApproval;
}
interface IEverRiseWallet {
event RevokeAllApprovals(address indexed account, bool tokens, bool nfts);
event SetApprovalAutoTimeout(address indexed account, uint16 tokensHrs, uint16 nftsHrs);
event LockWallet(address indexed account, address altAccount, uint256 length);
event LockWalletExtend(address indexed account, uint256 length);
}
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 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);
function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
function transferFromWithPermit(address sender, address recipient, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external returns (bool);
}
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
interface IEverRise is IERC20Metadata {
function totalBuyVolume() external view returns (uint256);
function totalSellVolume() external view returns (uint256);
function holders() external view returns (uint256);
function uniswapV2Pair() external view returns (address);
function transferStake(address fromAddress, address toAddress, uint96 amountToTransfer) external;
function isWalletLocked(address fromAddress) external view returns (bool);
function setApprovalForAll(address fromAddress, address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
function isExcludedFromFee(address account) external view returns (bool);
function approvals(address operator) external view returns (ApprovalChecks memory);
}
interface IERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function approve(address _approved, uint256 _tokenId) external payable;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
struct StakingDetails {
uint96 initialTokenAmount;
uint96 withdrawnAmount;
uint48 depositTime;
uint8 numOfMonths;
uint8 achievementClaimed;
address stakerAddress;
uint32 nftId;
uint32 lookupIndex;
uint24 stakerIndex;
uint8 isActive;
}
interface InftEverRise is IERC721 {
function voteEscrowedBalance(address account) external view returns (uint256);
function unclaimedRewardsBalance(address account) external view returns (uint256);
function totalAmountEscrowed() external view returns (uint256);
function totalAmountVoteEscrowed() external view returns (uint256);
function totalRewardsDistributed() external view returns (uint256);
function totalRewardsUnclaimed() external view returns (uint256);
function createRewards(uint256 tAmount) external;
function getNftData(uint256 id) external view returns (StakingDetails memory);
function enterStaking(address fromAddress, uint96 amount, uint8 numOfMonths) external returns (uint32 nftId);
function leaveStaking(address fromAddress, uint256 id, bool overrideNotClaimed) external returns (uint96 amount);
function earlyWithdraw(address fromAddress, uint256 id, uint96 amount) external returns (uint32 newNftId, uint96 penaltyAmount);
function withdraw(address fromAddress, uint256 id, uint96 amount, bool overrideNotClaimed) external returns (uint32 newNftId);
function bridgeStakeNftOut(address fromAddress, uint256 id) external returns (uint96 amount);
function bridgeOrAirdropStakeNftIn(address toAddress, uint96 depositAmount, uint8 numOfMonths, uint48 depositTime, uint96 withdrawnAmount, uint96 rewards, bool achievementClaimed) external returns (uint32 nftId);
function addStaker(address staker, uint256 nftId) external;
function removeStaker(address staker, uint256 nftId) external;
function reissueStakeNft(address staker, uint256 oldNftId, uint256 newNftId) external;
function increaseStake(address staker, uint256 nftId, uint96 amount) external returns (uint32 newNftId, uint96 original, uint8 numOfMonths);
function splitStake(uint256 id, uint96 amount) external payable returns (uint32 newNftId0, uint32 newNftId1);
function claimAchievement(address staker, uint256 nftId) external returns (uint32 newNftId);
function stakeCreateCost() external view returns (uint256);
function approve(address owner, address _operator, uint256 nftId) external;
}
abstract contract virtualToken is Ownable, IERC20, IERC20Metadata {
InftEverRise public veEverRise;
uint8 public constant decimals = 18;
string public name;
string public symbol;
constructor(string memory _name, string memory _symbol ) {
name = _name;
symbol = _symbol;
veEverRise = InftEverRise(owner);
}
function transferFrom(address sender, address recipient, uint256 amount)
external returns (bool) {
if (_msgSender() != owner) {
notTransferrable();
}
emit Transfer(sender, recipient, amount);
return true;
}
function transfer(address, uint256) pure external returns (bool) {
notTransferrable();
}
function allowance(address, address) pure external returns (uint256) {
return 0;
}
function approve(address, uint256) pure external returns (bool) {
notTransferrable();
}
function increaseAllowance(address, uint256) pure external returns (bool) {
notTransferrable();
}
function decreaseAllowance(address, uint256) pure external returns (bool) {
notTransferrable();
}
function transferFromWithPermit(address, address, uint256, uint256, uint8, bytes32, bytes32) pure external returns (bool) {
notTransferrable();
}
function notTransferrable() pure private {
revert NotTransferrable();
}
}
contract claimRise is virtualToken("EverRise Rewards", "claimRISE") {
function totalSupply() override external view returns (uint256) {
return veEverRise.totalRewardsUnclaimed();
}
function balanceOf(address account) override external view returns (uint256) {
if (account == owner) return 0;
return veEverRise.unclaimedRewardsBalance(account);
}
}
contract veRise is virtualToken("Vote-escrowed EverRise", "veRISE") {
function totalSupply() override external view returns (uint256) {
return veEverRise.totalAmountVoteEscrowed();
}
function balanceOf(address account) override external view returns (uint256) {
if (account == owner) return 0;
return veEverRise.voteEscrowedBalance(account);
}
}
interface IEverRoyaltySplitter {
event RoyaltiesSplit(uint256 value);
event SplitUpdated(uint256 previous, uint256 current);
event UniswapV2RouterSet(address indexed previous, address indexed current);
event EverRiseEcosystemSet(address indexed previous, address indexed current);
event EverRiseTokenSet(address indexed previous, address indexed current);
event StableCoinSet(address indexed previous, address indexed current);
function distribute() external;
}
interface IERC721TokenReceiver {
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external view returns(bytes4);
}
interface IERC721Metadata {
function name() external view returns (string memory _name);
function symbol() external view returns (string memory _symbol);
function tokenURI(uint256 _tokenId) external view returns (string memory);
}
interface IERC721Enumerable {
function totalSupply() external view returns (uint256);
function tokenByIndex(uint256 _index) external view returns (uint256);
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}
interface IOpenSeaCollectible {
function contractURI() external view returns (string memory);
}
interface IEverRiseRenderer is IOpenSeaCollectible {
event SetEverRiseNftStakes(address indexed addressStakes);
event SetEverRiseRendererGlyph(address indexed addressGlyphs);
function tokenURI(uint256 _tokenId) external view returns (string memory);
function everRiseNftStakes() external view returns (InftEverRise);
function setEverRiseNftStakes(address contractAddress) external;
}
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
interface IERC2981 is IERC165 {
function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (
address receiver,
uint256 royaltyAmount
);
}
contract EverRiseTokenOwned is Ownable {
IEverRise public everRiseToken;
event EverRiseTokenSet(address indexed tokenAddress);
function _onlyEverRiseToken(address senderAddress) private view {
if (address(everRiseToken) == address(0)) revert NotSetup();
if (address(everRiseToken) != senderAddress) revert CallerNotApproved();
}
modifier onlyEverRiseToken() {
_onlyEverRiseToken(_msgSender());
_;
}
}
struct IndividualAllowance {
address operator;
uint48 timestamp;
uint32 nftCheck;
}
abstract contract nftEverRiseConfigurable is EverRiseTokenOwned, InftEverRise, ERC165, IERC2981, IERC721Metadata, IOpenSeaCollectible {
event AddRewardCreator(address indexed _address);
event RemoveRewardCreator(address indexed _address);
event SetAchievementNfts(address indexed _address);
event RoyaltyFeeUpdated(uint256 newValue);
event RoyaltyAddressUpdated(address indexed contractAddress);
event RendererAddressUpdated(address indexed contractAddress);
event StakeCreateCostUpdated(uint256 newValue);
event StakingParametersSet(uint256 withdrawPct, uint256 firstHalfPenality, uint256 secondHalfPenality, uint256 maxStakeMonths, bool mergeEnabled);
IEverRiseRenderer public renderer;
IEverRoyaltySplitter public royaltySplitter;
uint256 public nftRoyaltySplit = 10;
address public currentAchievementNfts;
uint8 public maxEarlyWithdrawalPercent = 60;
uint8 public firstHalfPenality = 25;
uint8 public secondHalfPenality = 10;
uint8 public maxStakeMonths = 36;
uint256 public stakeCreateCost = 1 * 10**18 / (10**2);
uint256 public mergeEnabled = _FALSE;
uint256 constant _FALSE = 1;
uint256 constant _TRUE = 2;
mapping (address => bool) internal _canCreateRewards;
function setEverRiseToken(address tokenAddress) external onlyOwner {
if (tokenAddress == address(0)) revert NotZeroAddress();
_removeAddressToCreate(address(everRiseToken));
addAddressToCreate(tokenAddress);
everRiseToken = IEverRise(tokenAddress);
emit EverRiseTokenSet(tokenAddress);
}
function setStakeCreateCost(uint256 _stakeCreateCost, uint256 numOfDecimals)
external onlyOwner
{
if (_stakeCreateCost > 1_000) revert AmountOutOfRange();
stakeCreateCost = _stakeCreateCost * (10**18) / (10**numOfDecimals);
emit StakeCreateCostUpdated(_stakeCreateCost);
}
function setAchievementNfts(address contractAddress) external onlyOwner() {
if (contractAddress == address(0)) revert NotZeroAddress();
currentAchievementNfts = contractAddress;
emit SetAchievementNfts(contractAddress);
}
function addAddressToCreate(address account) public onlyOwner {
if (account == address(0)) revert NotZeroAddress();
_canCreateRewards[account] = true;
emit AddRewardCreator(account);
}
function removeAddressToCreate(address account) external onlyOwner {
if (account == address(0)) revert NotZeroAddress();
_removeAddressToCreate(account);
}
function _removeAddressToCreate(address account) private {
if (account != address(0)){
_canCreateRewards[account] = false;
emit RemoveRewardCreator(account);
}
}
function setNftRoyaltyFeePercent(uint256 royaltySplitRate) external onlyOwner {
if (royaltySplitRate > 10) revert AmountOutOfRange();
nftRoyaltySplit = royaltySplitRate;
emit RoyaltyFeeUpdated(royaltySplitRate);
}
function setRoyaltyAddress(address newAddress) external onlyOwner {
if (newAddress == address(0)) revert NotZeroAddress();
royaltySplitter = IEverRoyaltySplitter(newAddress);
emit RoyaltyAddressUpdated(newAddress);
}
function setRendererAddress(address newAddress) external onlyOwner {
if (newAddress == address(0)) revert NotZeroAddress();
renderer = IEverRiseRenderer(newAddress);
emit RendererAddressUpdated(newAddress);
}
function setStakingParameters(uint8 _withdrawPercent, uint8 _firstHalfPenality, uint8 _secondHalfPenality, uint8 _maxStakeMonths, bool _mergEnabled)
external onlyOwner
{
if (_maxStakeMonths == 0 || _maxStakeMonths > 120) {
revert AmountOutOfRange();
}
maxEarlyWithdrawalPercent = _withdrawPercent;
firstHalfPenality = _firstHalfPenality;
secondHalfPenality = _secondHalfPenality;
maxStakeMonths = _maxStakeMonths;
mergeEnabled = _mergEnabled ? _TRUE : _FALSE;
emit StakingParametersSet(_withdrawPercent, _firstHalfPenality, _secondHalfPenality, _maxStakeMonths, _mergEnabled);
}
}
contract nftEverRise is nftEverRiseConfigurable {
string public constant name = "EverRise NFT Stakes";
string public constant symbol = "nftRISE";
uint256 public constant month = 30 days;
uint256 private constant MAX = ~uint256(0);
uint8 public constant decimals = 0;
uint256 private constant totalStakeTokensSupply = 120_000_000 * 10**6 * 10**18;
uint8 constant _FALSE8 = 1;
uint8 constant _TRUE8 = 2;
event RewardsWithdrawn(address indexed from, uint256 amount);
event ExcludedFromRewards(address indexed _address);
event IncludedToRewards(address indexed _address);
mapping (address => bool) private _isExcludedFromReward;
mapping (address => uint256) private _rOwned;
mapping (address => uint256) private _tOwned;
mapping (address => uint256) private _withdrawnRewards;
mapping (uint256 => IndividualAllowance) private _individualApproval;
address[] private _excludedList;
uint256 private _rTotal = (MAX - (MAX % totalStakeTokensSupply));
StakingDetails[] private _allStakeDetails;
mapping (address => uint256[]) private _individualStakes;
mapping (uint256 => uint256) private _stakeById;
uint256[] private _freeStakes;
mapping (address => uint256) public voteEscrowedBalance;
uint256 public totalAmountEscrowed;
uint256 public totalAmountVoteEscrowed;
uint256 public totalRewardsDistributed;
uint32 private nextNftId = 1;
veRise public immutable veRiseToken;
claimRise public immutable claimRiseToken;
constructor() {
veRiseToken = new veRise();
claimRiseToken = new claimRise();
_rOwned[address(this)] = _rTotal;
excludeFromReward(address(this));
_allStakeDetails.push(StakingDetails({
initialTokenAmount: 0,
withdrawnAmount: 0,
depositTime: 0,
numOfMonths: 1,
achievementClaimed: 0,
stakerAddress: address(0),
nftId: 0,
lookupIndex: 0,
stakerIndex: 0,
isActive: _FALSE8
}));
}
function _walletLock(address fromAddress) private view {
if (everRiseToken.isWalletLocked(fromAddress)) revert WalletLocked();
}
modifier walletLock(address fromAddress) {
_walletLock(fromAddress);
_;
}
function totalSupply() external view returns (uint256) {
return _allStakeDetails.length - _freeStakes.length - 1;
}
function _onlyRewardCreator(address senderAddress) private view {
if (!_canCreateRewards[senderAddress]) revert CallerNotApproved();
}
modifier onlyRewardCreator() {
_onlyRewardCreator(_msgSender());
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override (ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC2981).interfaceId ||
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
function contractURI() external view returns (string memory) {
return renderer.contractURI();
}
function tokenURI(uint256 nftId) external view returns (string memory) {
return renderer.tokenURI(nftId);
}
function createRewards(uint256 amount) external onlyRewardCreator() {
address sender = _msgSender();
if (_isExcludedFromReward[sender]) revert NotAllowedToDeliverRewards();
_transferFromExcluded(address(this), sender, amount);
totalRewardsDistributed += amount;
uint256 rAmount = amount * _getRate();
_rOwned[sender] -= rAmount;
_rTotal -= rAmount;
claimRiseToken.transferFrom(address(0), address(this), amount);
}
function voteEscrowedAndRewards(address account) private view returns (uint256) {
if (account == address(0)) revert NotZeroAddress();
if (_isExcludedFromReward[account]) return _tOwned[account];
return tokenFromRewards(_rOwned[account]);
}
function totalRewardsUnclaimed() external view returns (uint256) {
return everRiseToken.balanceOf(address(this));
}
function isExcludedFromReward(address account) external view returns (bool) {
if (account == address(0)) revert NotZeroAddress();
return _isExcludedFromReward[account];
}
function rewardsFromToken(uint256 tAmount) external view returns(uint256) {
if (tAmount > totalStakeTokensSupply) revert AmountOutOfRange();
return tAmount * _getRate();
}
function tokenFromRewards(uint256 rAmount) public view returns(uint256) {
if (rAmount > _rTotal) revert AmountOutOfRange();
uint256 currentRate = _getRate();
return rAmount / currentRate;
}
function excludeFromReward(address account) public onlyOwner() {
if (account == address(0)) revert NotZeroAddress();
if (_isExcludedFromReward[account]) revert InvalidAddress();
if(_rOwned[account] > 0) {
_tOwned[account] = tokenFromRewards(_rOwned[account]);
}
_isExcludedFromReward[account] = true;
_excludedList.push(account);
emit ExcludedFromRewards(account);
}
function includeInReward(address account) external onlyOwner() {
if (account == address(0)) revert NotZeroAddress();
if (!_isExcludedFromReward[account]) revert InvalidAddress();
uint256 length = _excludedList.length;
for (uint256 i = 0; i < length;) {
if (_excludedList[i] == account) {
_excludedList[i] = _excludedList[_excludedList.length - 1];
_tOwned[account] = 0;
_isExcludedFromReward[account] = false;
_excludedList.pop();
break;
}
unchecked {
++i;
}
}
emit IncludedToRewards(account);
}
function _transfer(
address sender,
address recipient,
uint256 amount,
bool emitEvent
) private {
if (sender == address(0)) revert NotZeroAddress();
if (recipient == address(0)) revert NotZeroAddress();
if (amount == 0) revert AmountMustBeGreaterThanZero();
if (sender != address(this) && recipient != address(this)) revert InvalidAddress();
if (_isExcludedFromReward[sender]) {
if (!_isExcludedFromReward[recipient]) {
_transferFromExcluded(sender, recipient, amount);
} else {
_transferBothExcluded(sender, recipient, amount);
}
} else if (_isExcludedFromReward[recipient]) {
_transferToExcluded(sender, recipient, amount);
} else {
_transferStandard(sender, recipient, amount);
}
if (emitEvent) {
if (sender == address(this)) {
veRiseToken.transferFrom(address(0), recipient, amount);
}
else if (recipient == address(this)) {
veRiseToken.transferFrom(sender, address(0), amount);
}
}
}
function _transferStandard(address sender, address recipient, uint256 tAmount) private {
uint256 rAmount = tAmount * _getRate();
_rOwned[sender] -= rAmount;
_rOwned[recipient] += rAmount;
}
function _transferToExcluded(address sender, address recipient, uint256 tAmount) private {
uint256 rAmount = tAmount * _getRate();
_rOwned[sender] -= rAmount;
_tOwned[recipient] += tAmount;
_rOwned[recipient] += rAmount;
}
function _transferFromExcluded(address sender, address recipient, uint256 tAmount) private {
uint256 rAmount = tAmount * _getRate();
_tOwned[sender] -= tAmount;
_rOwned[sender] -= rAmount;
_rOwned[recipient] += rAmount;
}
function _transferBothExcluded(address sender, address recipient, uint256 tAmount) private {
uint256 rAmount = tAmount * _getRate();
_tOwned[sender] -= tAmount;
_rOwned[sender] -= rAmount;
_tOwned[recipient] += tAmount;
_rOwned[recipient] += rAmount;
}
function _getRate() private view returns(uint256) {
(uint256 rSupply, uint256 tSupply) = _getCurrentSupply();
return rSupply / tSupply;
}
function _getCurrentSupply() private view returns(uint256, uint256) {
uint256 rSupply = _rTotal;
uint256 tSupply = totalStakeTokensSupply;
uint256 length = _excludedList.length;
for (uint256 i = 0; i < length;) {
if (_rOwned[_excludedList[i]] > rSupply || _tOwned[_excludedList[i]] > tSupply) {
return (_rTotal, totalStakeTokensSupply);
}
rSupply -= _rOwned[_excludedList[i]];
tSupply -= _tOwned[_excludedList[i]];
unchecked {
++i;
}
}
if (rSupply < (_rTotal / totalStakeTokensSupply)) return (_rTotal, totalStakeTokensSupply);
return (rSupply, tSupply);
}
function getStakeIndex(uint256 nftId) private view returns (uint256 lookupIndex) {
if (nftId == 0) revert DoesNotExist();
lookupIndex = _stakeById[nftId];
if (lookupIndex >= _allStakeDetails.length) revert DoesNotExist();
if (_allStakeDetails[lookupIndex].isActive != _TRUE8) revert DoesNotExist();
}
function ownerOf(uint256 nftId) public view returns (address) {
uint256 lookupIndex = getStakeIndex(nftId);
StakingDetails storage stakeDetails = _allStakeDetails[lookupIndex];
return stakeDetails.stakerAddress;
}
function _getStake(uint256 nftId, address staker) private view returns (uint256 lookupIndex, StakingDetails storage stakeDetails) {
lookupIndex = getStakeIndex(nftId);
stakeDetails = _allStakeDetails[lookupIndex];
if (stakeDetails.stakerAddress != staker) revert NotStakerAddress();
assert(nftId == stakeDetails.nftId);
}
function getStake(uint256 nftId, address staker) external view returns (StakingDetails memory stakeDetails) {
(, stakeDetails) = _getStake(nftId, staker);
}
function getNftData(uint256 nftId) external view returns (StakingDetails memory) {
uint256 lookupIndex = getStakeIndex(nftId);
return _allStakeDetails[lookupIndex];
}
function balanceOf(address account) external view returns (uint256) {
return _individualStakes[account].length;
}
function unclaimedRewardsBalance(address account) public view returns (uint256) {
return voteEscrowedAndRewards(account) - voteEscrowedBalance[account];
}
function getTotalRewards(address account) external view returns (uint256) {
return unclaimedRewardsBalance(account) + _withdrawnRewards[account];
}
function tokenOfOwnerByIndex(address _owner, uint256 index) external view returns (uint32) {
uint256[] storage stakes = _individualStakes[_owner];
if (index > stakes.length) revert AmountOutOfRange();
uint256 lookupIndex = stakes[index];
return _allStakeDetails[lookupIndex].nftId;
}
function enterStaking(address staker, uint96 amount, uint8 numOfMonths)
external onlyEverRiseToken returns (uint32 nftId) {
if (numOfMonths == 0 ||
numOfMonths > maxStakeMonths ||
(numOfMonths > 12 && (numOfMonths % 12) > 0)
) {
revert AmountOutOfRange();
}
roundingCheck(amount, false);
nftId = _createStake(staker, amount, 0, numOfMonths, uint48(block.timestamp), false);
}
function withdrawRewards() external {
address staker = _msgSender();
uint256 rewards = unclaimedRewardsBalance(staker);
if (rewards == 0) revert NoRewardsToClaim();
_withdrawnRewards[staker] += rewards;
_transfer(staker, address(this), rewards, false);
claimRiseToken.transferFrom(staker, address(0), rewards);
require(everRiseToken.transfer(staker, rewards));
emit RewardsWithdrawn(staker, rewards);
}
function checkNotLocked(uint256 depositTime, uint256 numOfMonths) private view {
if (depositTime + (numOfMonths * month) > block.timestamp) {
revert StakeStillLocked();
}
}
function leaveStaking(address staker, uint256 nftId, bool overrideNotClaimed) external onlyEverRiseToken returns (uint96 amount) {
(uint256 lookupIndex, StakingDetails storage stakeDetails) = _getStake(nftId, staker);
checkNotLocked(stakeDetails.depositTime, stakeDetails.numOfMonths);
if (!overrideNotClaimed && stakeDetails.achievementClaimed != _TRUE8) {
revert AchievementNotClaimed();
}
amount = _removeStake(staker, nftId, lookupIndex, stakeDetails);
}
function withdraw(address staker, uint256 nftId, uint96 amount, bool overrideNotClaimed) external onlyEverRiseToken returns (uint32 newNftId) {
(uint256 lookupIndex, StakingDetails storage stakeDetails) = _getStake(nftId, staker);
checkNotLocked(stakeDetails.depositTime, stakeDetails.numOfMonths);
if (!overrideNotClaimed && stakeDetails.achievementClaimed != _TRUE8) {
revert AchievementNotClaimed();
}
uint96 remaining = stakeDetails.initialTokenAmount - stakeDetails.withdrawnAmount;
if (amount > remaining) revert AmountLargerThanAvailable();
roundingCheck(amount, true);
bool reissueNft = false;
if (amount > 0) {
decreaseVeAmount(staker, amount, stakeDetails.numOfMonths, true);
remaining -= amount;
reissueNft = true;
}
if (stakeDetails.initialTokenAmount != remaining) {
stakeDetails.initialTokenAmount = remaining;
stakeDetails.withdrawnAmount = 0;
reissueNft = true;
}
if (reissueNft) {
if (remaining == 0) {
_burnStake(staker, nftId, lookupIndex, stakeDetails);
newNftId = 0;
} else {
newNftId = _reissueStakeNftId(nftId, lookupIndex);
stakeDetails.nftId = newNftId;
}
} else {
newNftId = uint32(nftId);
}
}
function claimAchievement(address staker, uint256 nftId) external returns (uint32 newNftId) {
if (_msgSender() != currentAchievementNfts) revert CallerNotApproved();
(uint256 lookupIndex, StakingDetails storage stakeDetails) = _getStake(nftId, staker);
checkNotLocked(stakeDetails.depositTime, stakeDetails.numOfMonths);
if (stakeDetails.achievementClaimed == _TRUE8) {
revert AchievementAlreadyClaimed();
}
if (stakeDetails.withdrawnAmount > 0) {
stakeDetails.initialTokenAmount -= stakeDetails.withdrawnAmount;
stakeDetails.withdrawnAmount = 0;
}
stakeDetails.achievementClaimed = _TRUE8;
newNftId = _reissueStakeNftId(nftId, lookupIndex);
stakeDetails.nftId = newNftId;
_reissueStakeNft(staker, nftId, newNftId);
}
function getTime() external view returns (uint256) {
return block.timestamp;
}
function checkLocked(uint48 depositTime, uint8 numOfMonths) private view {
if (depositTime + (numOfMonths * month) < block.timestamp) {
revert StakeUnlocked();
}
}
function earlyWithdraw(address staker, uint256 nftId, uint96 amount) external onlyEverRiseToken returns (uint32 newNftId, uint96 penaltyAmount) {
(uint256 lookupIndex, StakingDetails storage stakeDetails) = _getStake(nftId, staker);
checkLocked(stakeDetails.depositTime, stakeDetails.numOfMonths);
uint256 remaingEarlyWithdrawal = (stakeDetails.initialTokenAmount * maxEarlyWithdrawalPercent) / 100 - stakeDetails.withdrawnAmount;
if (amount > remaingEarlyWithdrawal) {
revert AmountLargerThanAvailable();
}
roundingCheck(amount, false);
decreaseVeAmount(staker, amount, stakeDetails.numOfMonths, true);
penaltyAmount = calculateTax(amount, stakeDetails.depositTime, stakeDetails.numOfMonths);
stakeDetails.withdrawnAmount += uint96(amount);
newNftId = _reissueStakeNftId(nftId, lookupIndex);
stakeDetails.nftId = newNftId;
}
function increaseStake(address staker, uint256 nftId, uint96 amount)
external onlyEverRiseToken returns (uint32 newNftId, uint96 original, uint8 numOfMonths)
{
(uint256 lookupIndex, StakingDetails storage stakeDetails) = _getStake(nftId, staker);
checkLocked(stakeDetails.depositTime, stakeDetails.numOfMonths);
roundingCheck(amount, false);
numOfMonths = stakeDetails.numOfMonths;
increaseVeAmount(staker, amount, numOfMonths, true);
original = stakeDetails.initialTokenAmount - stakeDetails.withdrawnAmount;
if (amount > stakeDetails.withdrawnAmount) {
amount -= stakeDetails.withdrawnAmount;
stakeDetails.withdrawnAmount = 0;
stakeDetails.initialTokenAmount += amount;
} else {
stakeDetails.withdrawnAmount -= amount;
}
stakeDetails.depositTime = uint48(block.timestamp);
newNftId = _reissueStakeNftId(nftId, lookupIndex);
stakeDetails.nftId = newNftId;
_reissueStakeNft(staker, nftId, newNftId);
}
function extendStake(uint256 nftId, uint8 numOfMonths) external walletLock(_msgSender()) returns (uint32 newNftId) {
address staker = _msgSender();
(uint256 lookupIndex, StakingDetails storage stakeDetails) = _getStake(nftId, staker);
checkLocked(stakeDetails.depositTime, stakeDetails.numOfMonths);
if (stakeDetails.numOfMonths >= numOfMonths) revert StakeCanOnlyBeExtended();
if (numOfMonths > maxStakeMonths ||
(numOfMonths > 12 && (numOfMonths % 12) > 0)
) {
revert AmountOutOfRange();
}
uint8 extraMonths = numOfMonths - stakeDetails.numOfMonths;
uint96 amount = (stakeDetails.initialTokenAmount - stakeDetails.withdrawnAmount);
increaseVeAmount(staker, amount, extraMonths, true);
stakeDetails.numOfMonths = numOfMonths;
stakeDetails.depositTime = uint48(block.timestamp);
newNftId = _reissueStakeNftId(nftId, lookupIndex);
stakeDetails.nftId = newNftId;
_reissueStakeNft(staker, nftId, newNftId);
}
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) revert Overflow();
return uint96(value);
}
function splitStake(uint256 nftId, uint96 amount) external payable walletLock(_msgSender()) returns (uint32 newNftId0, uint32 newNftId1) {
address staker = _msgSender();
if (msg.value < stakeCreateCost) revert NotEnoughToCoverStakeFee();
roundingCheck(amount, false);
payable(address(everRiseToken)).transfer(address(this).balance);
(uint256 lookupIndex, StakingDetails storage stakeDetails) = _getStake(nftId, staker);
uint256 remainingAmount = stakeDetails.initialTokenAmount - stakeDetails.withdrawnAmount;
if (amount >= remainingAmount) revert AmountLargerThanAvailable();
newNftId0 = _reissueStakeNftId(nftId, lookupIndex);
uint96 transferredWithdrawnAmount = toUint96(stakeDetails.withdrawnAmount * uint256(amount) / stakeDetails.initialTokenAmount);
stakeDetails.initialTokenAmount -= amount;
stakeDetails.withdrawnAmount -= transferredWithdrawnAmount;
stakeDetails.nftId = newNftId0;
newNftId1 = _addSplitStake(
staker,
amount,
transferredWithdrawnAmount,
stakeDetails.depositTime,
stakeDetails.numOfMonths,
stakeDetails.achievementClaimed == _TRUE8
);
_reissueStakeNft(staker, nftId, newNftId0);
emit Transfer(address(0), staker, newNftId1);
}
function roundingCheck(uint96 amount, bool allowZero) private pure {
uint96 roundedAmount = amount - (amount % uint96(10**18));
if (amount != roundedAmount || (!allowZero && amount == 0)) revert AmountMustBeAnInteger();
}
function mergeStakes(uint256 nftId0, uint256 nftId1, bool overrideStatuses)
external walletLock(_msgSender())
returns (uint32 newNftId)
{
if (mergeEnabled != _TRUE) revert MergeNotEnabled();
address staker = _msgSender();
(uint256 lookupIndex0, StakingDetails storage stakeDetails0) = _getStake(nftId0, staker);
(uint256 lookupIndex1, StakingDetails storage stakeDetails1) = _getStake(nftId1, staker);
bool unlocked0 = stakeDetails0.depositTime + (stakeDetails0.numOfMonths * month) < block.timestamp;
bool unlocked1 = stakeDetails1.depositTime + (stakeDetails1.numOfMonths * month) < block.timestamp;
if (unlocked0 == unlocked1) {
if (stakeDetails0.numOfMonths != stakeDetails1.numOfMonths) {
revert UnlockedStakesMustBeSametimePeriod();
}
if (!overrideStatuses && stakeDetails0.achievementClaimed != stakeDetails1.achievementClaimed) {
revert AchievementClaimStatusesDiffer();
}
if (stakeDetails0.withdrawnAmount > 0) {
stakeDetails0.initialTokenAmount -= stakeDetails0.withdrawnAmount;
stakeDetails0.withdrawnAmount = 0;
}
if (stakeDetails1.withdrawnAmount > 0) {
stakeDetails1.initialTokenAmount -= stakeDetails1.withdrawnAmount;
stakeDetails1.withdrawnAmount = 0;
}
} else if (unlocked0 != unlocked1) {
revert CannotMergeLockedAndUnlockedStakes();
} else {
if (!overrideStatuses && (stakeDetails0.withdrawnAmount > 0) != (stakeDetails1.withdrawnAmount > 0)) {
revert BrokenStatusesDiffer();
}
}
uint8 numOfMonths0 = stakeDetails0.numOfMonths;
if (!unlocked0) {
uint8 extraMonths = 0;
uint96 amount = 0;
uint8 numOfMonths1 = stakeDetails1.numOfMonths;
if (numOfMonths0 > numOfMonths1) {
extraMonths = numOfMonths0 - numOfMonths1;
amount = (stakeDetails1.initialTokenAmount - stakeDetails1.withdrawnAmount);
} else if (numOfMonths0 < numOfMonths1) {
extraMonths = numOfMonths1 - numOfMonths0;
amount = (stakeDetails0.initialTokenAmount - stakeDetails0.withdrawnAmount);
numOfMonths0 = numOfMonths1;
}
if (extraMonths > 0 && amount > 0) {
increaseVeAmount(staker, amount, extraMonths, true);
}
}
stakeDetails0.initialTokenAmount += stakeDetails1.initialTokenAmount;
stakeDetails0.withdrawnAmount += stakeDetails1.withdrawnAmount;
if (unlocked0) {
stakeDetails0.depositTime = stakeDetails0.depositTime > stakeDetails1.depositTime ?
stakeDetails0.depositTime : stakeDetails1.depositTime;
} else {
stakeDetails0.depositTime = uint48(block.timestamp);
}
stakeDetails0.numOfMonths = numOfMonths0;
if (stakeDetails1.achievementClaimed == _TRUE8) {
stakeDetails0.achievementClaimed = _TRUE8;
}
stakeDetails1.isActive = _FALSE8;
uint24 stakerIndex = stakeDetails1.stakerIndex;
_removeIndividualStake(staker, stakerIndex);
_stakeById[nftId1] = 0;
_freeStakes.push(lookupIndex1);
emit Transfer(staker, address(0), nftId1);
newNftId = _reissueStakeNftId(nftId0, lookupIndex0);
stakeDetails0.nftId = newNftId;
_reissueStakeNft(staker, nftId0, newNftId);
}
function _addSplitStake(
address staker,
uint96 initialTokenAmount,
uint96 withdrawnAmount,
uint48 depositTime,
uint8 numOfMonths,
bool achievementClaimed
) private returns (uint32 nftId) {
uint256[] storage stakes = _individualStakes[staker];
StakingDetails storage splitStakeDetails = _createStakeDetails(
initialTokenAmount,
withdrawnAmount,
depositTime,
numOfMonths,
achievementClaimed,
staker,
uint24(stakes.length)
);
stakes.push(splitStakeDetails.lookupIndex);
nftId = splitStakeDetails.nftId;
}
function bridgeStakeNftOut(address fromAddress, uint256 nftId)
external onlyEverRiseToken returns (uint96 amount)
{
(uint256 lookupIndex, StakingDetails storage stakeDetails) = _getStake(nftId, fromAddress);
return _removeStake(fromAddress, nftId, lookupIndex, stakeDetails);
}
function bridgeOrAirdropStakeNftIn(address toAddress, uint96 depositAmount, uint8 numOfMonths, uint48 depositTime, uint96 withdrawnAmount, uint96 rewards, bool achievementClaimed)
external onlyEverRiseToken returns (uint32 nftId) {
nftId = _createStake(toAddress, depositAmount, withdrawnAmount, numOfMonths, depositTime, achievementClaimed);
if (rewards > 0) {
_transfer(address(this), toAddress, rewards, false);
claimRiseToken.transferFrom(address(0), toAddress, rewards);
}
}
function _createStakeDetails(
uint96 initialTokenAmount,
uint96 withdrawnAmount,
uint48 depositTime,
uint8 numOfMonths,
bool achievementClaimed,
address stakerAddress,
uint24 stakerIndex
) private returns (StakingDetails storage stakeDetails) {
uint256 index = _freeStakes.length;
if (index > 0) {
index = _freeStakes[index - 1];
_freeStakes.pop();
stakeDetails = _allStakeDetails[index];
} else {
index = _allStakeDetails.length;
stakeDetails = _allStakeDetails.push();
}
stakeDetails.initialTokenAmount = initialTokenAmount;
stakeDetails.withdrawnAmount = withdrawnAmount;
stakeDetails.depositTime = depositTime;
stakeDetails.numOfMonths = numOfMonths;
stakeDetails.achievementClaimed = achievementClaimed ? _TRUE8 : _FALSE8;
stakeDetails.stakerAddress = stakerAddress;
stakeDetails.nftId = nextNftId;
stakeDetails.lookupIndex = uint32(index);
stakeDetails.stakerIndex = stakerIndex;
stakeDetails.isActive = _TRUE8;
_stakeById[nextNftId] = index;
++nextNftId;
}
function _transferStake(address fromAddress, address toAddress, uint256 nftId)
private
{
(uint256 lookupIndex, StakingDetails storage stakeDetails) = _getStake(nftId, fromAddress);
require(stakeDetails.withdrawnAmount == 0, "Broken, non-transferable");
stakeDetails.stakerAddress = toAddress;
uint96 amountToTransfer = stakeDetails.initialTokenAmount;
uint8 numOfMonths = stakeDetails.numOfMonths;
decreaseVeAmount(fromAddress, amountToTransfer, numOfMonths, false);
increaseVeAmount(toAddress, amountToTransfer, numOfMonths, false);
veRiseToken.transferFrom(fromAddress, toAddress, amountToTransfer * numOfMonths);
_removeIndividualStake(fromAddress, stakeDetails.stakerIndex);
stakeDetails.stakerIndex = uint24(_individualStakes[toAddress].length);
_individualStakes[toAddress].push(lookupIndex);
everRiseToken.transferStake(fromAddress, toAddress, amountToTransfer);
}
function _removeIndividualStake(address staker, uint24 stakerIndex) private {
uint256[] storage stakes = _individualStakes[staker];
uint24 stakerLength = uint24(stakes.length);
if (stakerLength >= stakerIndex + 1) {
uint256 lastStakeIndex = stakes[stakerLength - 1];
_allStakeDetails[lastStakeIndex].stakerIndex = stakerIndex;
stakes[stakerIndex] = lastStakeIndex;
}
stakes.pop();
}
function _reissueStakeNftId(uint256 nftId, uint256 stakeIndex) private returns (uint32 newNftId) {
_stakeById[nftId] = 0;
newNftId = nextNftId;
_stakeById[newNftId] = stakeIndex;
++nextNftId;
}
function increaseVeAmount(address staker, uint96 amount, uint8 numOfMonths, bool emitEvent) private {
uint256 veTokens = amount * numOfMonths;
totalAmountEscrowed += amount;
totalAmountVoteEscrowed += veTokens;
voteEscrowedBalance[staker] += veTokens;
_transfer(address(this), staker, veTokens, emitEvent);
}
function decreaseVeAmount(address staker, uint96 amount, uint8 numOfMonths, bool emitEvent) private {
uint256 veTokens = amount * numOfMonths;
totalAmountEscrowed -= amount;
totalAmountVoteEscrowed -= veTokens;
voteEscrowedBalance[staker] -= veTokens;
_transfer(staker, address(this), veTokens, emitEvent);
}
function _removeStake(address staker, uint256 nftId, uint256 lookupIndex, StakingDetails storage stakeDetails) private returns (uint96 amount) {
uint96 remainingAmount = stakeDetails.initialTokenAmount - stakeDetails.withdrawnAmount;
decreaseVeAmount(staker, remainingAmount, stakeDetails.numOfMonths, true);
_burnStake(staker, nftId, lookupIndex, stakeDetails);
return remainingAmount;
}
function _burnStake(address staker, uint256 nftId, uint256 lookupIndex, StakingDetails storage stakeDetails) private {
stakeDetails.isActive = _FALSE8;
uint24 stakerIndex = stakeDetails.stakerIndex;
_removeIndividualStake(staker, stakerIndex);
_stakeById[nftId] = 0;
_freeStakes.push(lookupIndex);
}
function _createStake(address staker, uint96 depositAmount, uint96 withdrawnAmount, uint8 numOfMonths, uint48 depositTime, bool achievementClaimed)
private returns (uint32 nftId)
{
if (withdrawnAmount >= depositAmount) revert AmountOutOfRange();
uint256[] storage stakes = _individualStakes[staker];
StakingDetails storage stakeDetails = _createStakeDetails(
depositAmount,
withdrawnAmount,
depositTime,
numOfMonths,
achievementClaimed,
staker,
uint24(stakes.length)
);
stakes.push(stakeDetails.lookupIndex);
uint96 remaining = depositAmount - withdrawnAmount;
increaseVeAmount(staker, remaining, numOfMonths, true);
nftId = stakeDetails.nftId;
}
function calculateTax(uint96 amount, uint256 depositTime, uint256 numOfMonths) public view returns (uint96) {
return calculateTaxAt(amount, depositTime, numOfMonths, block.timestamp);
}
function calculateTaxAt(uint96 amount, uint256 depositTime, uint256 numOfMonths, uint256 timestamp) public view returns (uint96) {
uint256 lockTime = depositTime + (numOfMonths * month);
uint96 taxAmount = 0;
if (timestamp < depositTime + (numOfMonths * month / 2)) {
taxAmount = (amount * firstHalfPenality) / 100;
} else if (timestamp < lockTime) {
taxAmount = (amount * secondHalfPenality) / 100;
}
return taxAmount;
}
function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (
address receiver,
uint256 royaltyAmount
) {
if (_tokenId == 0) revert AmountMustBeGreaterThanZero();
return (address(royaltySplitter), _salePrice / nftRoyaltySplit);
}
function setApprovalForAll(address operator, bool approved) external {
if (address(everRiseToken) == address(0)) revert NotSetup();
address _owner = _msgSender();
everRiseToken.setApprovalForAll(_owner, operator, approved);
emit ApprovalForAll(_owner, operator, approved);
}
function isApprovedForAll(address account, address operator) public view returns (bool) {
return everRiseToken.isApprovedForAll(account, operator);
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external payable walletLock(_msgSender()) {
address operator = _msgSender();
_transferFrom(operator, from, to, tokenId);
_doSafeERC721TransferAcceptanceCheck(operator, from, to, tokenId, data);
}
function safeTransferFrom(address from, address to, uint256 tokenId) external payable walletLock(_msgSender()) {
address operator = _msgSender();
_transferFrom(operator, from, to, tokenId);
_doSafeERC721TransferAcceptanceCheck(operator, from, to, tokenId, new bytes(0));
}
function transferFrom(address from, address to, uint256 tokenId)
external payable walletLock(_msgSender()) {
address operator = _msgSender();
_transferFrom(operator, from, to, tokenId);
}
function approve(address account, address _operator, uint256 nftId)
external onlyEverRiseToken {
_approve(account, _operator, nftId);
}
function approve(address _operator, uint256 nftId) external payable {
_approve(_msgSender(), _operator, nftId);
}
function _approve(address account, address _operator, uint256 nftId) private {
if (ownerOf(nftId) != account) revert NotStakerAddress();
ApprovalChecks memory approvals = everRiseToken.approvals(account);
_individualApproval[nftId] = IndividualAllowance({
operator: _operator,
timestamp: approvals.autoRevokeNftHours == 0 ?
type(uint48).max :
uint48(block.timestamp) + approvals.autoRevokeNftHours * 1 hours,
nftCheck: approvals.nftCheck
});
}
function getApproved(uint256 nftId) external view returns (address) {
getStakeIndex(nftId);
IndividualAllowance storage _allowance = _individualApproval[nftId];
ApprovalChecks memory approvals = everRiseToken.approvals(ownerOf(nftId));
if (block.timestamp > _allowance.timestamp ||
approvals.nftCheck != _allowance.nftCheck)
{
return address(0);
}
return _allowance.operator;
}
function _isAddressApproved(address operator, uint256 nftId) private view returns (bool) {
IndividualAllowance storage _allowance = _individualApproval[nftId];
ApprovalChecks memory approvals = everRiseToken.approvals(ownerOf(nftId));
if (_allowance.operator != operator ||
block.timestamp > _allowance.timestamp ||
approvals.nftCheck != _allowance.nftCheck)
{
return false;
}
return true;
}
function _transferFrom(
address operator,
address from,
address to,
uint256 nftId
) private {
if (address(everRiseToken) == address(0)) revert NotSetup();
if (from == address(0)) revert NotZeroAddress();
if (to == address(0)) revert NotZeroAddress();
if (operator != from &&
!isApprovedForAll(from, operator) &&
!_isAddressApproved(from, nftId)
) revert AmountLargerThanAllowance();
delete _individualApproval[nftId];
_transferStake(from, to, nftId);
emit Transfer(from, to, nftId);
}
function addStaker(address staker, uint256 nftId)
external onlyEverRiseToken
{
emit Transfer(address(0), staker, nftId);
}
function _doSafeERC721TransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
bytes memory data
) private view {
if (isContract(to)) {
try IERC721TokenReceiver(to).onERC721Received(operator, from, id, data) returns (bytes4 response) {
if (response != IERC721TokenReceiver.onERC721Received.selector) {
revert ERC721ReceiverReject();
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert ERC721ReceiverNotImplemented();
}
}
}
function isContract(address account) private view returns (bool) {
return account.code.length > 0;
}
function removeStaker(address staker, uint256 nftId)
external onlyEverRiseToken
{
emit Transfer(staker, address(0), nftId);
}
function reissueStakeNft(address staker, uint256 oldNftId, uint256 newNftId)
external onlyEverRiseToken
{
_reissueStakeNft(staker, oldNftId, newNftId);
}
function _reissueStakeNft(address staker, uint256 oldNftId, uint256 newNftId)
private
{
emit Transfer(staker, address(0), oldNftId);
emit Transfer(address(0), staker, newNftId);
}
function transferExternalTokens(address tokenAddress, address toAddress) external onlyOwner {
if (tokenAddress == address(0)) revert NotZeroAddress();
if (toAddress == address(0)) revert NotZeroAddress();
if (IERC20(tokenAddress).balanceOf(address(this)) == 0) revert AmountLargerThanAvailable();
require(IERC20(tokenAddress).transfer(toAddress, IERC20(tokenAddress).balanceOf(address(this))));
}
function transferToAddressETH(address payable receipient) external onlyOwner {
if (receipient == address(0)) revert NotZeroAddress();
receipient.transfer(address(this).balance);
}
}