ETH Price: $3,504.37 (+3.95%)
Gas: 3 Gwei

Token

Vote-escrowed EverRise (veRISE)
 

Overview

Max Total Supply

166,927,914,734.751513958254173124 veRISE

Holders

387

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Balance
3,175,870 veRISE

Value
$0.00
0x417d2a0fc1895dbb4af6a59f2dce8d6f57d983dd
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
veRise

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2022-04-04
*/

// 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
*/

// 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);
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"CallerNotOwner","type":"error"},{"inputs":[],"name":"NotTransferrable","type":"error"},{"inputs":[],"name":"NotZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":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":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"transferFromWithPermit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"veEverRise","outputs":[{"internalType":"contract InftEverRise","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b506040518060400160405280601681526020017f566f74652d657363726f776564204576657252697365000000000000000000008152506040518060400160405280600681526020016576655249534560d01b815250600061007661011160201b60201c565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35081516100d3906002906020850190610115565b5080516100e7906003906020840190610115565b5050600054600180546001600160a01b0319166001600160a01b03909216919091179055506101e8565b3390565b828054610121906101ae565b90600052602060002090601f0160209004810192826101435760008555610189565b82601f1061015c57805160ff1916838001178555610189565b82800160010185558215610189579182015b8281111561018957825182559160200191906001019061016e565b50610195929150610199565b5090565b5b80821115610195576000815560010161019a565b600181811c908216806101c257607f821691505b6020821081036101e257634e487b7160e01b600052602260045260246000fd5b50919050565b610748806101f76000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80638da5cb5b11610097578063b038f20d11610066578063b038f20d146101c4578063d6e03cd4146101d7578063dd62ed3e146101ea578063f2fde38b1461020057600080fd5b80638da5cb5b1461019157806395d89b41146101bc578063a457c2d714610118578063a9059cbb1461011857600080fd5b806323b872dd116100d357806323b872dd14610151578063313ce56714610164578063395093511461011857806370a082311461017e57600080fd5b806306fdde03146100fa578063095ea7b31461011857806318160ddd1461013b575b600080fd5b610102610215565b60405161010f9190610520565b60405180910390f35b61012b610126366004610591565b6102a3565b604051901515815260200161010f565b6101436102b3565b60405190815260200161010f565b61012b61015f3660046105bb565b610326565b61016c601281565b60405160ff909116815260200161010f565b61014361018c3660046105f7565b6103a1565b6000546101a4906001600160a01b031681565b6040516001600160a01b03909116815260200161010f565b61010261042e565b6001546101a4906001600160a01b031681565b61012b6101e5366004610619565b61043b565b6101436101f836600461068c565b600092915050565b61021361020e3660046105f7565b610450565b005b60028054610222906106bf565b80601f016020809104026020016040519081016040528092919081815260200182805461024e906106bf565b801561029b5780601f106102705761010080835404028352916020019161029b565b820191906000526020600020905b81548152906001019060200180831161027e57829003601f168201915b505050505081565b60006102ad6104da565b92915050565b600154604080516363234f3d60e01b815290516000926001600160a01b0316916363234f3d9160048083019260209291908290030181865afa1580156102fd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061032191906106f9565b905090565b600080546001600160a01b0316336001600160a01b03161461034a5761034a6104da565b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161038f91815260200190565b60405180910390a35060019392505050565b600080546001600160a01b03908116908316036103c057506000919050565b60015460405163894f03b560e01b81526001600160a01b0384811660048301529091169063894f03b590602401602060405180830381865afa15801561040a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102ad91906106f9565b60038054610222906106bf565b60006104456104da565b979650505050505050565b6104586104f3565b6001600160a01b03811661047f576040516366385fa360e01b815260040160405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6040516354ee515160e01b815260040160405180910390fd5b6000546001600160a01b0316331461051e57604051632e6c18c960e11b815260040160405180910390fd5b565b600060208083528351808285015260005b8181101561054d57858101830151858201604001528201610531565b8181111561055f576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b038116811461058c57600080fd5b919050565b600080604083850312156105a457600080fd5b6105ad83610575565b946020939093013593505050565b6000806000606084860312156105d057600080fd5b6105d984610575565b92506105e760208501610575565b9150604084013590509250925092565b60006020828403121561060957600080fd5b61061282610575565b9392505050565b600080600080600080600060e0888a03121561063457600080fd5b61063d88610575565b965061064b60208901610575565b95506040880135945060608801359350608088013560ff8116811461066f57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561069f57600080fd5b6106a883610575565b91506106b660208401610575565b90509250929050565b600181811c908216806106d357607f821691505b6020821081036106f357634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561070b57600080fd5b505191905056fea26469706673582212201e80de34cf8f2824df4d8a4de6eda16b4b93f6b4d9af40966fdf522b9197067864736f6c634300080d0033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100f55760003560e01c80638da5cb5b11610097578063b038f20d11610066578063b038f20d146101c4578063d6e03cd4146101d7578063dd62ed3e146101ea578063f2fde38b1461020057600080fd5b80638da5cb5b1461019157806395d89b41146101bc578063a457c2d714610118578063a9059cbb1461011857600080fd5b806323b872dd116100d357806323b872dd14610151578063313ce56714610164578063395093511461011857806370a082311461017e57600080fd5b806306fdde03146100fa578063095ea7b31461011857806318160ddd1461013b575b600080fd5b610102610215565b60405161010f9190610520565b60405180910390f35b61012b610126366004610591565b6102a3565b604051901515815260200161010f565b6101436102b3565b60405190815260200161010f565b61012b61015f3660046105bb565b610326565b61016c601281565b60405160ff909116815260200161010f565b61014361018c3660046105f7565b6103a1565b6000546101a4906001600160a01b031681565b6040516001600160a01b03909116815260200161010f565b61010261042e565b6001546101a4906001600160a01b031681565b61012b6101e5366004610619565b61043b565b6101436101f836600461068c565b600092915050565b61021361020e3660046105f7565b610450565b005b60028054610222906106bf565b80601f016020809104026020016040519081016040528092919081815260200182805461024e906106bf565b801561029b5780601f106102705761010080835404028352916020019161029b565b820191906000526020600020905b81548152906001019060200180831161027e57829003601f168201915b505050505081565b60006102ad6104da565b92915050565b600154604080516363234f3d60e01b815290516000926001600160a01b0316916363234f3d9160048083019260209291908290030181865afa1580156102fd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061032191906106f9565b905090565b600080546001600160a01b0316336001600160a01b03161461034a5761034a6104da565b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161038f91815260200190565b60405180910390a35060019392505050565b600080546001600160a01b03908116908316036103c057506000919050565b60015460405163894f03b560e01b81526001600160a01b0384811660048301529091169063894f03b590602401602060405180830381865afa15801561040a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102ad91906106f9565b60038054610222906106bf565b60006104456104da565b979650505050505050565b6104586104f3565b6001600160a01b03811661047f576040516366385fa360e01b815260040160405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6040516354ee515160e01b815260040160405180910390fd5b6000546001600160a01b0316331461051e57604051632e6c18c960e11b815260040160405180910390fd5b565b600060208083528351808285015260005b8181101561054d57858101830151858201604001528201610531565b8181111561055f576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b038116811461058c57600080fd5b919050565b600080604083850312156105a457600080fd5b6105ad83610575565b946020939093013593505050565b6000806000606084860312156105d057600080fd5b6105d984610575565b92506105e760208501610575565b9150604084013590509250925092565b60006020828403121561060957600080fd5b61061282610575565b9392505050565b600080600080600080600060e0888a03121561063457600080fd5b61063d88610575565b965061064b60208901610575565b95506040880135945060608801359350608088013560ff8116811461066f57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561069f57600080fd5b6106a883610575565b91506106b660208401610575565b90509250929050565b600181811c908216806106d357607f821691505b6020821081036106f357634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561070b57600080fd5b505191905056fea26469706673582212201e80de34cf8f2824df4d8a4de6eda16b4b93f6b4d9af40966fdf522b9197067864736f6c634300080d0033

Deployed Bytecode Sourcemap

16450:397:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12023:18;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;12745:101;;;;;;:::i;:::-;;:::i;:::-;;;1218:14:1;;1211:22;1193:41;;1181:2;1166:18;12745:101:0;1053:187:1;16525:126:0;;;:::i;:::-;;;1391:25:1;;;1379:2;1364:18;16525:126:0;1245:177:1;12245:282:0;;;;;;:::i;:::-;;:::i;11981:35::-;;12014:2;11981:35;;;;;1932:4:1;1920:17;;;1902:36;;1890:2;1875:18;11981:35:0;1760:184:1;16659:185:0;;;;;;:::i;:::-;;:::i;3677:20::-;;;;;-1:-1:-1;;;;;3677:20:0;;;;;;-1:-1:-1;;;;;2304:32:1;;;2286:51;;2274:2;2259:18;3677:20:0;2140:203:1;12048:20:0;;;:::i;11942:30::-;;;;;-1:-1:-1;;;;;11942:30:0;;;13086:159;;;;;;:::i;:::-;;:::i;12643:96::-;;;;;;:::i;:::-;12703:7;12643:96;;;;;4412:224;;;;;;:::i;:::-;;:::i;:::-;;12023:18;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;12745:101::-;12803:4;12820:18;:16;:18::i;:::-;12745:101;;;;:::o;16525:126::-;16607:10;;:36;;;-1:-1:-1;;;16607:36:0;;;;16580:7;;-1:-1:-1;;;;;16607:10:0;;:34;;:36;;;;;;;;;;;;;;:10;:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;16600:43;;16525:126;:::o;12245:282::-;12345:4;12392:5;;-1:-1:-1;;;;;12392:5:0;3554:10;-1:-1:-1;;;;;12376:21:0;;12372:73;;12415:18;:16;:18::i;:::-;12479:9;-1:-1:-1;;;;;12462:35:0;12471:6;-1:-1:-1;;;;;12462:35:0;;12490:6;12462:35;;;;1391:25:1;;1379:2;1364:18;;1245:177;12462:35:0;;;;;;;;-1:-1:-1;12515:4:0;12245:282;;;;;:::o;16659:185::-;16727:7;16762:5;;-1:-1:-1;;;;;16762:5:0;;;16751:16;;;;16747:30;;-1:-1:-1;16776:1:0;;16659:185;-1:-1:-1;16659:185:0:o;16747:30::-;16797:10;;:39;;-1:-1:-1;;;16797:39:0;;-1:-1:-1;;;;;2304:32:1;;;16797:39:0;;;2286:51:1;16797:10:0;;;;:30;;2259:18:1;;16797:39:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;12048:20::-;;;;;;;:::i;13086:159::-;13202:4;13219:18;:16;:18::i;:::-;13086:159;;;;;;;;;:::o;4412:224::-;3850:12;:10;:12::i;:::-;-1:-1:-1;;;;;4499:22:0;::::1;4495:51;;4530:16;;-1:-1:-1::0;;;4530:16:0::1;;;;;;;;;;;4495:51;4585:5;::::0;;4564:37:::1;::::0;-1:-1:-1;;;;;4564:37:0;;::::1;::::0;4585:5;::::1;::::0;4564:37:::1;::::0;::::1;4612:5;:16:::0;;-1:-1:-1;;;;;;4612:16:0::1;-1:-1:-1::0;;;;;4612:16:0;;;::::1;::::0;;;::::1;::::0;;4412:224::o;13253:85::-;13312:18;;-1:-1:-1;;;13312:18:0;;;;;;;;;;;3706:104;3756:5;;-1:-1:-1;;;;;3756:5:0;3554:10;3756:21;3752:50;;3786:16;;-1:-1:-1;;;3786:16:0;;;;;;;;;;;3752:50;3706:104::o;14:597:1:-;126:4;155:2;184;173:9;166:21;216:6;210:13;259:6;254:2;243:9;239:18;232:34;284:1;294:140;308:6;305:1;302:13;294:140;;;403:14;;;399:23;;393:30;369:17;;;388:2;365:26;358:66;323:10;;294:140;;;452:6;449:1;446:13;443:91;;;522:1;517:2;508:6;497:9;493:22;489:31;482:42;443:91;-1:-1:-1;595:2:1;574:15;-1:-1:-1;;570:29:1;555:45;;;;602:2;551:54;;14:597;-1:-1:-1;;;14:597:1:o;616:173::-;684:20;;-1:-1:-1;;;;;733:31:1;;723:42;;713:70;;779:1;776;769:12;713:70;616:173;;;:::o;794:254::-;862:6;870;923:2;911:9;902:7;898:23;894:32;891:52;;;939:1;936;929:12;891:52;962:29;981:9;962:29;:::i;:::-;952:39;1038:2;1023:18;;;;1010:32;;-1:-1:-1;;;794:254:1:o;1427:328::-;1504:6;1512;1520;1573:2;1561:9;1552:7;1548:23;1544:32;1541:52;;;1589:1;1586;1579:12;1541:52;1612:29;1631:9;1612:29;:::i;:::-;1602:39;;1660:38;1694:2;1683:9;1679:18;1660:38;:::i;:::-;1650:48;;1745:2;1734:9;1730:18;1717:32;1707:42;;1427:328;;;;;:::o;1949:186::-;2008:6;2061:2;2049:9;2040:7;2036:23;2032:32;2029:52;;;2077:1;2074;2067:12;2029:52;2100:29;2119:9;2100:29;:::i;:::-;2090:39;1949:186;-1:-1:-1;;;1949:186:1:o;2576:693::-;2687:6;2695;2703;2711;2719;2727;2735;2788:3;2776:9;2767:7;2763:23;2759:33;2756:53;;;2805:1;2802;2795:12;2756:53;2828:29;2847:9;2828:29;:::i;:::-;2818:39;;2876:38;2910:2;2899:9;2895:18;2876:38;:::i;:::-;2866:48;;2961:2;2950:9;2946:18;2933:32;2923:42;;3012:2;3001:9;2997:18;2984:32;2974:42;;3066:3;3055:9;3051:19;3038:33;3111:4;3104:5;3100:16;3093:5;3090:27;3080:55;;3131:1;3128;3121:12;3080:55;2576:693;;;;-1:-1:-1;2576:693:1;;;;3154:5;3206:3;3191:19;;3178:33;;-1:-1:-1;3258:3:1;3243:19;;;3230:33;;2576:693;-1:-1:-1;;2576:693:1:o;3274:260::-;3342:6;3350;3403:2;3391:9;3382:7;3378:23;3374:32;3371:52;;;3419:1;3416;3409:12;3371:52;3442:29;3461:9;3442:29;:::i;:::-;3432:39;;3490:38;3524:2;3513:9;3509:18;3490:38;:::i;:::-;3480:48;;3274:260;;;;;:::o;3539:380::-;3618:1;3614:12;;;;3661;;;3682:61;;3736:4;3728:6;3724:17;3714:27;;3682:61;3789:2;3781:6;3778:14;3758:18;3755:38;3752:161;;3835:10;3830:3;3826:20;3823:1;3816:31;3870:4;3867:1;3860:15;3898:4;3895:1;3888:15;3752:161;;3539:380;;;:::o;3924:184::-;3994:6;4047:2;4035:9;4026:7;4022:23;4018:32;4015:52;;;4063:1;4060;4053:12;4015:52;-1:-1:-1;4086:16:1;;3924:184;-1:-1:-1;3924:184:1:o

Swarm Source

ipfs://1e80de34cf8f2824df4d8a4de6eda16b4b93f6b4d9af40966fdf522b91970678
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.