// Copyright (c) 2022 EverRise Pte Ltd. All rights reserved.
// EverRise licenses this file to you under the MIT license.
/*
EverRise Staking NFTs are containers of Vote Escrowed (ve)EverRise
weighted governance tokens. veRISE generates rewards from the
auto-buyback with a market driven yield curve, based on the transaction
volume of EverRise trades and veEverRise sales.
On sales of nftEverRise Staking NFTs a 10% royalty fee is collected:
* 6% for token auto-buyback from the market, with bought back tokens
directly distributed as ve-staking rewards
* 4% for Business Development (Development, Sustainability and Marketing)
________ _______ __
/ | / \ / |
$$$$$$$$/__ __ ______ ______ $$$$$$$ |$$/ _______ ______ v3.14159265
$$ |__ / \ / |/ \ / \ $$ |__$$ |/ | / | / \
$$ | $$ \ /$$//$$$$$$ |/$$$$$$ |$$ $$< $$ |/$$$$$$$/ /$$$$$$ |
_______ _______ _______ $$$$$/ $$ /$$/ $$ $$ |$$ | $$/ $$$$$$$ |$$ |$$ \ $$ $$ |
| | | ___|_ _| $$ |_____ $$ $$/ $$$$$$$$/ $$ | $$ | $$ |$$ | $$$$$$ |$$$$$$$$/
| | ___| | | $$ | $$$/ $$ |$$ | $$ | $$ |$$ |/ $$/ $$ |
|__|____|___| |___| $$$$$$$$/ $/ $$$$$$$/ $$/ $$/ $$/ $$/ $$$$$$$/ $$$$$$$/ Magnum opus
Learn more about EverRise and the EverRise Ecosystem of dApps and
how our utilities and partners can help protect your investors
and help your project grow: https://everrise.com
*/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
error NotZeroAddress(); // 0x66385fa3
error CallerNotApproved(); // 0x4014f1a5
error InvalidAddress(); // 0xe6c4247b
error CallerNotOwner();
error WalletLocked(); // 0xd550ed24
error DoesNotExist(); // 0xb0ce7591
error AmountMustBeGreaterThanZero(); // 0x5e85ae73
error AmountOutOfRange(); // 0xc64200e9
error StakeStillLocked(); // 0x7f6699f6
error NotStakerAddress(); // 0x2a310a0c
error AmountLargerThanAvailable(); // 0xbb296109
error StakeCanOnlyBeExtended(); // 0x73f7040a
error ArrayLengthsMismatch(); // 0x3b800a46
error NotSetup(); // 0xb09c99c0
error NotAllowedToCreateRewards(); // 0xfdc42f29
error NotAllowedToDeliverRewards(); // 0x69a3e246
error ERC721ReceiverReject(); // 0xfa34343f
error ERC721ReceiverNotImplemented(); // 0xa89c6c0d
error NotEnoughToCoverStakeFee(); // 0x627554ed
error AmountLargerThanAllowance(); // 0x9b144c57
error AchievementNotClaimed(); // 0x3834dd9c
error AchievementAlreadyClaimed(); // 0x2d5345f4
error AmountMustBeAnInteger(); // 0x743aec61
error BrokenStatusesDiffer(); // 0x097b027d
error AchievementClaimStatusesDiffer(); // 0x6524e8b0
error UnlockedStakesMustBeSametimePeriod();// 0x42e227b0
error CannotMergeLockedAndUnlockedStakes();// 0x9efeef2c
error StakeUnlocked(); // 0x6717a455
error NoRewardsToClaim(); // 0x73380d99
error NotTransferrable(); // 0x54ee5151
error Overflow(); // 0x35278d12
error MergeNotEnabled(); // 0x
// File: EverRise-v3/Interfaces/IERC173-Ownable.sol
interface IOwnable {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function owner() external view returns (address);
function transferOwnership(address newOwner) external;
}
// File: EverRise-v3/Abstract/Context.sol
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return payable(msg.sender);
}
}
// File: EverRise-v3/Abstract/ERC173-Ownable.sol
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);
}
// Allow contract ownership and access to contract onlyOwner functions
// to be locked using EverOwn with control gated by community vote.
//
// EverRise ($RISE) stakers become voting members of the
// decentralized autonomous organization (DAO) that controls access
// to the token contract via the EverRise Ecosystem dApp EverOwn
function transferOwnership(address newOwner) external virtual onlyOwner {
if (newOwner == address(0)) revert NotZeroAddress();
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
struct ApprovalChecks {
// Prevent permits being reused (IERC2612)
uint64 nonce;
// Allow revoke all spenders/operators approvals in single txn
uint32 nftCheck;
uint32 tokenCheck;
// Allow auto timeout on approvals
uint16 autoRevokeNftHours;
uint16 autoRevokeTokenHours;
// Allow full wallet locking of all transfers
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);
}
// File: EverRise-v3/Interfaces/IERC20-Token.sol
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);
}
// File: EverRise-v3/Interfaces/IEverRise.sol
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);
}
// File: EverRise-v3/Interfaces/IERC721-Nft.sol
interface IERC721 /* is ERC165 */ {
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);
}
// File: EverRise-v3/Interfaces/InftEverRise.sol
struct StakingDetails {
uint96 initialTokenAmount; // Max 79 Bn tokens
uint96 withdrawnAmount; // Max 79 Bn tokens
uint48 depositTime; // 8 M years
uint8 numOfMonths; // Max 256 month period
uint8 achievementClaimed;
// 256 bits, 20000 gwei gas
address stakerAddress; // 160 bits (96 bits remaining)
uint32 nftId; // Max 4 Bn nfts issued
uint32 lookupIndex; // Max 4 Bn active stakes
uint24 stakerIndex; // Max 16 M active stakes per wallet
uint8 isActive;
// 256 bits, 20000 gwei gas
} // Total 768 bits, 40000 gwei gas
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;
}
// File: EverRise-v3/Abstract/virtualToken.sol
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();
}
}
// File: EverRise-v3/claimRISE.sol
// Copyright (c) 2022 EverRise Pte Ltd. All rights reserved.
// EverRise licenses this file to you under the MIT license.
/*
Virtual token that allows unclaimed rewards from EverRise Staking NFTs
and its Vote Escrowed (ve) EverRise to display in wallet balances.
________ _______ __
/ | / \ / |
$$$$$$$$/__ __ ______ ______ $$$$$$$ |$$/ _______ ______ v3.14159265
$$ |__ / \ / |/ \ / \ $$ |__$$ |/ | / | / \
______ _____ $$ | $$ \ /$$//$$$$$$ |/$$$$$$ |$$ $$< $$ |/$$$$$$$/ /$$$$$$ |
_________ /_____ ___(_)______ ___ $$$$$/ $$ /$$/ $$ $$ |$$ | $$/ $$$$$$$ |$$ |$$ \ $$ $$ |
_ ___/_ /_ __ `/_ /__ __ `__ \$$ |_____ $$ $$/ $$$$$$$$/ $$ | $$ | $$ |$$ | $$$$$$ |$$$$$$$$/
/ /__ _ / / /_/ /_ / _ / / / / /$$ | $$$/ $$ |$$ | $$ | $$ |$$ |/ $$/ $$ |
\___/ /_/ \__,_/ /_/ /_/ /_/ /_/ $$$$$$$$/ $/ $$$$$$$/ $$/ $$/ $$/ $$/ $$$$$$$/ $$$$$$$/
Learn more about EverRise and the EverRise Ecosystem of dApps and
how our utilities and partners can help protect your investors
and help your project grow: https://www.everrise.com
*/
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);
}
}
// File: EverRise-v3/veRISE.sol
// Copyright (c) 2022 EverRise Pte Ltd. All rights reserved.
// EverRise licenses this file to you under the MIT license.
/*
Virtual token that allows the Vote Escrowed (ve) EverRise weigthed
governance tokens from EverRise Staking NFTs to display in
wallet balances.
________ _______ __
/ | / \ / |
$$$$$$$$/__ __ ______ ______ $$$$$$$ |$$/ _______ ______ v3.14159265
$$ |__ / \ / |/ \ / \ $$ |__$$ |/ | / | / \
$$ | $$ \ /$$//$$$$$$ |/$$$$$$ |$$ $$< $$ |/$$$$$$$/ /$$$$$$ |
__ _____ $$$$$/ $$ /$$/ $$ $$ |$$ | $$/ $$$$$$$ |$$ |$$ \ $$ $$ |
\ \ / / _ \ $$ |_____ $$ $$/ $$$$$$$$/ $$ | $$ | $$ |$$ | $$$$$$ |$$$$$$$$/
\ V / __/ $$ | $$$/ $$ |$$ | $$ | $$ |$$ |/ $$/ $$ |
\_/ \___| $$$$$$$$/ $/ $$$$$$$/ $$/ $$/ $$/ $$/ $$$$$$$/ $$$$$$$/
Learn more about EverRise and the EverRise Ecosystem of dApps and
how our utilities and partners can help protect your investors
and help your project grow: https://www.everrise.com
*/
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);
}
}
// File: EverRise-v3/Interfaces/IEverRoyaltySplitter.sol
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;
}
/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
interface IERC721TokenReceiver {
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external view returns(bytes4);
}
interface IERC721Metadata /* is ERC721 */ {
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 /* is ERC721 */ {
function totalSupply() external view returns (uint256);
function tokenByIndex(uint256 _index) external view returns (uint256);
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}
// File: EverRise-v3/Interfaces/IEverRiseRenderer.sol
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;
}
// File: EverRise-v3/Interfaces/IERC165-SupportsInterface.sol
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// File: EverRise-v3/Abstract/ERC165-SupportsInterface.sol
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// File: EverRise-v3/Interfaces/IERC2981-Royalty.sol
interface IERC2981 is IERC165 {
function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (
address receiver,
uint256 royaltyAmount
);
}
// File: EverRise-v3/nftEverRise.sol
// Copyright (c) 2022 EverRise Pte Ltd. All rights reserved.
// EverRise licenses this file to you under the MIT license.
/*
EverRise Staking NFTs are Vote Escrowed (ve) EverRise weigthed governance tokens
which generate rewards with a market driven yield curve, based of the
transaction volume of EverRise trades and veEverRise sales.
On sales of veEverRise Staking NFTs a 10% royalty fee is collected
* 6% for token Buyback from the market,
with bought back tokens directly distributed as ve-staking rewards
* 4% for Business Development (Development, Sustainability and Marketing)
________ _______ __
/ | / \ / |
$$$$$$$$/__ __ ______ ______ $$$$$$$ |$$/ _______ ______ v3.14159265
$$ |__ / \ / |/ \ / \ $$ |__$$ |/ | / | / \
$$ | $$ \ /$$//$$$$$$ |/$$$$$$ |$$ $$< $$ |/$$$$$$$/ /$$$$$$ |
_______ _______ _______ $$$$$/ $$ /$$/ $$ $$ |$$ | $$/ $$$$$$$ |$$ |$$ \ $$ $$ |
| | | ___|_ _| $$ |_____ $$ $$/ $$$$$$$$/ $$ | $$ | $$ |$$ | $$$$$$ |$$$$$$$$/
| | ___| | | $$ | $$$/ $$ |$$ | $$ | $$ |$$ |/ $$/ $$ |
|__|____|___| |___| $$$$$$$$/ $/ $$$$$$$/ $$/ $$/ $$/ $$/ $$$$$$$/ $$$$$$$/
Learn more about EverRise and the EverRise Ecosystem of dApps and
how our utilities and partners can help protect your investors
and help your project grow: https://www.everrise.com
*/
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;
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
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
{
// Catch typos, if decimals are pre-added
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());
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
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();
// One of the addresses has to be this contract
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) {
// Stake time period must be in valid range
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);
}
// Rewards withdrawal doesn't need token lock check as is adding to locked wallet not removing
function withdrawRewards() external {
address staker = _msgSender();
uint256 rewards = unclaimedRewardsBalance(staker);
if (rewards == 0) revert NoRewardsToClaim();
// Something to withdraw
_withdrawnRewards[staker] += rewards;
// Remove the veTokens for the rewards
_transfer(staker, address(this), rewards, false);
// Emit transfer
claimRiseToken.transferFrom(staker, address(0), rewards);
// Send RISE 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);
// Out of period, inital now becomes remaining
remaining -= amount;
reissueNft = true;
}
if (stakeDetails.initialTokenAmount != remaining) {
// Out of period, zero out the withdrawal amount
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 {
// Nothing changed, keep the same nft
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);
// Can only claim once
if (stakeDetails.achievementClaimed == _TRUE8) {
revert AchievementAlreadyClaimed();
}
// Reset broken status if unlocked
if (stakeDetails.withdrawnAmount > 0) {
stakeDetails.initialTokenAmount -= stakeDetails.withdrawnAmount;
stakeDetails.withdrawnAmount = 0;
}
// Mark claimed
stakeDetails.achievementClaimed = _TRUE8;
// Get new id
newNftId = _reissueStakeNftId(nftId, lookupIndex);
// Set new id
stakeDetails.nftId = newNftId;
// Emit burn and mint events
_reissueStakeNft(staker, nftId, newNftId);
}
function getTime() external view returns (uint256) {
// Used to workout UI time drift from blockchain
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); // calculate early penalty tax
stakeDetails.withdrawnAmount += uint96(amount); // update the withdrawl 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);
// Get current amount for main contract change event
original = stakeDetails.initialTokenAmount - stakeDetails.withdrawnAmount;
// Take amount off the withdrawnAmount, "repairing" the stake
if (amount > stakeDetails.withdrawnAmount) {
// Take withdrawn off amount
amount -= stakeDetails.withdrawnAmount;
// Clear withdrawn
stakeDetails.withdrawnAmount = 0;
// Add remaining to initial
stakeDetails.initialTokenAmount += amount;
} else {
// Just reduce amount withdrawn
stakeDetails.withdrawnAmount -= amount;
}
// Relock
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();
// Stake time period must be in valid range
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;
// Relock
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);
// Transfer everything, easier than transferring extras later
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);
// Update the existing stake
uint96 transferredWithdrawnAmount = toUint96(stakeDetails.withdrawnAmount * uint256(amount) / stakeDetails.initialTokenAmount);
stakeDetails.initialTokenAmount -= amount;
stakeDetails.withdrawnAmount -= transferredWithdrawnAmount;
stakeDetails.nftId = newNftId0;
// Create new stake
newNftId1 = _addSplitStake(
staker,
amount, // initialTokenAmount
transferredWithdrawnAmount, // withdrawnAmount
stakeDetails.depositTime, // depositTime
stakeDetails.numOfMonths,
stakeDetails.achievementClaimed == _TRUE8 // achievementClaimed
);
_reissueStakeNft(staker, nftId, newNftId0);
emit Transfer(address(0), staker, newNftId1);
}
function roundingCheck(uint96 amount, bool allowZero) private pure {
// Round to nearest unit
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();
}
// Reset broken status if unlocked
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 {
// Both locked
if (!overrideStatuses && (stakeDetails0.withdrawnAmount > 0) != (stakeDetails1.withdrawnAmount > 0)) {
revert BrokenStatusesDiffer();
}
}
uint8 numOfMonths0 = stakeDetails0.numOfMonths;
if (!unlocked0) {
uint8 extraMonths = 0;
uint96 amount = 0;
// Must both be locked
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) {
// Give new tokens for time period
increaseVeAmount(staker, amount, extraMonths, true);
}
}
stakeDetails0.initialTokenAmount += stakeDetails1.initialTokenAmount;
stakeDetails0.withdrawnAmount += stakeDetails1.withdrawnAmount;
if (unlocked0) {
// For unlocked, use higher of two deposit times
// Can't "age" and nft by merging an older one in
stakeDetails0.depositTime = stakeDetails0.depositTime > stakeDetails1.depositTime ?
stakeDetails0.depositTime : stakeDetails1.depositTime;
} else {
// Re-lock starting now
stakeDetails0.depositTime = uint48(block.timestamp);
}
stakeDetails0.numOfMonths = numOfMonths0;
if (stakeDetails1.achievementClaimed == _TRUE8) {
stakeDetails0.achievementClaimed = _TRUE8;
}
// Drop the second stake
stakeDetails1.isActive = _FALSE8;
uint24 stakerIndex = stakeDetails1.stakerIndex;
_removeIndividualStake(staker, stakerIndex);
// Clear the lookup for second
_stakeById[nftId1] = 0;
// Add to available data items
_freeStakes.push(lookupIndex1);
// Burn the second stake completely
emit Transfer(staker, address(0), nftId1);
// Renumber first stake
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];
// Create new stake
StakingDetails storage splitStakeDetails = _createStakeDetails(
initialTokenAmount, // initialTokenAmount
withdrawnAmount, // withdrawnAmount
depositTime, // depositTime
numOfMonths,
achievementClaimed, // achievementClaimed
staker,
uint24(stakes.length) // New staker's stake index
);
// Add new stake to individual's list
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);
// Emit event
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) {
// Is an existing allocated StakingDetails
// that we can reuse for cheaper gas
index = _freeStakes[index - 1];
_freeStakes.pop();
stakeDetails = _allStakeDetails[index];
} else {
// None free, allocate a new StakingDetails
index = _allStakeDetails.length;
stakeDetails = _allStakeDetails.push();
}
// Set stake details
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;
// Set lookup
_stakeById[nextNftId] = index;
// Increase the next nft id
++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;
// Full initial as withdrawn must be zero (above)
uint96 amountToTransfer = stakeDetails.initialTokenAmount;
uint8 numOfMonths = stakeDetails.numOfMonths;
// Remove veTokens from sender (don't emit ve transfer event)
decreaseVeAmount(fromAddress, amountToTransfer, numOfMonths, false);
// Give veTokens to receiver (don't emit ve transfer event)
increaseVeAmount(toAddress, amountToTransfer, numOfMonths, false);
// Emit the ve transfer event
veRiseToken.transferFrom(fromAddress, toAddress, amountToTransfer * numOfMonths);
// Remove from previous owners list
_removeIndividualStake(fromAddress, stakeDetails.stakerIndex);
// Add to new owners list
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) {
// Not last item, overwrite with last item from account stakes
uint256 lastStakeIndex = stakes[stakerLength - 1];
_allStakeDetails[lastStakeIndex].stakerIndex = stakerIndex;
stakes[stakerIndex] = lastStakeIndex;
}
// Remove last item
stakes.pop();
}
function _reissueStakeNftId(uint256 nftId, uint256 stakeIndex) private returns (uint32 newNftId) {
// Burn the Stake NFT id
_stakeById[nftId] = 0;
// Reissue new Stake NFT id
newNftId = nextNftId;
_stakeById[newNftId] = stakeIndex;
// Increase the next nft id
++nextNftId;
}
function increaseVeAmount(address staker, uint96 amount, uint8 numOfMonths, bool emitEvent) private {
// Transfer vote escrowed tokens from contract to staker
uint256 veTokens = amount * numOfMonths;
totalAmountEscrowed += amount;
totalAmountVoteEscrowed += veTokens;
voteEscrowedBalance[staker] += veTokens; // increase the ve tokens amount
_transfer(address(this), staker, veTokens, emitEvent);
}
function decreaseVeAmount(address staker, uint96 amount, uint8 numOfMonths, bool emitEvent) private {
// Transfer vote escrowed tokens back to the contract
uint256 veTokens = amount * numOfMonths;
totalAmountEscrowed -= amount;
totalAmountVoteEscrowed -= veTokens;
voteEscrowedBalance[staker] -= veTokens; // decrease the ve tokens amount
_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);
// Clear the lookup
_stakeById[nftId] = 0;
// Add to available data items
_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];
// Create new stake
StakingDetails storage stakeDetails = _createStakeDetails(
depositAmount, // initialTokenAmount
withdrawnAmount, // withdrawnAmount
depositTime, // depositTime
numOfMonths,
achievementClaimed, // achievementClaimed
staker,
uint24(stakes.length) // New staker's stake index
);
// Add new stake to individual's list
stakes.push(stakeDetails.lookupIndex);
uint96 remaining = depositAmount - withdrawnAmount;
increaseVeAmount(staker, remaining, numOfMonths, true);
// Mint new Stake NFT to staker
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);
}
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
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);
}
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
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 : // Don't timeout approval
uint48(block.timestamp) + approvals.autoRevokeNftHours * 1 hours, // Timeout after user chosen period,
nftCheck: approvals.nftCheck
});
}
function getApproved(uint256 nftId) external view returns (address) {
getStakeIndex(nftId); // Reverts on not exist
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();
// Clear any individual approvals
delete _individualApproval[nftId];
_transferStake(from, to, nftId);
// Signal transfer complete
emit Transfer(from, to, nftId);
}
function addStaker(address staker, uint256 nftId)
external onlyEverRiseToken
{
// Send event for new staking
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) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
function removeStaker(address staker, uint256 nftId)
external onlyEverRiseToken
{
// Send event for left staking
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
{
// Burn old Stake NFT
emit Transfer(staker, address(0), oldNftId);
// Reissue new Stake NFT
emit Transfer(address(0), staker, newNftId);
}
// Admin for trapped tokens
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);
}
}
{
"compilationTarget": {
"nftEverRise.sol": "nftEverRise"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AchievementAlreadyClaimed","type":"error"},{"inputs":[],"name":"AchievementClaimStatusesDiffer","type":"error"},{"inputs":[],"name":"AchievementNotClaimed","type":"error"},{"inputs":[],"name":"AmountLargerThanAllowance","type":"error"},{"inputs":[],"name":"AmountLargerThanAvailable","type":"error"},{"inputs":[],"name":"AmountMustBeAnInteger","type":"error"},{"inputs":[],"name":"AmountMustBeGreaterThanZero","type":"error"},{"inputs":[],"name":"AmountOutOfRange","type":"error"},{"inputs":[],"name":"BrokenStatusesDiffer","type":"error"},{"inputs":[],"name":"CallerNotApproved","type":"error"},{"inputs":[],"name":"CallerNotOwner","type":"error"},{"inputs":[],"name":"CannotMergeLockedAndUnlockedStakes","type":"error"},{"inputs":[],"name":"DoesNotExist","type":"error"},{"inputs":[],"name":"ERC721ReceiverNotImplemented","type":"error"},{"inputs":[],"name":"ERC721ReceiverReject","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"MergeNotEnabled","type":"error"},{"inputs":[],"name":"NoRewardsToClaim","type":"error"},{"inputs":[],"name":"NotAllowedToDeliverRewards","type":"error"},{"inputs":[],"name":"NotEnoughToCoverStakeFee","type":"error"},{"inputs":[],"name":"NotSetup","type":"error"},{"inputs":[],"name":"NotStakerAddress","type":"error"},{"inputs":[],"name":"NotZeroAddress","type":"error"},{"inputs":[],"name":"Overflow","type":"error"},{"inputs":[],"name":"StakeCanOnlyBeExtended","type":"error"},{"inputs":[],"name":"StakeStillLocked","type":"error"},{"inputs":[],"name":"StakeUnlocked","type":"error"},{"inputs":[],"name":"UnlockedStakesMustBeSametimePeriod","type":"error"},{"inputs":[],"name":"WalletLocked","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_address","type":"address"}],"name":"AddRewardCreator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_operator","type":"address"},{"indexed":false,"internalType":"bool","name":"_approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"EverRiseTokenSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_address","type":"address"}],"name":"ExcludedFromRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_address","type":"address"}],"name":"IncludedToRewards","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":"_address","type":"address"}],"name":"RemoveRewardCreator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"RendererAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"RoyaltyAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"RoyaltyFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_address","type":"address"}],"name":"SetAchievementNfts","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"StakeCreateCostUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"withdrawPct","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"firstHalfPenality","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"secondHalfPenality","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxStakeMonths","type":"uint256"},{"indexed":false,"internalType":"bool","name":"mergeEnabled","type":"bool"}],"name":"StakingParametersSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addAddressToCreate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"addStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"toAddress","type":"address"},{"internalType":"uint96","name":"depositAmount","type":"uint96"},{"internalType":"uint8","name":"numOfMonths","type":"uint8"},{"internalType":"uint48","name":"depositTime","type":"uint48"},{"internalType":"uint96","name":"withdrawnAmount","type":"uint96"},{"internalType":"uint96","name":"rewards","type":"uint96"},{"internalType":"bool","name":"achievementClaimed","type":"bool"}],"name":"bridgeOrAirdropStakeNftIn","outputs":[{"internalType":"uint32","name":"nftId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fromAddress","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"bridgeStakeNftOut","outputs":[{"internalType":"uint96","name":"amount","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint256","name":"depositTime","type":"uint256"},{"internalType":"uint256","name":"numOfMonths","type":"uint256"}],"name":"calculateTax","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint256","name":"depositTime","type":"uint256"},{"internalType":"uint256","name":"numOfMonths","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"calculateTaxAt","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"claimAchievement","outputs":[{"internalType":"uint32","name":"newNftId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimRiseToken","outputs":[{"internalType":"contract claimRise","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"createRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentAchievementNfts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"earlyWithdraw","outputs":[{"internalType":"uint32","name":"newNftId","type":"uint32"},{"internalType":"uint96","name":"penaltyAmount","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint8","name":"numOfMonths","type":"uint8"}],"name":"enterStaking","outputs":[{"internalType":"uint32","name":"nftId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"everRiseToken","outputs":[{"internalType":"contract IEverRise","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"excludeFromReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"uint8","name":"numOfMonths","type":"uint8"}],"name":"extendStake","outputs":[{"internalType":"uint32","name":"newNftId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"firstHalfPenality","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"getNftData","outputs":[{"components":[{"internalType":"uint96","name":"initialTokenAmount","type":"uint96"},{"internalType":"uint96","name":"withdrawnAmount","type":"uint96"},{"internalType":"uint48","name":"depositTime","type":"uint48"},{"internalType":"uint8","name":"numOfMonths","type":"uint8"},{"internalType":"uint8","name":"achievementClaimed","type":"uint8"},{"internalType":"address","name":"stakerAddress","type":"address"},{"internalType":"uint32","name":"nftId","type":"uint32"},{"internalType":"uint32","name":"lookupIndex","type":"uint32"},{"internalType":"uint24","name":"stakerIndex","type":"uint24"},{"internalType":"uint8","name":"isActive","type":"uint8"}],"internalType":"struct StakingDetails","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"address","name":"staker","type":"address"}],"name":"getStake","outputs":[{"components":[{"internalType":"uint96","name":"initialTokenAmount","type":"uint96"},{"internalType":"uint96","name":"withdrawnAmount","type":"uint96"},{"internalType":"uint48","name":"depositTime","type":"uint48"},{"internalType":"uint8","name":"numOfMonths","type":"uint8"},{"internalType":"uint8","name":"achievementClaimed","type":"uint8"},{"internalType":"address","name":"stakerAddress","type":"address"},{"internalType":"uint32","name":"nftId","type":"uint32"},{"internalType":"uint32","name":"lookupIndex","type":"uint32"},{"internalType":"uint24","name":"stakerIndex","type":"uint24"},{"internalType":"uint8","name":"isActive","type":"uint8"}],"internalType":"struct StakingDetails","name":"stakeDetails","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getTotalRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"includeInReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"increaseStake","outputs":[{"internalType":"uint32","name":"newNftId","type":"uint32"},{"internalType":"uint96","name":"original","type":"uint96"},{"internalType":"uint8","name":"numOfMonths","type":"uint8"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isExcludedFromReward","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"bool","name":"overrideNotClaimed","type":"bool"}],"name":"leaveStaking","outputs":[{"internalType":"uint96","name":"amount","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxEarlyWithdrawalPercent","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxStakeMonths","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mergeEnabled","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId0","type":"uint256"},{"internalType":"uint256","name":"nftId1","type":"uint256"},{"internalType":"bool","name":"overrideStatuses","type":"bool"}],"name":"mergeStakes","outputs":[{"internalType":"uint32","name":"newNftId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"month","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nftRoyaltySplit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"oldNftId","type":"uint256"},{"internalType":"uint256","name":"newNftId","type":"uint256"}],"name":"reissueStakeNft","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"removeAddressToCreate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"removeStaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renderer","outputs":[{"internalType":"contract IEverRiseRenderer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tAmount","type":"uint256"}],"name":"rewardsFromToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"royaltySplitter","outputs":[{"internalType":"contract IEverRoyaltySplitter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"secondHalfPenality","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"setAchievementNfts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"setEverRiseToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"royaltySplitRate","type":"uint256"}],"name":"setNftRoyaltyFeePercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAddress","type":"address"}],"name":"setRendererAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAddress","type":"address"}],"name":"setRoyaltyAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stakeCreateCost","type":"uint256"},{"internalType":"uint256","name":"numOfDecimals","type":"uint256"}],"name":"setStakeCreateCost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_withdrawPercent","type":"uint8"},{"internalType":"uint8","name":"_firstHalfPenality","type":"uint8"},{"internalType":"uint8","name":"_secondHalfPenality","type":"uint8"},{"internalType":"uint8","name":"_maxStakeMonths","type":"uint8"},{"internalType":"bool","name":"_mergEnabled","type":"bool"}],"name":"setStakingParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"splitStake","outputs":[{"internalType":"uint32","name":"newNftId0","type":"uint32"},{"internalType":"uint32","name":"newNftId1","type":"uint32"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"stakeCreateCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"rAmount","type":"uint256"}],"name":"tokenFromRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAmountEscrowed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAmountVoteEscrowed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalRewardsDistributed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalRewardsUnclaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"toAddress","type":"address"}],"name":"transferExternalTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"receipient","type":"address"}],"name":"transferToAddressETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"unclaimedRewardsBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"veRiseToken","outputs":[{"internalType":"contract veRise","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"voteEscrowedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"bool","name":"overrideNotClaimed","type":"bool"}],"name":"withdraw","outputs":[{"internalType":"uint32","name":"newNftId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawRewards","outputs":[],"stateMutability":"nonpayable","type":"function"}]