HYPE Price: $22.17 (+0.15%)
 

Overview

HYPE Balance

HyperEVM LogoHyperEVM LogoHyperEVM Logo0 HYPE

HYPE Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SingelTokenVirtualRewarderUpgradeable

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 2000 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.19;

import {ISingelTokenVirtualRewarder} from "./interfaces/ISingelTokenVirtualRewarder.sol";
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {VirtualRewarderCheckpoints} from "./libraries/VirtualRewarderCheckpoints.sol";
import {UpgradeCall} from "../integration/UpgradeCall.sol";

/**
 * @title Single Token Virtual Rewarder Upgradeable
 * @dev An upgradeable contract for managing token rewards based on virtual balances and epochs. It supports functionalities
 * like deposits, withdrawals, and reward calculations based on checkpoints.
 */
contract SingelTokenVirtualRewarderUpgradeable is ISingelTokenVirtualRewarder, Initializable, UpgradeCall {
    /**
     * @title Struct for managing token information within a virtual reward system
     * @notice Holds all pertinent data related to individual tokens within the reward system.
     * @dev The structure stores balances, checkpoint indices, and a mapping of balance checkpoints.
     */
    struct TokenInfo {
        uint256 balance; // Current balance of the token
        uint256 checkpointLastIndex; // Index of the last checkpoint for the token
        uint256 lastEarnEpoch; // The last epoch during which rewards were calculated for the token
        mapping(uint256 index => VirtualRewarderCheckpoints.Checkpoint) balanceCheckpoints; // Mapping of index to balance checkpoints
    }

    /**
     * @notice Address of the strategy contract that interacts with this reward system
     * @dev This should be set to the address of the strategy managing the tokens and their rewards.
     */
    address public override strategy;

    /**
     * @notice Total supply of all tokens managed by the reward system
     * @dev This total supply is used in reward calculations across different epochs.
     */
    uint256 public override totalSupply;

    /**
     * @notice Index of the last checkpoint for the total supply
     * @dev Used to track changes in total supply at each checkpoint.
     */
    uint256 public totalSupplyCheckpointLastIndex;

    /**
     * @notice Mapping of total supply checkpoints
     * @dev This stores checkpoints of the total supply which are referenced in reward calculations.
     */
    mapping(uint256 index => VirtualRewarderCheckpoints.Checkpoint) public totalSupplyCheckpoints;

    /**
     * @notice Mapping from token ID to its associated TokenInfo
     * @dev Keeps track of all relevant token information, including balances and checkpoints.
     */
    mapping(uint256 tokenId => TokenInfo tokenInfo) public tokensInfo;

    /**
     * @notice Mapping from epoch to the total rewards allocated for that epoch
     * @dev Used to determine the amount of rewards available per epoch, which influences reward calculations.
     */
    mapping(uint256 epoch => uint256 rewards) public rewardsPerEpoch;

    /**
     * @notice Constant defining the length of a week in seconds
     * @dev Used for time-related calculations, particularly in determining epoch boundaries.
     */
    uint256 internal constant _WEEK = 86400 * 7;

    /**
     * @dev Custom error for unauthorized access attempts
     * @notice Thrown when an operation is attempted by an unauthorized address, typically checked against the strategy.
     */
    error AccessDenied();

    /**
     * @dev Custom error indicating operation involving zero amount which is not permitted
     * @notice Used primarily in deposit, withdrawal, and reward distribution to prevent erroneous zero value transactions.
     */
    error ZeroAmount();

    error AddressZero();
    
    /**
     * @dev Modifier to restrict function calls to the strategy address
     * @notice Ensures that only the designated strategy can call certain functions.
     */
    modifier onlyStrategy() {
        if (msg.sender != strategy) {
            revert AccessDenied();
        }
        _;
    }

    /**
     * @dev Constructor that disables initialization on implementation.
     */
    constructor() {
        _disableInitializers();
    }

    /**
     * @notice Initializes the contract with necessary governance and operational addresses
     * @dev Sets up operational aspects of the contract. This function can only be called once.
     *
     * @param strategy_ The strategy address that will interact with this contract
     */
    function initialize(address strategy_) external override initializer {
        _checkAddressZero(strategy_);

        strategy = strategy_;
    }

    /**
     * @notice Deposits a specific amount of tokens for a given tokenId
     * @dev This function updates the token's balance and total supply and writes a new checkpoint.
     *
     * @param tokenId_ The ID of the token to deposit
     * @param amount_ The amount of tokens to deposit
     */
    function deposit(uint256 tokenId_, uint256 amount_) external onlyStrategy {
        if (amount_ == 0) {
            revert ZeroAmount();
        }

        TokenInfo storage info = tokensInfo[tokenId_];
        info.balance += amount_;
        totalSupply += amount_;

        uint256 currentEpoch = _currentEpoch();
        _writeCheckpoints(info, currentEpoch);

        emit Deposit(tokenId_, amount_, currentEpoch);
    }

    /**
     * @notice Withdraws a specific amount of tokens for a given tokenId
     * @dev This function updates the token's balance and total supply and writes a new checkpoint.
     *
     * @param tokenId_ The ID of the token from which to withdraw
     * @param amount_ The amount of tokens to withdraw
     */
    function withdraw(uint256 tokenId_, uint256 amount_) external onlyStrategy {
        TokenInfo storage info = tokensInfo[tokenId_];
        if (info.balance == 0 || amount_ == 0) {
            revert ZeroAmount();
        }

        info.balance -= amount_;
        totalSupply -= amount_;

        uint256 currentEpoch = _currentEpoch();
        _writeCheckpoints(info, currentEpoch);

        emit Withdraw(tokenId_, amount_, currentEpoch);
    }

    /**
     * @notice Harvests rewards for a specific tokenId
     * @dev Calculates the available rewards for the token and updates the last earned epoch.
     *
     * IMPORTANT: If the reward was issued after the harvest summon in an epoch,
     *  you will not be able to claim it. Wait for the distribution of rewards for the past era
     *
     * @param tokenId_ The ID of the token for which to harvest rewards
     * @return reward The amount of rewards harvested
     */
    function harvest(uint256 tokenId_) external onlyStrategy returns (uint256 reward) {
        reward = _calculateAvailableRewardsAmount(tokenId_);

        uint256 currentEpoch = _currentEpoch();
        tokensInfo[tokenId_].lastEarnEpoch = currentEpoch;

        emit Harvest(tokenId_, reward, currentEpoch);
        return reward;
    }

    /**
     * @notice Notifies the contract of a new reward amount to be distributed in the current epoch
     * @dev Updates the rewards for the current epoch and emits a notification event.
     *
     * @param amount_ The amount of rewards to distribute
     */
    function notifyRewardAmount(uint256 amount_) external onlyStrategy {
        if (amount_ == 0) {
            revert ZeroAmount();
        }

        uint256 currentEpoch = _currentEpoch();
        rewardsPerEpoch[currentEpoch] += amount_;

        emit NotifyReward(amount_, currentEpoch);
    }

    /**
     * @notice Calculates the available rewards amount for a given tokenId
     *
     * @param tokenId_ The ID of the token to calculate rewards for
     * @return reward The calculated reward amount
     */
    function calculateAvailableRewardsAmount(uint256 tokenId_) external view returns (uint256 reward) {
        return _calculateAvailableRewardsAmount(tokenId_);
    }

    /**
     * @notice Provides the current balance of a specific tokenId
     *
     * @param tokenId_ The ID of the token to check
     * @return The current balance of the token
     */
    function balanceOf(uint256 tokenId_) external view returns (uint256) {
        return tokensInfo[tokenId_].balance;
    }

    /**
     * @notice Provides the balance of a specific tokenId at a given timestamp
     *
     * @param tokenId_ The ID of the token to check
     * @param timestamp_ The specific timestamp to check the balance at
     * @return The balance of the token at the given timestamp
     */
    function balanceOfAt(uint256 tokenId_, uint256 timestamp_) external view returns (uint256) {
        return
            VirtualRewarderCheckpoints.getAmount(
                tokensInfo[tokenId_].balanceCheckpoints,
                tokensInfo[tokenId_].checkpointLastIndex,
                timestamp_
            );
    }

    /**
     * @notice Provides the total supply of tokens at a given timestamp
     *
     * @param timestamp_ The timestamp to check the total supply at
     * @return The total supply of tokens at the specified timestamp
     */
    function totalSupplyAt(uint256 timestamp_) external view returns (uint256) {
        return VirtualRewarderCheckpoints.getAmount(totalSupplyCheckpoints, totalSupplyCheckpointLastIndex, timestamp_);
    }

    /**
     * @notice Returns the checkpoint data for a specific token and index
     *
     * @param tokenId_ The ID of the token to check
     * @param index The index of the checkpoint to retrieve
     * @return A checkpoint struct containing the timestamp and amount at that index
     */
    function balanceCheckpoints(uint256 tokenId_, uint256 index) external view returns (VirtualRewarderCheckpoints.Checkpoint memory) {
        return tokensInfo[tokenId_].balanceCheckpoints[index];
    }

    /**
     * @dev Writes checkpoints for token balance and total supply at a given epoch.
     * @notice This function updates both the token's individual balance checkpoint and the total supply checkpoint.
     *
     * @param info_ The storage reference to the token's information which includes balance and checkpoint index.
     * @param epoch_ The epoch for which the checkpoint is being written.
     */
    function _writeCheckpoints(TokenInfo storage info_, uint256 epoch_) internal {
        info_.checkpointLastIndex = VirtualRewarderCheckpoints.writeCheckpoint(
            info_.balanceCheckpoints,
            info_.checkpointLastIndex,
            epoch_,
            info_.balance
        );

        totalSupplyCheckpointLastIndex = VirtualRewarderCheckpoints.writeCheckpoint(
            totalSupplyCheckpoints,
            totalSupplyCheckpointLastIndex,
            epoch_,
            totalSupply
        );
    }

    /**
     * @notice This function accumulates rewards over each epoch since last claimed to present.
     * @dev Calculates the total available rewards for a given tokenId since the last earned epoch.
     *
     * @param tokenId_ The identifier of the token for which rewards are being calculated.
     * @return reward The total accumulated reward since the last claim.
     */
    function _calculateAvailableRewardsAmount(uint256 tokenId_) internal view returns (uint256 reward) {
        uint256 checkpointLastIndex = tokensInfo[tokenId_].checkpointLastIndex;
        if (checkpointLastIndex == 0) {
            return 0;
        }

        uint256 startEpoch = tokensInfo[tokenId_].lastEarnEpoch;

        uint256 index = startEpoch == 0
            ? 1
            : VirtualRewarderCheckpoints.getCheckpointIndex(
                tokensInfo[tokenId_].balanceCheckpoints,
                tokensInfo[tokenId_].checkpointLastIndex,
                startEpoch
            );

        uint256 epochTimestamp = tokensInfo[tokenId_].balanceCheckpoints[index].timestamp;

        if (epochTimestamp > startEpoch) {
            startEpoch = epochTimestamp;
        }

        uint256 currentEpoch = _currentEpoch();
        uint256 notHarvestedEpochCount = (currentEpoch - startEpoch) / _WEEK;

        for (uint256 i; i < notHarvestedEpochCount; ) {
            reward += _calculateRewardPerEpoch(tokenId_, startEpoch);

            startEpoch += _WEEK;
            unchecked {
                i++;
            }
        }
    }

    /**
     * @notice This method uses the reward per epoch and the token's proportion of the total supply to determine the reward amount.
     * @dev Calculates the reward for a specific tokenId for a single epoch based on the token's balance and total supply.
     *
     * @param tokenId_ The identifier of the token.
     * @param epoch_ The epoch for which to calculate the reward.
     * @return The calculated reward for the epoch.
     */
    function _calculateRewardPerEpoch(uint256 tokenId_, uint256 epoch_) internal view returns (uint256) {
        uint256 balance = VirtualRewarderCheckpoints.getAmount(
            tokensInfo[tokenId_].balanceCheckpoints,
            tokensInfo[tokenId_].checkpointLastIndex,
            epoch_
        );

        uint256 supply = VirtualRewarderCheckpoints.getAmount(totalSupplyCheckpoints, totalSupplyCheckpointLastIndex, epoch_);

        if (supply == 0) {
            return 0;
        }

        return (balance * rewardsPerEpoch[epoch_ + _WEEK]) / supply;
    }

    /**
     * @notice This function return current epoch
     * @dev Retrieves the current epoch
     *
     * @return The current epoch
     */
    function _currentEpoch() internal view returns (uint256) {
        return _roundToEpoch(block.timestamp);
    }

    /**
     * @notice This function is used to align timestamps with epoch boundaries.
     * @dev Rounds down the timestamp to the start of the epoch.
     *
     * @param timestamp_ The timestamp to round down.
     * @return The timestamp rounded down to the nearest epoch start.
     */
    function _roundToEpoch(uint256 timestamp_) internal pure returns (uint256) {
        return (timestamp_ / _WEEK) * _WEEK;
    }

    /**
     * @dev Checked provided address on zero value, throw AddressZero error in case when addr_ is zero
     *
     * @param addr_ The address which will checked on zero
     */
    function _checkAddressZero(address addr_) internal pure {
        if (addr_ == address(0)) {
            revert AddressZero();
        }
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/Address.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal 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;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 4 of 7 : IUgradeCall.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

interface IUpgradeCall {
    function upgradeCall() external;
}

File 5 of 7 : UpgradeCall.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;

import {IUpgradeCall} from "./interfaces/IUgradeCall.sol";

abstract contract UpgradeCall is IUpgradeCall {
    function upgradeCall() external virtual override {}
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/**
 * @title Interface for Single Token Virtual Rewarder
 * @dev Defines the basic interface for a reward system that handles deposits, withdrawals, and rewards based on token staking over different epochs.
 */
interface ISingelTokenVirtualRewarder {
    /**
     * @dev Emitted when a deposit is made.
     * @param tokenId The identifier of the token being deposited.
     * @param amount The amount of tokens deposited.
     * @param epoch The epoch during which the deposit occurs.
     */
    event Deposit(uint256 indexed tokenId, uint256 indexed amount, uint256 indexed epoch);

    /**
     * @dev Emitted when a withdrawal is made.
     * @param tokenId The identifier of the token being withdrawn.
     * @param amount The amount of tokens withdrawn.
     * @param epoch The epoch during which the withdrawal occurs.
     */
    event Withdraw(uint256 indexed tokenId, uint256 indexed amount, uint256 indexed epoch);

    /**
     * @dev Emitted when rewards are harvested.
     * @param tokenId The identifier of the token for which rewards are harvested.
     * @param rewardAmount The amount of rewards harvested.
     * @param epochCount The epoch during which the harvest occurs.
     */
    event Harvest(uint256 indexed tokenId, uint256 indexed rewardAmount, uint256 indexed epochCount);

    /**
     * @dev Emitted when a new reward amount is notified to be added to the pool.
     * @param rewardAmount The amount of rewards added.
     * @param epoch The epoch during which the reward is added.
     */
    event NotifyReward(uint256 indexed rewardAmount, uint256 indexed epoch);

    /**
     * @notice Handles the deposit of tokens into the reward system.
     * @param tokenId The identifier of the token being deposited.
     * @param amount The amount of tokens to deposit.
     */
    function deposit(uint256 tokenId, uint256 amount) external;

    /**
     * @notice Handles the withdrawal of tokens from the reward system.
     * @param tokenId The identifier of the token being withdrawn.
     * @param amount The amount of tokens to withdraw.
     */
    function withdraw(uint256 tokenId, uint256 amount) external;

    /**
     * @notice Notifies the system of a new reward amount to be distributed.
     * @param amount The amount of the new reward to add.
     */
    function notifyRewardAmount(uint256 amount) external;

    /**
     * @notice Harvests rewards for a specific token.
     * @param tokenId The identifier of the token for which to harvest rewards.
     * @return reward The amount of harvested rewards.
     */
    function harvest(uint256 tokenId) external returns (uint256 reward);

    /**
     * @notice Calculates the available amount of rewards for a specific token.
     * @param tokenId The identifier of the token.
     * @return reward The calculated reward amount.
     */
    function calculateAvailableRewardsAmount(uint256 tokenId) external view returns (uint256 reward);

    /**
     * @notice Returns the strategy address associated with this contract.
     * @return The address of the strategy.
     */
    function strategy() external view returns (address);

    /**
     * @notice Returns the total supply of tokens under management.
     * @return The total supply of tokens.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Returns the balance of a specific token.
     * @param tokenId The identifier of the token.
     * @return The balance of the specified token.
     */
    function balanceOf(uint256 tokenId) external view returns (uint256);

    /**
     * @notice Provides the balance of a specific tokenId at a given timestamp
     *
     * @param tokenId_ The ID of the token to check
     * @param timestamp_ The specific timestamp to check the balance at
     * @return The balance of the token at the given timestamp
     */
    function balanceOfAt(uint256 tokenId_, uint256 timestamp_) external view returns (uint256);

    /**
     * @notice Provides the total supply of tokens at a given timestamp
     *
     * @param timestamp_ The timestamp to check the total supply at
     * @return The total supply of tokens at the specified timestamp
     */
    function totalSupplyAt(uint256 timestamp_) external view returns (uint256);

    /**
     * @notice Returns the reward per epoch for a specific epoch.
     * @param epoch The epoch for which to retrieve the reward amount.
     * @return The reward amount for the specified epoch.
     */
    function rewardsPerEpoch(uint256 epoch) external view returns (uint256);

    /**
     * @notice Initializes the contract with necessary governance and operational addresses
     * @dev Sets up operational aspects of the contract. This function can only be called once.
     *
     * @param strategy_ The strategy address that will interact with this contract
     */
    function initialize(address strategy_) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.19;

/**
 * @title VirtualRewarderCheckpoints
 * @dev Library to manage checkpoints in a virtual reward system. This library facilitates the storage of state
 * at specific timestamps for historical data tracking and reward calculation.
 */
library VirtualRewarderCheckpoints {
    struct Checkpoint {
        uint256 timestamp; // Timestamp at which the checkpoint is logged
        uint256 amount; // Amount or value associated with the checkpoint
    }

    /**
     * @notice Writes a new checkpoint or updates an existing one in the mapping.
     * @dev If a checkpoint at the given timestamp already exists, it updates the amount; otherwise, it creates a new checkpoint.
     *
     * @param self_ Mapping from index to Checkpoint.
     * @param lastIndex_ Index of the last recorded checkpoint.
     * @param timestamp_ Timestamp for the new checkpoint.
     * @param amount_ Amount to be associated with the new checkpoint.
     * @return newIndex The index of the newly written checkpoint.
     *
     * Example:
     * mapping(uint256 => Checkpoint) checkpoints;
     * uint256 lastIndex = 0;
     * lastIndex = VirtualRewarderCheckpoints.writeCheckpoint(checkpoints, lastIndex, block.timestamp, 100);
     */
    function writeCheckpoint(
        mapping(uint256 index => Checkpoint checkpoint) storage self_,
        uint256 lastIndex_,
        uint256 timestamp_,
        uint256 amount_
    ) internal returns (uint256 newIndex) {
        Checkpoint memory last = self_[lastIndex_];

        newIndex = last.timestamp == timestamp_ ? lastIndex_ : lastIndex_ + 1;

        self_[newIndex] = Checkpoint({timestamp: timestamp_, amount: amount_});
    }

    /**
     * @notice Retrieves the amount at the checkpoint closest to and not after the given timestamp.
     *
     * @param self_ Mapping from index to Checkpoint.
     * @param lastIndex_ Index of the last checkpoint.
     * @param timestamp_ Timestamp for querying the amount.
     * @return amount The amount at the closest checkpoint.
     *
     * Example:
     * uint256 amount = VirtualRewarderCheckpoints.getAmount(checkpoints, lastIndex, block.timestamp);
     */
    function getAmount(
        mapping(uint256 index => Checkpoint checkpoint) storage self_,
        uint256 lastIndex_,
        uint256 timestamp_
    ) internal view returns (uint256) {
        return self_[getCheckpointIndex(self_, lastIndex_, timestamp_)].amount;
    }

    /**
     * @notice Retrieves the index of the checkpoint that is nearest to and less than or equal to the given timestamp.
     * @dev Performs a binary search to find the closest timestamp, which is efficient on sorted data.
     *
     * @param self_ Mapping from index to Checkpoint.
     * @param lastIndex_ Index of the last checkpoint.
     * @param timestamp_ Timestamp to query the nearest checkpoint for.
     * @return index The index of the closest checkpoint by timestamp.
     *
     * Example:
     * uint256 index = VirtualRewarderCheckpoints.getCheckpointIndex(checkpoints, lastIndex, block.timestamp - 10);
     */
    function getCheckpointIndex(
        mapping(uint256 index => Checkpoint checkpoint) storage self_,
        uint256 lastIndex_,
        uint256 timestamp_
    ) internal view returns (uint256) {
        if (lastIndex_ == 0) {
            return 0;
        }

        if (self_[lastIndex_].timestamp <= timestamp_) {
            return lastIndex_;
        }

        if (self_[0].timestamp > timestamp_) {
            return 0;
        }

        uint256 start;
        uint256 end = lastIndex_;
        while (end > start) {
            uint256 middle = end - (end - start) / 2;
            Checkpoint memory checkpoint = self_[middle];
            if (checkpoint.timestamp == timestamp_) {
                return middle;
            } else if (checkpoint.timestamp < timestamp_) {
                start = middle;
            } else {
                end = middle - 1;
            }
        }

        return start;
    }
}

Settings
{
  "evmVersion": "paris",
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 2000
  },
  "metadata": {
    "bytecodeHash": "none"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"epochCount","type":"uint256"}],"name":"Harvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"NotifyReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"balanceCheckpoints","outputs":[{"components":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct VirtualRewarderCheckpoints.Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"uint256","name":"timestamp_","type":"uint256"}],"name":"balanceOfAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"calculateAvailableRewardsAmount","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"harvest","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"rewardsPerEpoch","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokensInfo","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"checkpointLastIndex","type":"uint256"},{"internalType":"uint256","name":"lastEarnEpoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp_","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupplyCheckpointLastIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"totalSupplyCheckpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"upgradeCall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608080604052346100c1576000549060ff8260081c1661006f575060ff80821603610034575b604051610d0b90816100c78239f35b60ff90811916176000557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498602060405160ff8152a138610025565b62461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b6064820152608490fd5b600080fdfe608060408181526004908136101561001657600080fd5b600092833560e01c90816318160ddd146107d2575080633bb2fad9146107be5780633c6b16ab1461071c578063441a3e701461065c5780635c5f6c3d1461063d57806363db9f331461061a57806369448b6d146105dd57806371e1a8ac14610580578063853c8aeb1461054c57806394cee7b314610524578063981b24d0146104ee5780639cc7f708146104c7578063a8c62e7614610491578063b66e4cdf1461045f578063c4d66de814610231578063ddc63262146101965763e2bbb158146100df57600080fd5b34610192576100ed366107ee565b91909273ffffffffffffffffffffffffffffffffffffffff855460101c1633036101845782156101765783855260205283209161012b828454610809565b835561013982600154610809565b60015561014f61014842610afe565b809461086f565b7f33da4f9b82b3e18a281ca2cabbe2f076925692abb593b7ea3f850009e8ec97708480a480f35b9051631f2a200560e01b8152fd5b9051634ca8886760e01b8152fd5b8280fd5b5082903461022d57602060031936011261022d57823573ffffffffffffffffffffffffffffffffffffffff835460101c16330361021e57927f4ec2d4038813a7f233af1d6d09519189db3ed5bc5b823bf72f6d3144574721de91846101fc602096610979565b93849261020842610afe565b9483885288528460028289200155519580a48152f35b838251634ca8886760e01b8152fd5b5080fd5b50346101925760206003193601126101925781359073ffffffffffffffffffffffffffffffffffffffff82169283830361045b5784549360ff8560081c16159485809661044e575b8015610437575b156103b4578560017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008316178855610386575b501561035f575075ffffffffffffffffffffffffffffffffffffffff000084549260101b1692837fffffffffffffffffffff0000000000000000000000000000000000000000ffff8416178555610308578380f35b7f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498927fffffffffffffffffffff000000000000000000000000000000000000000000ff602093161784555160018152a13880808380f35b90517f9fabe1c1000000000000000000000000000000000000000000000000000000008152fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016610101178655386102b3565b60848360208651917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b50303b1580156102805750600160ff821614610280565b50600160ff821610610279565b8480fd5b509034610192576020600319360112610192579181923581526003602052206001815491015482519182526020820152f35b83823461022d578160031936011261022d5773ffffffffffffffffffffffffffffffffffffffff6020925460101c169051908152f35b50903461019257602060031936011261019257602092818392358252845220549051908152f35b509034610192576020600319360112610192578160209361051460019335600254610b35565b8152600385522001549051908152f35b5090346101925760206003193601126101925760209282913581526005845220549051908152f35b509034610192576105799082602094610564366107ee565b93908252865220600360018201549101610b18565b9051908152f35b50903461019257918192610593366107ee565b91908360206105a0610839565b82815201528352602052600383832001908252602052206105bf610839565b60206001835493848452015491019081528251918252516020820152f35b5090346101925760206003193601126101925760609281839235825260205220805491600260018301549201549181519384526020840152820152f35b50823461063a57602060031936011261063a575061057960209235610979565b80fd5b83823461022d578160031936011261022d576020906002549051908152f35b50346101925761066b366107ee565b9173ffffffffffffffffffffffffffffffffffffffff855460101c16330361070d57818552836020528085209384549182158015610705575b6106f8575050826106b49161082c565b83556106c28260015461082c565b6001556106d161014842610afe565b7fa01a72713bf837059e3a668d28f0de277fb7f24f2a4e95bf926703c95b5f12b28480a480f35b51631f2a200560e01b8152fd5b5084156106a4565b839051634ca8886760e01b8152fd5b5090346101925760206003193601126101925780359073ffffffffffffffffffffffffffffffffffffffff845460101c1633036107b05781156107a2575061076342610afe565b9182845260056020528320610779828254610809565b90557f0364fe27a9346f7a558ebdc4ce9cc58896d295d9db8b28960680ec6b0db571c98380a380f35b8251631f2a200560e01b8152fd5b8251634ca8886760e01b8152fd5b833461063a578060031936011261063a5780f35b84903461022d578160031936011261022d576020906001548152f35b6003196040910112610804576004359060243590565b600080fd5b9190820180921161081657565b634e487b7160e01b600052601160045260246000fd5b9190820391821161081657565b604051906040820182811067ffffffffffffffff82111761085957604052565b634e487b7160e01b600052604160045260246000fd5b90600382016001908184018054945490600092868452846020978289528760408720610899610839565b815480825291850154908c01520361095357935b6108b5610839565b92888452898401918252858752895260408620925183555191015555600254908254918082526003865284604083206108ec610839565b815480825291870154908901520361092957906003604092965b61090e610839565b96875280870194855287835252209251835551910155600255565b83810180911161093f5790600360409296610906565b602482634e487b7160e01b81526011600452fd5b908101809111610965578590936108ad565b602485634e487b7160e01b81526011600452fd5b9060009160009080825260049182602052604081209060019384830154928315610a6757600281015480948115600014610a5657505050845b84835281602052600360408420019083526020526040822054838111610a4e575b509193929062093a8092836109f0876109eb42610afe565b61082c565b049382935b858510610a06575050505050505050565b9091929394959698610a2290610a1c8b84610a71565b90610809565b98828101809111610a3b579695948601939291906109f5565b602485601186634e487b7160e01b835252fd5b9250386109d3565b6003610a629301610c13565b6109b2565b5090955050505050565b60009081526004602052610a918260408320600360018201549101610b18565b90610a9e83600254610b35565b815260036020526001604082200154928315610af75762093a80810180911161093f5781526005602052604081205491828102928184041490151715610ae357500490565b80634e487b7160e01b602492526011600452fd5b5091505090565b62093a808091048181029181830414901517156108165790565b91610b239183610c13565b60005260205260016040600020015490565b908115610c0c5760008281526003916020838152604082818520541115610c0457838052828185205411610bfb579392919082955b868111610b7a5750505050505090565b610b84878261082c565b96610b93600198891c8361082c565b978886528684528786209084610ba7610839565b918354938484520154910152848114600014610bc857505050505050505090565b93809596979892939410600014610be65750955b9493929190610b6a565b96915060001981019081116109655790610bdc565b50505091505090565b505050505090565b5050600090565b90918215610cf657600083815260209183835260409281848420541115610c0457828052818484205411610ced57939291908195945b868611610c595750505050505090565b610c63878761082c565b96610c72600198891c8861082c565b80988186528784528686209084610c87610839565b918354938484520154910152848114600014610ca95750505050505050505090565b93809596979899939410600014610cc8575050955b9493929190610c49565b90975060001981019150811115610cbe57602484634e487b7160e01b81526011600452fd5b50509250505090565b50505060009056fea164736f6c6343000813000a

Deployed Bytecode

0x608060408181526004908136101561001657600080fd5b600092833560e01c90816318160ddd146107d2575080633bb2fad9146107be5780633c6b16ab1461071c578063441a3e701461065c5780635c5f6c3d1461063d57806363db9f331461061a57806369448b6d146105dd57806371e1a8ac14610580578063853c8aeb1461054c57806394cee7b314610524578063981b24d0146104ee5780639cc7f708146104c7578063a8c62e7614610491578063b66e4cdf1461045f578063c4d66de814610231578063ddc63262146101965763e2bbb158146100df57600080fd5b34610192576100ed366107ee565b91909273ffffffffffffffffffffffffffffffffffffffff855460101c1633036101845782156101765783855260205283209161012b828454610809565b835561013982600154610809565b60015561014f61014842610afe565b809461086f565b7f33da4f9b82b3e18a281ca2cabbe2f076925692abb593b7ea3f850009e8ec97708480a480f35b9051631f2a200560e01b8152fd5b9051634ca8886760e01b8152fd5b8280fd5b5082903461022d57602060031936011261022d57823573ffffffffffffffffffffffffffffffffffffffff835460101c16330361021e57927f4ec2d4038813a7f233af1d6d09519189db3ed5bc5b823bf72f6d3144574721de91846101fc602096610979565b93849261020842610afe565b9483885288528460028289200155519580a48152f35b838251634ca8886760e01b8152fd5b5080fd5b50346101925760206003193601126101925781359073ffffffffffffffffffffffffffffffffffffffff82169283830361045b5784549360ff8560081c16159485809661044e575b8015610437575b156103b4578560017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008316178855610386575b501561035f575075ffffffffffffffffffffffffffffffffffffffff000084549260101b1692837fffffffffffffffffffff0000000000000000000000000000000000000000ffff8416178555610308578380f35b7f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498927fffffffffffffffffffff000000000000000000000000000000000000000000ff602093161784555160018152a13880808380f35b90517f9fabe1c1000000000000000000000000000000000000000000000000000000008152fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016610101178655386102b3565b60848360208651917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152fd5b50303b1580156102805750600160ff821614610280565b50600160ff821610610279565b8480fd5b509034610192576020600319360112610192579181923581526003602052206001815491015482519182526020820152f35b83823461022d578160031936011261022d5773ffffffffffffffffffffffffffffffffffffffff6020925460101c169051908152f35b50903461019257602060031936011261019257602092818392358252845220549051908152f35b509034610192576020600319360112610192578160209361051460019335600254610b35565b8152600385522001549051908152f35b5090346101925760206003193601126101925760209282913581526005845220549051908152f35b509034610192576105799082602094610564366107ee565b93908252865220600360018201549101610b18565b9051908152f35b50903461019257918192610593366107ee565b91908360206105a0610839565b82815201528352602052600383832001908252602052206105bf610839565b60206001835493848452015491019081528251918252516020820152f35b5090346101925760206003193601126101925760609281839235825260205220805491600260018301549201549181519384526020840152820152f35b50823461063a57602060031936011261063a575061057960209235610979565b80fd5b83823461022d578160031936011261022d576020906002549051908152f35b50346101925761066b366107ee565b9173ffffffffffffffffffffffffffffffffffffffff855460101c16330361070d57818552836020528085209384549182158015610705575b6106f8575050826106b49161082c565b83556106c28260015461082c565b6001556106d161014842610afe565b7fa01a72713bf837059e3a668d28f0de277fb7f24f2a4e95bf926703c95b5f12b28480a480f35b51631f2a200560e01b8152fd5b5084156106a4565b839051634ca8886760e01b8152fd5b5090346101925760206003193601126101925780359073ffffffffffffffffffffffffffffffffffffffff845460101c1633036107b05781156107a2575061076342610afe565b9182845260056020528320610779828254610809565b90557f0364fe27a9346f7a558ebdc4ce9cc58896d295d9db8b28960680ec6b0db571c98380a380f35b8251631f2a200560e01b8152fd5b8251634ca8886760e01b8152fd5b833461063a578060031936011261063a5780f35b84903461022d578160031936011261022d576020906001548152f35b6003196040910112610804576004359060243590565b600080fd5b9190820180921161081657565b634e487b7160e01b600052601160045260246000fd5b9190820391821161081657565b604051906040820182811067ffffffffffffffff82111761085957604052565b634e487b7160e01b600052604160045260246000fd5b90600382016001908184018054945490600092868452846020978289528760408720610899610839565b815480825291850154908c01520361095357935b6108b5610839565b92888452898401918252858752895260408620925183555191015555600254908254918082526003865284604083206108ec610839565b815480825291870154908901520361092957906003604092965b61090e610839565b96875280870194855287835252209251835551910155600255565b83810180911161093f5790600360409296610906565b602482634e487b7160e01b81526011600452fd5b908101809111610965578590936108ad565b602485634e487b7160e01b81526011600452fd5b9060009160009080825260049182602052604081209060019384830154928315610a6757600281015480948115600014610a5657505050845b84835281602052600360408420019083526020526040822054838111610a4e575b509193929062093a8092836109f0876109eb42610afe565b61082c565b049382935b858510610a06575050505050505050565b9091929394959698610a2290610a1c8b84610a71565b90610809565b98828101809111610a3b579695948601939291906109f5565b602485601186634e487b7160e01b835252fd5b9250386109d3565b6003610a629301610c13565b6109b2565b5090955050505050565b60009081526004602052610a918260408320600360018201549101610b18565b90610a9e83600254610b35565b815260036020526001604082200154928315610af75762093a80810180911161093f5781526005602052604081205491828102928184041490151715610ae357500490565b80634e487b7160e01b602492526011600452fd5b5091505090565b62093a808091048181029181830414901517156108165790565b91610b239183610c13565b60005260205260016040600020015490565b908115610c0c5760008281526003916020838152604082818520541115610c0457838052828185205411610bfb579392919082955b868111610b7a5750505050505090565b610b84878261082c565b96610b93600198891c8361082c565b978886528684528786209084610ba7610839565b918354938484520154910152848114600014610bc857505050505050505090565b93809596979892939410600014610be65750955b9493929190610b6a565b96915060001981019081116109655790610bdc565b50505091505090565b505050505090565b5050600090565b90918215610cf657600083815260209183835260409281848420541115610c0457828052818484205411610ced57939291908195945b868611610c595750505050505090565b610c63878761082c565b96610c72600198891c8861082c565b80988186528784528686209084610c87610839565b918354938484520154910152848114600014610ca95750505050505050505090565b93809596979899939410600014610cc8575050955b9493929190610c49565b90975060001981019150811115610cbe57602484634e487b7160e01b81526011600452fd5b50509250505090565b50505060009056fea164736f6c6343000813000a

Block Transaction Gas Used Reward
view all blocks ##produced##

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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.