ETH Price: $3,534.58 (-4.85%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block
From
To
145017322022-04-01 17:32:381011 days ago1648834358  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
claimRise

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

// 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"}]

608060405234801561001057600080fd5b506040518060400160405280601081526020016f4576657252697365205265776172647360801b81525060405180604001604052806009815260200168636c61696d5249534560b81b815250600061006c61010760201b60201c565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35081516100c990600290602085019061010b565b5080516100dd90600390602084019061010b565b5050600054600180546001600160a01b0319166001600160a01b03909216919091179055506101de565b3390565b828054610117906101a4565b90600052602060002090601f016020900481019282610139576000855561017f565b82601f1061015257805160ff191683800117855561017f565b8280016001018555821561017f579182015b8281111561017f578251825591602001919060010190610164565b5061018b92915061018f565b5090565b5b8082111561018b5760008155600101610190565b600181811c908216806101b857607f821691505b6020821081036101d857634e487b7160e01b600052602260045260246000fd5b50919050565b610748806101ed6000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80638da5cb5b11610097578063b038f20d11610066578063b038f20d146101c4578063d6e03cd4146101d7578063dd62ed3e146101ea578063f2fde38b1461020057600080fd5b80638da5cb5b1461019157806395d89b41146101bc578063a457c2d714610118578063a9059cbb1461011857600080fd5b806323b872dd116100d357806323b872dd14610151578063313ce56714610164578063395093511461011857806370a082311461017e57600080fd5b806306fdde03146100fa578063095ea7b31461011857806318160ddd1461013b575b600080fd5b610102610215565b60405161010f9190610520565b60405180910390f35b61012b610126366004610591565b6102a3565b604051901515815260200161010f565b6101436102b3565b60405190815260200161010f565b61012b61015f3660046105bb565b610326565b61016c601281565b60405160ff909116815260200161010f565b61014361018c3660046105f7565b6103a1565b6000546101a4906001600160a01b031681565b6040516001600160a01b03909116815260200161010f565b61010261042e565b6001546101a4906001600160a01b031681565b61012b6101e5366004610619565b61043b565b6101436101f836600461068c565b600092915050565b61021361020e3660046105f7565b610450565b005b60028054610222906106bf565b80601f016020809104026020016040519081016040528092919081815260200182805461024e906106bf565b801561029b5780601f106102705761010080835404028352916020019161029b565b820191906000526020600020905b81548152906001019060200180831161027e57829003601f168201915b505050505081565b60006102ad6104da565b92915050565b600154604080516306dd29e560e01b815290516000926001600160a01b0316916306dd29e59160048083019260209291908290030181865afa1580156102fd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061032191906106f9565b905090565b600080546001600160a01b0316336001600160a01b03161461034a5761034a6104da565b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161038f91815260200190565b60405180910390a35060019392505050565b600080546001600160a01b03908116908316036103c057506000919050565b6001546040516304cbc2fb60e11b81526001600160a01b0384811660048301529091169063099785f690602401602060405180830381865afa15801561040a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102ad91906106f9565b60038054610222906106bf565b60006104456104da565b979650505050505050565b6104586104f3565b6001600160a01b03811661047f576040516366385fa360e01b815260040160405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6040516354ee515160e01b815260040160405180910390fd5b6000546001600160a01b0316331461051e57604051632e6c18c960e11b815260040160405180910390fd5b565b600060208083528351808285015260005b8181101561054d57858101830151858201604001528201610531565b8181111561055f576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b038116811461058c57600080fd5b919050565b600080604083850312156105a457600080fd5b6105ad83610575565b946020939093013593505050565b6000806000606084860312156105d057600080fd5b6105d984610575565b92506105e760208501610575565b9150604084013590509250925092565b60006020828403121561060957600080fd5b61061282610575565b9392505050565b600080600080600080600060e0888a03121561063457600080fd5b61063d88610575565b965061064b60208901610575565b95506040880135945060608801359350608088013560ff8116811461066f57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561069f57600080fd5b6106a883610575565b91506106b660208401610575565b90509250929050565b600181811c908216806106d357607f821691505b6020821081036106f357634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561070b57600080fd5b505191905056fea2646970667358221220f35afae76adc4b92710eb555fc201d0373faf6a7430815869d3d45f9c99a850e64736f6c634300080d0033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100f55760003560e01c80638da5cb5b11610097578063b038f20d11610066578063b038f20d146101c4578063d6e03cd4146101d7578063dd62ed3e146101ea578063f2fde38b1461020057600080fd5b80638da5cb5b1461019157806395d89b41146101bc578063a457c2d714610118578063a9059cbb1461011857600080fd5b806323b872dd116100d357806323b872dd14610151578063313ce56714610164578063395093511461011857806370a082311461017e57600080fd5b806306fdde03146100fa578063095ea7b31461011857806318160ddd1461013b575b600080fd5b610102610215565b60405161010f9190610520565b60405180910390f35b61012b610126366004610591565b6102a3565b604051901515815260200161010f565b6101436102b3565b60405190815260200161010f565b61012b61015f3660046105bb565b610326565b61016c601281565b60405160ff909116815260200161010f565b61014361018c3660046105f7565b6103a1565b6000546101a4906001600160a01b031681565b6040516001600160a01b03909116815260200161010f565b61010261042e565b6001546101a4906001600160a01b031681565b61012b6101e5366004610619565b61043b565b6101436101f836600461068c565b600092915050565b61021361020e3660046105f7565b610450565b005b60028054610222906106bf565b80601f016020809104026020016040519081016040528092919081815260200182805461024e906106bf565b801561029b5780601f106102705761010080835404028352916020019161029b565b820191906000526020600020905b81548152906001019060200180831161027e57829003601f168201915b505050505081565b60006102ad6104da565b92915050565b600154604080516306dd29e560e01b815290516000926001600160a01b0316916306dd29e59160048083019260209291908290030181865afa1580156102fd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061032191906106f9565b905090565b600080546001600160a01b0316336001600160a01b03161461034a5761034a6104da565b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161038f91815260200190565b60405180910390a35060019392505050565b600080546001600160a01b03908116908316036103c057506000919050565b6001546040516304cbc2fb60e11b81526001600160a01b0384811660048301529091169063099785f690602401602060405180830381865afa15801561040a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102ad91906106f9565b60038054610222906106bf565b60006104456104da565b979650505050505050565b6104586104f3565b6001600160a01b03811661047f576040516366385fa360e01b815260040160405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6040516354ee515160e01b815260040160405180910390fd5b6000546001600160a01b0316331461051e57604051632e6c18c960e11b815260040160405180910390fd5b565b600060208083528351808285015260005b8181101561054d57858101830151858201604001528201610531565b8181111561055f576000604083870101525b50601f01601f1916929092016040019392505050565b80356001600160a01b038116811461058c57600080fd5b919050565b600080604083850312156105a457600080fd5b6105ad83610575565b946020939093013593505050565b6000806000606084860312156105d057600080fd5b6105d984610575565b92506105e760208501610575565b9150604084013590509250925092565b60006020828403121561060957600080fd5b61061282610575565b9392505050565b600080600080600080600060e0888a03121561063457600080fd5b61063d88610575565b965061064b60208901610575565b95506040880135945060608801359350608088013560ff8116811461066f57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561069f57600080fd5b6106a883610575565b91506106b660208401610575565b90509250929050565b600181811c908216806106d357607f821691505b6020821081036106f357634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561070b57600080fd5b505191905056fea2646970667358221220f35afae76adc4b92710eb555fc201d0373faf6a7430815869d3d45f9c99a850e64736f6c634300080d0033

Deployed Bytecode Sourcemap

14989:407:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12220:18;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;12942:101;;;;;;:::i;:::-;;:::i;:::-;;;1218:14:1;;1211:22;1193:41;;1181:2;1166:18;12942:101:0;1053:187:1;15064:124:0;;;:::i;:::-;;;1391:25:1;;;1379:2;1364:18;15064:124:0;1245:177:1;12442:282:0;;;;;;:::i;:::-;;:::i;12178:35::-;;12211:2;12178:35;;;;;1932:4:1;1920:17;;;1902:36;;1890:2;1875:18;12178:35:0;1760:184:1;15196:197:0;;;;;;:::i;:::-;;:::i;3874:20::-;;;;;-1:-1:-1;;;;;3874:20:0;;;;;;-1:-1:-1;;;;;2304:32:1;;;2286:51;;2274:2;2259:18;3874:20:0;2140:203:1;12245:20:0;;;:::i;12139:30::-;;;;;-1:-1:-1;;;;;12139:30:0;;;13283:159;;;;;;:::i;:::-;;:::i;12840:96::-;;;;;;:::i;:::-;12900:7;12840:96;;;;;4609:224;;;;;;:::i;:::-;;:::i;:::-;;12220:18;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;12942:101::-;13000:4;13017:18;:16;:18::i;:::-;12942:101;;;;:::o;15064:124::-;15146:10;;:34;;;-1:-1:-1;;;15146:34:0;;;;15119:7;;-1:-1:-1;;;;;15146:10:0;;:32;;:34;;;;;;;;;;;;;;:10;:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;15139:41;;15064:124;:::o;12442:282::-;12542:4;12589:5;;-1:-1:-1;;;;;12589:5:0;3751:10;-1:-1:-1;;;;;12573:21:0;;12569:73;;12612:18;:16;:18::i;:::-;12676:9;-1:-1:-1;;;;;12659:35:0;12668:6;-1:-1:-1;;;;;12659:35:0;;12687:6;12659:35;;;;1391:25:1;;1379:2;1364:18;;1245:177;12659:35:0;;;;;;;;-1:-1:-1;12712:4:0;12442:282;;;;;:::o;15196:197::-;15264:7;15299:5;;-1:-1:-1;;;;;15299:5:0;;;15288:16;;;;15284:30;;-1:-1:-1;15313:1:0;;15196:197;-1:-1:-1;15196:197:0:o;15284:30::-;15342:10;;:43;;-1:-1:-1;;;15342:43:0;;-1:-1:-1;;;;;2304:32:1;;;15342:43:0;;;2286:51:1;15342:10:0;;;;:34;;2259:18:1;;15342:43:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;12245:20::-;;;;;;;:::i;13283:159::-;13399:4;13416:18;:16;:18::i;:::-;13283:159;;;;;;;;;:::o;4609:224::-;4047:12;:10;:12::i;:::-;-1:-1:-1;;;;;4696:22:0;::::1;4692:51;;4727:16;;-1:-1:-1::0;;;4727:16:0::1;;;;;;;;;;;4692:51;4782:5;::::0;;4761:37:::1;::::0;-1:-1:-1;;;;;4761:37:0;;::::1;::::0;4782:5;::::1;::::0;4761:37:::1;::::0;::::1;4809:5;:16:::0;;-1:-1:-1;;;;;;4809:16:0::1;-1:-1:-1::0;;;;;4809:16:0;;;::::1;::::0;;;::::1;::::0;;4609:224::o;13450:85::-;13509:18;;-1:-1:-1;;;13509:18:0;;;;;;;;;;;3903:104;3953:5;;-1:-1:-1;;;;;3953:5:0;3751:10;3953:21;3949:50;;3983:16;;-1:-1:-1;;;3983:16:0;;;;;;;;;;;3949:50;3903: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://f35afae76adc4b92710eb555fc201d0373faf6a7430815869d3d45f9c99a850e

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.