MATIC Price: $0.538094 (+3.10%)
Gas: 30.9 GWei
 

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Notify Reward Am...399444032023-03-04 6:10:05504 days ago1677910205IN
0xd3A24D60...BC7301e20
0 MATIC0.02080893143
Notify Reward Am...398306662023-03-01 5:13:55507 days ago1677647635IN
0xd3A24D60...BC7301e20
0 MATIC0.03618583217
Notify Reward Am...308880202022-07-19 6:30:29732 days ago1658212229IN
0xd3A24D60...BC7301e20
0 MATIC0.0065482645
Notify Reward Am...308879892022-07-19 6:29:23732 days ago1658212163IN
0xd3A24D60...BC7301e20
0 MATIC0.0068896345
Notify Reward Am...302243252022-07-01 19:19:07749 days ago1656703147IN
0xd3A24D60...BC7301e20
0 MATIC0.0153079100
Notify Reward Am...298551692022-06-22 4:52:59759 days ago1655873579IN
0xd3A24D60...BC7301e20
0 MATIC0.0153079100
Notify Reward Am...294616612022-06-12 3:43:48769 days ago1655005428IN
0xd3A24D60...BC7301e20
0 MATIC0.0153091100

Latest 1 internal transaction

Parent Transaction Hash Block From To
284401362022-05-17 19:19:33794 days ago1652815173  Contract Creation0 MATIC
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x76ff8510...5Cec85C10
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
Bribe

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 14 : Bribe.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

import "../../interface/IBribe.sol";
import "../../interface/IERC721.sol";
import "../../interface/IVoter.sol";
import "../../interface/IVe.sol";
import "./MultiRewardsPoolBase.sol";

/// @title Bribes pay out rewards for a given pool based on the votes
///        that were received from the user (goes hand in hand with Gauges.vote())
contract Bribe is IBribe, MultiRewardsPoolBase {

  /// @dev Only voter can modify balances (since it only happens on vote())
  address public immutable voter;
  address public immutable ve;

  // Assume that will be created from voter contract through factory
  constructor(
    address _voter,
    address[] memory _allowedRewardTokens
  ) MultiRewardsPoolBase(address(0), _voter, _allowedRewardTokens) {
    voter = _voter;
    ve = IVoter(_voter).ve();
  }

  function getReward(uint tokenId, address[] memory tokens) external {
    require(IVe(ve).isApprovedOrOwner(msg.sender, tokenId), "Not token owner");
    _getReward(_tokenIdToAddress(tokenId), tokens, msg.sender);
  }

  /// @dev Used by Voter to allow batched reward claims
  function getRewardForOwner(uint tokenId, address[] memory tokens) external override {
    require(msg.sender == voter, "Not voter");
    address owner = IERC721(ve).ownerOf(tokenId);
    _getReward(_tokenIdToAddress(tokenId), tokens, owner);
  }

  /// @dev This is an external function, but internal notation is used
  ///      since it can only be called "internally" from Gauges
  function _deposit(uint amount, uint tokenId) external override {
    require(msg.sender == voter, "Not voter");
    require(amount > 0, "Zero amount");

    address adr = _tokenIdToAddress(tokenId);
    _increaseBalance(adr, amount);
    emit Deposit(adr, amount);
  }

  function _withdraw(uint amount, uint tokenId) external override {
    require(msg.sender == voter, "Not voter");
    require(amount > 0, "Zero amount");

    address adr = _tokenIdToAddress(tokenId);
    _decreaseBalance(adr, amount);
    emit Withdraw(adr, amount);
  }

  /// @dev Used to notify a gauge/bribe of a given reward,
  ///      this can create griefing attacks by extending rewards
  function notifyRewardAmount(address token, uint amount) external override {
    _notifyRewardAmount(token, amount);
  }

  // use tokenId instead of address for

  function tokenIdToAddress(uint tokenId) external pure returns (address) {
    return _tokenIdToAddress(tokenId);
  }

  function _tokenIdToAddress(uint tokenId) internal pure returns (address) {
    address adr = address(uint160(tokenId));
    require(_addressToTokenId(adr) == tokenId, "Wrong convert");
    return adr;
  }

  function addressToTokenId(address adr) external pure returns (uint) {
    return _addressToTokenId(adr);
  }

  function _addressToTokenId(address adr) internal pure returns (uint) {
    return uint(uint160(adr));
  }

}

File 2 of 14 : IBribe.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

interface IBribe {

  function notifyRewardAmount(address token, uint amount) external;

  function _deposit(uint amount, uint tokenId) external;

  function _withdraw(uint amount, uint tokenId) external;

  function getRewardForOwner(uint tokenId, address[] memory tokens) external;

}

File 3 of 14 : IERC721.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

import "./IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
  /**
   * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
   */
  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

  /**
   * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
   */
  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

  /**
   * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
   */
  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

  /**
   * @dev Returns the number of tokens in ``owner``'s account.
   */
  function balanceOf(address owner) external view returns (uint256 balance);

  /**
   * @dev Returns the owner of the `tokenId` token.
   *
   * Requirements:
   *
   * - `tokenId` must exist.
   */
  function ownerOf(uint256 tokenId) external view returns (address owner);

  /**
   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
   *
   * Requirements:
   *
   * - `from` cannot be the zero address.
   * - `to` cannot be the zero address.
   * - `tokenId` token must exist and be owned by `from`.
   * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
   *
   * Emits a {Transfer} event.
   */
  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId
  ) external;

  /**
   * @dev Transfers `tokenId` token from `from` to `to`.
   *
   * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
   *
   * Requirements:
   *
   * - `from` cannot be the zero address.
   * - `to` cannot be the zero address.
   * - `tokenId` token must be owned by `from`.
   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
   *
   * Emits a {Transfer} event.
   */
  function transferFrom(
    address from,
    address to,
    uint256 tokenId
  ) external;

  /**
   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
   * The approval is cleared when the token is transferred.
   *
   * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
   *
   * Requirements:
   *
   * - The caller must own the token or be an approved operator.
   * - `tokenId` must exist.
   *
   * Emits an {Approval} event.
   */
  function approve(address to, uint256 tokenId) external;

  /**
   * @dev Returns the account approved for `tokenId` token.
   *
   * Requirements:
   *
   * - `tokenId` must exist.
   */
  function getApproved(uint256 tokenId) external view returns (address operator);

  /**
   * @dev Approve or remove `operator` as an operator for the caller.
   * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
   *
   * Requirements:
   *
   * - The `operator` cannot be the caller.
   *
   * Emits an {ApprovalForAll} event.
   */
  function setApprovalForAll(address operator, bool _approved) external;

  /**
   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
   *
   * See {setApprovalForAll}
   */
  function isApprovedForAll(address owner, address operator) external view returns (bool);

  /**
   * @dev Safely transfers `tokenId` token from `from` to `to`.
   *
   * Requirements:
   *
   * - `from` cannot be the zero address.
   * - `to` cannot be the zero address.
   * - `tokenId` token must exist and be owned by `from`.
   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
   *
   * Emits a {Transfer} event.
   */
  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId,
    bytes calldata data
  ) external;
}

File 4 of 14 : IVoter.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

interface IVoter {

  function ve() external view returns (address);

  function attachTokenToGauge(uint _tokenId, address account) external;

  function detachTokenFromGauge(uint _tokenId, address account) external;

  function emitDeposit(uint _tokenId, address account, uint amount) external;

  function emitWithdraw(uint _tokenId, address account, uint amount) external;

  function distribute(address _gauge) external;

  function notifyRewardAmount(uint amount) external;

}

File 5 of 14 : IVe.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

interface IVe {

  enum DepositType {
    DEPOSIT_FOR_TYPE,
    CREATE_LOCK_TYPE,
    INCREASE_LOCK_AMOUNT,
    INCREASE_UNLOCK_TIME,
    MERGE_TYPE
  }

  struct Point {
    int128 bias;
    int128 slope; // # -dweight / dt
    uint ts;
    uint blk; // block
  }
  /* We cannot really do block numbers per se b/c slope is per time, not per block
  * and per block could be fairly bad b/c Ethereum changes blocktimes.
  * What we can do is to extrapolate ***At functions */

  struct LockedBalance {
    int128 amount;
    uint end;
  }

  function token() external view returns (address);

  function balanceOfNFT(uint) external view returns (uint);

  function isApprovedOrOwner(address, uint) external view returns (bool);

  function createLockFor(uint, uint, address) external returns (uint);

  function userPointEpoch(uint tokenId) external view returns (uint);

  function epoch() external view returns (uint);

  function userPointHistory(uint tokenId, uint loc) external view returns (Point memory);

  function pointHistory(uint loc) external view returns (Point memory);

  function checkpoint() external;

  function depositFor(uint tokenId, uint value) external;

  function attachToken(uint tokenId) external;

  function detachToken(uint tokenId) external;

  function voting(uint tokenId) external;

  function abstain(uint tokenId) external;
}

File 6 of 14 : MultiRewardsPoolBase.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

import "../../interface/IERC20.sol";
import "../../interface/IMultiRewardsPool.sol";
import "../../lib/Math.sol";
import "../../lib/SafeERC20.sol";
import "../../lib/CheckpointLib.sol";
import "../Reentrancy.sol";

abstract contract MultiRewardsPoolBase is Reentrancy, IMultiRewardsPool {
  using SafeERC20 for IERC20;
  using CheckpointLib for mapping(uint => CheckpointLib.Checkpoint);

  /// @dev Operator can add/remove reward tokens
  address public operator;

  /// @dev The LP token that needs to be staked for rewards
  address public immutable override underlying;

  uint public override derivedSupply;
  mapping(address => uint) public override derivedBalances;

  /// @dev Rewards are released over 7 days
  uint internal constant DURATION = 7 days;
  uint internal constant PRECISION = 10 ** 18;
  uint internal constant MAX_REWARD_TOKENS = 10;

  /// Default snx staking contract implementation
  /// https://github.com/Synthetixio/synthetix/blob/develop/contracts/StakingRewards.sol

  /// @dev Reward rate with precision 1e18
  mapping(address => uint) public rewardRate;
  mapping(address => uint) public periodFinish;
  mapping(address => uint) public lastUpdateTime;
  mapping(address => uint) public rewardPerTokenStored;

  mapping(address => mapping(address => uint)) public lastEarn;
  mapping(address => mapping(address => uint)) public userRewardPerTokenStored;

  uint public override totalSupply;
  mapping(address => uint) public override balanceOf;

  address[] public override rewardTokens;
  mapping(address => bool) public override isRewardToken;

  /// @notice A record of balance checkpoints for each account, by index
  mapping(address => mapping(uint => CheckpointLib.Checkpoint)) public checkpoints;
  /// @notice The number of checkpoints for each account
  mapping(address => uint) public numCheckpoints;
  /// @notice A record of balance checkpoints for each token, by index
  mapping(uint => CheckpointLib.Checkpoint) public supplyCheckpoints;
  /// @notice The number of checkpoints
  uint public supplyNumCheckpoints;
  /// @notice A record of balance checkpoints for each token, by index
  mapping(address => mapping(uint => CheckpointLib.Checkpoint)) public rewardPerTokenCheckpoints;
  /// @notice The number of checkpoints for each token
  mapping(address => uint) public rewardPerTokenNumCheckpoints;

  event Deposit(address indexed from, uint amount);
  event Withdraw(address indexed from, uint amount);
  event NotifyReward(address indexed from, address indexed reward, uint amount);
  event ClaimRewards(address indexed from, address indexed reward, uint amount, address recepient);

  constructor(address _stake, address _operator, address[] memory _allowedRewardTokens) {
    underlying = _stake;
    operator = _operator;
    for (uint i; i < _allowedRewardTokens.length; i++) {
      if (_allowedRewardTokens[i] != address(0)) {
        _registerRewardToken(_allowedRewardTokens[i]);
      }
    }
  }

  modifier onlyOperator() {
    require(msg.sender == operator, "Not operator");
    _;
  }

  //**************************************************************************
  //************************ VIEWS *******************************************
  //**************************************************************************

  function rewardTokensLength() external view override returns (uint) {
    return rewardTokens.length;
  }

  function rewardPerToken(address token) external view returns (uint) {
    return _rewardPerToken(token);
  }

  function _rewardPerToken(address token) internal view returns (uint) {
    if (derivedSupply == 0) {
      return rewardPerTokenStored[token];
    }
    return rewardPerTokenStored[token]
    + (
    (_lastTimeRewardApplicable(token) - Math.min(lastUpdateTime[token], periodFinish[token]))
    * rewardRate[token]
    / derivedSupply
    );
  }

  function derivedBalance(address account) external view override returns (uint) {
    return _derivedBalance(account);
  }

  function left(address token) external view override returns (uint) {
    if (block.timestamp >= periodFinish[token]) return 0;
    uint _remaining = periodFinish[token] - block.timestamp;
    return _remaining * rewardRate[token] / PRECISION;
  }

  function earned(address token, address account) external view override returns (uint) {
    return _earned(token, account);
  }

  //**************************************************************************
  //************************ OPERATOR ACTIONS ********************************
  //**************************************************************************

  function registerRewardToken(address token) external onlyOperator {
    _registerRewardToken(token);
  }

  function _registerRewardToken(address token) internal {
    require(rewardTokens.length < MAX_REWARD_TOKENS, "Too many reward tokens");
    require(!isRewardToken[token], "Already registered");
    isRewardToken[token] = true;
    rewardTokens.push(token);
  }

  function removeRewardToken(address token) external onlyOperator {
    require(periodFinish[token] < block.timestamp, "Rewards not ended");
    require(isRewardToken[token], "Not reward token");

    isRewardToken[token] = false;
    uint length = rewardTokens.length;
    require(length > 3, "First 3 tokens should not be removed");
    // keep 3 tokens as guarantee against malicious actions
    // assume it will be DYST + pool tokens
    uint i = 3;
    bool found = false;
    for (; i < length; i++) {
      address t = rewardTokens[i];
      if (t == token) {
        found = true;
        break;
      }
    }
    require(found, "First tokens forbidden to remove");
    rewardTokens[i] = rewardTokens[length - 1];
    rewardTokens.pop();
  }

  //**************************************************************************
  //************************ USER ACTIONS ************************************
  //**************************************************************************

  function _deposit(uint amount) internal virtual lock {
    require(amount > 0, "Zero amount");
    _increaseBalance(msg.sender, amount);
    IERC20(underlying).safeTransferFrom(msg.sender, address(this), amount);
    emit Deposit(msg.sender, amount);
  }

  function _increaseBalance(address account, uint amount) internal virtual {
    _updateRewardForAllTokens();

    totalSupply += amount;
    balanceOf[account] += amount;

    _updateDerivedBalanceAndWriteCheckpoints(account);
  }

  function _withdraw(uint amount) internal lock virtual {
    _decreaseBalance(msg.sender, amount);
    IERC20(underlying).safeTransfer(msg.sender, amount);
    emit Withdraw(msg.sender, amount);
  }

  function _decreaseBalance(address account, uint amount) internal virtual {
    _updateRewardForAllTokens();

    totalSupply -= amount;
    balanceOf[account] -= amount;

    _updateDerivedBalanceAndWriteCheckpoints(account);
  }

  /// @dev Implement restriction checks!
  function _getReward(address account, address[] memory tokens, address recipient) internal lock virtual {

    for (uint i = 0; i < tokens.length; i++) {
      (rewardPerTokenStored[tokens[i]], lastUpdateTime[tokens[i]]) = _updateRewardPerToken(tokens[i], type(uint).max, true);

      uint _reward = _earned(tokens[i], account);
      lastEarn[tokens[i]][account] = block.timestamp;
      userRewardPerTokenStored[tokens[i]][account] = rewardPerTokenStored[tokens[i]];
      if (_reward > 0) {
        IERC20(tokens[i]).safeTransfer(recipient, _reward);
      }

      emit ClaimRewards(msg.sender, tokens[i], _reward, recipient);
    }

    _updateDerivedBalanceAndWriteCheckpoints(account);
  }

  function _updateDerivedBalanceAndWriteCheckpoints(address account) internal {
    uint __derivedBalance = derivedBalances[account];
    derivedSupply -= __derivedBalance;
    __derivedBalance = _derivedBalance(account);
    derivedBalances[account] = __derivedBalance;
    derivedSupply += __derivedBalance;

    _writeCheckpoint(account, __derivedBalance);
    _writeSupplyCheckpoint();
  }

  //**************************************************************************
  //************************ REWARDS CALCULATIONS ****************************
  //**************************************************************************

  // earned is an estimation, it won't be exact till the supply > rewardPerToken calculations have run
  function _earned(address token, address account) internal view returns (uint) {
    // zero checkpoints means zero deposits
    if (numCheckpoints[account] == 0) {
      return 0;
    }
    // last claim rewards time
    uint _startTimestamp = Math.max(lastEarn[token][account], rewardPerTokenCheckpoints[token][0].timestamp);

    // find an index of the balance that the user had on the last claim
    uint _startIndex = _getPriorBalanceIndex(account, _startTimestamp);
    uint _endIndex = numCheckpoints[account] - 1;

    uint reward = 0;

    // calculate previous snapshots if exist
    if (_endIndex > 0) {
      for (uint i = _startIndex; i <= _endIndex - 1; i++) {
        CheckpointLib.Checkpoint memory cp0 = checkpoints[account][i];
        CheckpointLib.Checkpoint memory cp1 = checkpoints[account][i + 1];
        (uint _rewardPerTokenStored0,) = _getPriorRewardPerToken(token, cp0.timestamp);
        (uint _rewardPerTokenStored1,) = _getPriorRewardPerToken(token, cp1.timestamp);
        reward += cp0.value * (_rewardPerTokenStored1 - _rewardPerTokenStored0) / PRECISION;
      }
    }

    CheckpointLib.Checkpoint memory cp = checkpoints[account][_endIndex];
    (uint _rewardPerTokenStored,) = _getPriorRewardPerToken(token, cp.timestamp);
    reward += cp.value * (_rewardPerToken(token) - Math.max(_rewardPerTokenStored, userRewardPerTokenStored[token][account])) / PRECISION;
    return reward;
  }

  function _derivedBalance(address account) internal virtual view returns (uint) {
    // supposed to be implemented in a parent contract
    return balanceOf[account];
  }

  /// @dev Update stored rewardPerToken values without the last one snapshot
  ///      If the contract will get "out of gas" error on users actions this will be helpful
  function batchUpdateRewardPerToken(address token, uint maxRuns) external {
    (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token, maxRuns, false);
  }

  function _updateRewardForAllTokens() internal {
    uint length = rewardTokens.length;
    for (uint i; i < length; i++) {
      address token = rewardTokens[i];
      (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token, type(uint).max, true);
    }
  }

  /// @dev Should be called only with properly updated snapshots, or with actualLast=false
  function _updateRewardPerToken(address token, uint maxRuns, bool actualLast) internal returns (uint, uint) {
    uint _startTimestamp = lastUpdateTime[token];
    uint reward = rewardPerTokenStored[token];

    if (supplyNumCheckpoints == 0) {
      return (reward, _startTimestamp);
    }

    if (rewardRate[token] == 0) {
      return (reward, block.timestamp);
    }
    uint _startIndex = _getPriorSupplyIndex(_startTimestamp);
    uint _endIndex = Math.min(supplyNumCheckpoints - 1, maxRuns);

    if (_endIndex > 0) {
      for (uint i = _startIndex; i <= _endIndex - 1; i++) {
        CheckpointLib.Checkpoint memory sp0 = supplyCheckpoints[i];
        if (sp0.value > 0) {
          CheckpointLib.Checkpoint memory sp1 = supplyCheckpoints[i + 1];
          (uint _reward, uint _endTime) = _calcRewardPerToken(
            token,
            sp1.timestamp,
            sp0.timestamp,
            sp0.value,
            _startTimestamp
          );
          reward += _reward;
          _writeRewardPerTokenCheckpoint(token, reward, _endTime);
          _startTimestamp = _endTime;
        }
      }
    }

    // need to override the last value with actual numbers only on deposit/withdraw/claim/notify actions
    if (actualLast) {
      CheckpointLib.Checkpoint memory sp = supplyCheckpoints[_endIndex];
      if (sp.value > 0) {
        (uint _reward,) = _calcRewardPerToken(token, _lastTimeRewardApplicable(token), Math.max(sp.timestamp, _startTimestamp), sp.value, _startTimestamp);
        reward += _reward;
        _writeRewardPerTokenCheckpoint(token, reward, block.timestamp);
        _startTimestamp = block.timestamp;
      }
    }

    return (reward, _startTimestamp);
  }

  function _calcRewardPerToken(
    address token,
    uint lastSupplyTs1,
    uint lastSupplyTs0,
    uint supply,
    uint startTimestamp
  ) internal view returns (uint, uint) {
    uint endTime = Math.max(lastSupplyTs1, startTimestamp);
    uint _periodFinish = periodFinish[token];
    return (
    (Math.min(endTime, _periodFinish) - Math.min(Math.max(lastSupplyTs0, startTimestamp), _periodFinish))
    * rewardRate[token] / supply
    , endTime);
  }

  /// @dev Returns the last time the reward was modified or periodFinish if the reward has ended
  function _lastTimeRewardApplicable(address token) internal view returns (uint) {
    return Math.min(block.timestamp, periodFinish[token]);
  }

  //**************************************************************************
  //************************ NOTIFY ******************************************
  //**************************************************************************

  function _notifyRewardAmount(address token, uint amount) internal lock virtual {
    require(token != underlying, "Wrong token for rewards");
    require(amount > 0, "Zero amount");
    require(isRewardToken[token], "Token not allowed");
    if (rewardRate[token] == 0) {
      _writeRewardPerTokenCheckpoint(token, 0, block.timestamp);
    }
    (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token, type(uint).max, true);

    if (block.timestamp >= periodFinish[token]) {
      IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
      rewardRate[token] = amount * PRECISION / DURATION;
    } else {
      uint _remaining = periodFinish[token] - block.timestamp;
      uint _left = _remaining * rewardRate[token];
      // not sure what the reason was in the original solidly implementation for this restriction
      // however, by design probably it is a good idea against human errors
      require(amount > _left / PRECISION, "Amount should be higher than remaining rewards");
      IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
      rewardRate[token] = (amount * PRECISION + _left) / DURATION;
    }

    periodFinish[token] = block.timestamp + DURATION;
    emit NotifyReward(msg.sender, token, amount);
  }

  //**************************************************************************
  //************************ CHECKPOINTS *************************************
  //**************************************************************************

  function getPriorBalanceIndex(address account, uint timestamp) external view returns (uint) {
    return _getPriorBalanceIndex(account, timestamp);
  }

  /// @notice Determine the prior balance for an account as of a block number
  /// @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
  /// @param account The address of the account to check
  /// @param timestamp The timestamp to get the balance at
  /// @return The balance the account had as of the given block
  function _getPriorBalanceIndex(address account, uint timestamp) internal view returns (uint) {
    uint nCheckpoints = numCheckpoints[account];
    if (nCheckpoints == 0) {
      return 0;
    }
    return checkpoints[account].findLowerIndex(nCheckpoints, timestamp);
  }

  function getPriorSupplyIndex(uint timestamp) external view returns (uint) {
    return _getPriorSupplyIndex(timestamp);
  }

  function _getPriorSupplyIndex(uint timestamp) internal view returns (uint) {
    uint nCheckpoints = supplyNumCheckpoints;
    if (nCheckpoints == 0) {
      return 0;
    }
    return supplyCheckpoints.findLowerIndex(nCheckpoints, timestamp);
  }

  function getPriorRewardPerToken(address token, uint timestamp) external view returns (uint, uint) {
    return _getPriorRewardPerToken(token, timestamp);
  }

  function _getPriorRewardPerToken(address token, uint timestamp) internal view returns (uint, uint) {
    uint nCheckpoints = rewardPerTokenNumCheckpoints[token];
    if (nCheckpoints == 0) {
      return (0, 0);
    }
    mapping(uint => CheckpointLib.Checkpoint) storage cps = rewardPerTokenCheckpoints[token];
    uint lower = cps.findLowerIndex(nCheckpoints, timestamp);
    CheckpointLib.Checkpoint memory cp = cps[lower];
    return (cp.value, cp.timestamp);
  }

  function _writeCheckpoint(address account, uint balance) internal {
    uint _timestamp = block.timestamp;
    uint _nCheckPoints = numCheckpoints[account];

    if (_nCheckPoints > 0 && checkpoints[account][_nCheckPoints - 1].timestamp == _timestamp) {
      checkpoints[account][_nCheckPoints - 1].value = balance;
    } else {
      checkpoints[account][_nCheckPoints] = CheckpointLib.Checkpoint(_timestamp, balance);
      numCheckpoints[account] = _nCheckPoints + 1;
    }
  }

  function _writeRewardPerTokenCheckpoint(address token, uint reward, uint timestamp) internal {
    uint _nCheckPoints = rewardPerTokenNumCheckpoints[token];

    if (_nCheckPoints > 0 && rewardPerTokenCheckpoints[token][_nCheckPoints - 1].timestamp == timestamp) {
      rewardPerTokenCheckpoints[token][_nCheckPoints - 1].value = reward;
    } else {
      rewardPerTokenCheckpoints[token][_nCheckPoints] = CheckpointLib.Checkpoint(timestamp, reward);
      rewardPerTokenNumCheckpoints[token] = _nCheckPoints + 1;
    }
  }

  function _writeSupplyCheckpoint() internal {
    uint _nCheckPoints = supplyNumCheckpoints;
    uint _timestamp = block.timestamp;

    if (_nCheckPoints > 0 && supplyCheckpoints[_nCheckPoints - 1].timestamp == _timestamp) {
      supplyCheckpoints[_nCheckPoints - 1].value = derivedSupply;
    } else {
      supplyCheckpoints[_nCheckPoints] = CheckpointLib.Checkpoint(_timestamp, derivedSupply);
      supplyNumCheckpoints = _nCheckPoints + 1;
    }
  }
}

File 7 of 14 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
  /**
   * @dev Returns true if this contract implements the interface defined by
   * `interfaceId`. See the corresponding
   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
   * to learn more about how these ids are created.
   *
   * This function call must use less than 30 000 gas.
   */
  function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 8 of 14 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
  /**
   * @dev Returns the amount of tokens in existence.
   */
  function totalSupply() external view returns (uint256);

  /**
   * @dev Returns the amount of tokens owned by `account`.
   */
  function balanceOf(address account) external view returns (uint256);

  /**
   * @dev Moves `amount` tokens from the caller's account to `recipient`.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * Emits a {Transfer} event.
   */
  function transfer(address recipient, uint256 amount) external returns (bool);

  /**
   * @dev Returns the remaining number of tokens that `spender` will be
   * allowed to spend on behalf of `owner` through {transferFrom}. This is
   * zero by default.
   *
   * This value changes when {approve} or {transferFrom} are called.
   */
  function allowance(address owner, address spender) external view returns (uint256);

  /**
   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * IMPORTANT: Beware that changing an allowance with this method brings the risk
   * that someone may use both the old and the new allowance by unfortunate
   * transaction ordering. One possible solution to mitigate this race
   * condition is to first reduce the spender's allowance to 0 and set the
   * desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   *
   * Emits an {Approval} event.
   */
  function approve(address spender, uint256 amount) external returns (bool);

  /**
   * @dev Moves `amount` tokens from `sender` to `recipient` using the
   * allowance mechanism. `amount` is then deducted from the caller's
   * allowance.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * Emits a {Transfer} event.
   */
  function transferFrom(
    address sender,
    address recipient,
    uint256 amount
  ) external returns (bool);

  /**
   * @dev Emitted when `value` tokens are moved from one account (`from`) to
   * another (`to`).
   *
   * Note that `value` may be zero.
   */
  event Transfer(address indexed from, address indexed to, uint256 value);

  /**
   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
   * a call to {approve}. `value` is the new allowance.
   */
  event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 9 of 14 : IMultiRewardsPool.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

interface IMultiRewardsPool {

  function underlying() external view returns (address);

  function derivedSupply() external view returns (uint);

  function derivedBalances(address account) external view returns (uint);

  function totalSupply() external view returns (uint);

  function balanceOf(address account) external view returns (uint);

  function rewardTokens(uint id) external view returns (address);

  function isRewardToken(address token) external view returns (bool);

  function rewardTokensLength() external view returns (uint);

  function derivedBalance(address account) external view returns (uint);

  function left(address token) external view returns (uint);

  function earned(address token, address account) external view returns (uint);

  function registerRewardToken(address token) external;

  function removeRewardToken(address token) external;

}

File 10 of 14 : Math.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

library Math {

  function max(uint a, uint b) internal pure returns (uint) {
    return a >= b ? a : b;
  }

  function min(uint a, uint b) internal pure returns (uint) {
    return a < b ? a : b;
  }

  function positiveInt128(int128 value) internal pure returns (int128) {
    return value < 0 ? int128(0) : value;
  }

  function closeTo(uint a, uint b, uint target) internal pure returns (bool) {
    if (a > b) {
      if (a - b <= target) {
        return true;
      }
    } else {
      if (b - a <= target) {
        return true;
      }
    }
    return false;
  }

  function sqrt(uint y) internal pure returns (uint z) {
    if (y > 3) {
      z = y;
      uint x = y / 2 + 1;
      while (x < z) {
        z = x;
        x = (y / x + x) / 2;
      }
    } else if (y != 0) {
      z = 1;
    }
  }

}

File 11 of 14 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.13;

import "../interface/IERC20.sol";
import "./Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
  using Address for address;

  function safeTransfer(
    IERC20 token,
    address to,
    uint value
  ) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
  }

  function safeTransferFrom(
    IERC20 token,
    address from,
    address to,
    uint value
  ) internal {
    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
  }

  function safeIncreaseAllowance(
    IERC20 token,
    address spender,
    uint value
  ) internal {
    uint newAllowance = token.allowance(address(this), spender) + value;
    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
  }

  /**
   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
  function _callOptionalReturn(IERC20 token, bytes memory data) private {
    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
    // the target address contains contract code and also asserts for success in the low-level call.

    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
    if (returndata.length > 0) {
      // Return data is optional
      require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }
  }

}

File 12 of 14 : CheckpointLib.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

library CheckpointLib {

  /// @notice A checkpoint for uint value
  struct Checkpoint {
    uint timestamp;
    uint value;
  }

  function findLowerIndex(mapping(uint => Checkpoint) storage checkpoints, uint size, uint timestamp) internal view returns (uint) {
    require(size != 0, "Empty checkpoints");

    // First check most recent value
    if (checkpoints[size - 1].timestamp <= timestamp) {
      return (size - 1);
    }

    // Next check implicit zero value
    if (checkpoints[0].timestamp > timestamp) {
      return 0;
    }

    uint lower = 0;
    uint upper = size - 1;
    while (upper > lower) {
      // ceil, avoiding overflow
      uint center = upper - (upper - lower) / 2;
      Checkpoint memory cp = checkpoints[center];
      if (cp.timestamp == timestamp) {
        return center;
      } else if (cp.timestamp < timestamp) {
        lower = center;
      } else {
        upper = center - 1;
      }
    }
    return lower;
  }

}

File 13 of 14 : Reentrancy.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

abstract contract Reentrancy {

  /// @dev simple re-entrancy check
  uint internal _unlocked = 1;

  modifier lock() {
    require(_unlocked == 1, "Reentrant call");
    _unlocked = 2;
    _;
    _unlocked = 1;
  }

}

File 14 of 14 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.13;

/**
 * @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
     * ====
     *
     * [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;
  }

  function functionCall(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    require(isContract(target), "Address: call to non-contract");
    (bool success, bytes memory returndata) = target.call(data);
    return verifyCallResult(success, returndata, errorMessage);
  }

  /**
   * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason 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 {
      // 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
        assembly {
          let returndata_size := mload(returndata)
          revert(add(32, returndata), returndata_size)
        }
      } else {
        revert(errorMessage);
      }
    }
  }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_voter","type":"address"},{"internalType":"address[]","name":"_allowedRewardTokens","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"recepient","type":"address"}],"name":"ClaimRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NotifyReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"_deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"_withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"adr","type":"address"}],"name":"addressToTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maxRuns","type":"uint256"}],"name":"batchUpdateRewardPerToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"checkpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"derivedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"derivedBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"derivedSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPriorBalanceIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPriorRewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPriorSupplyIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"getRewardForOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isRewardToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"lastEarn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"left","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"periodFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"registerRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"removeRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardPerTokenCheckpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardPerTokenNumCheckpoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardTokensLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"supplyCheckpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyNumCheckpoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenIdToAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userRewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ve","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102485760003560e01c80637bb7bed11161013b578063d35e2544116100b8578063f12297771161007c578063f122977714610631578063f320772314610644578063f5f8d36514610657578063f7412baf1461066a578063fd3140981461069157600080fd5b8063d35e2544146105d9578063d7da4bb0146105ec578063da09d19d146105f5578063e46dbc9814610615578063e8111a121461062857600080fd5b8063a7852afa116100ff578063a7852afa14610558578063aa4796521461056b578063b5fd73f81461058b578063b66503cf146105be578063bf199e62146105d157600080fd5b80637bb7bed1146104d457806399bcc052146104e75780639ce43f90146104fa5780639e2bf22c1461051a578063a495e5b51461052d57600080fd5b80633d509c97116101c957806368fcee1a1161018d57806368fcee1a146104475780636f307dc31461045a5780636fcfff451461048157806370a08231146104a157806376f4be36146104c157600080fd5b80633d509c97146103c557806346c96aac146103da578063570ca7351461040157806363fb415b146104145780636511bf2e1461043457600080fd5b80631f850716116102105780631f85071614610320578063211dc32d14610347578063221ca18c1461035a5780632ce9aead1461037a5780633ca068b61461039a57600080fd5b806301316ddf1461024d57806308bb76a5146102995780630cdfebfa146102c4578063115c6f39146102f657806318160ddd14610317575b600080fd5b61027f61025b3660046125a0565b60126020908152600092835260408084209091529082529020805460019091015482565b604080519283526020830191909152015b60405180910390f35b6102ac6102a73660046125cc565b6106a4565b6040516001600160a01b039091168152602001610290565b61027f6102d23660046125a0565b600e6020908152600092835260408084209091529082529020805460019091015482565b6103096103043660046125a0565b6106b5565b604051908152602001610290565b610309600a5481565b6102ac7f000000000000000000000000060fa7ad32c510f12550c7a967999810dafc569781565b6103096103553660046125e5565b6106c8565b61030961036836600461261e565b60046020526000908152604090205481565b61030961038836600461261e565b60066020526000908152604090205481565b6103096103a83660046125e5565b600960209081526000928352604080842090915290825290205481565b6103d86103d336600461261e565b6106d4565b005b6102ac7f000000000000000000000000649bdf58b09a0cd4ac848b42c4b5e1390a72a49a81565b6001546102ac906001600160a01b031681565b61030961042236600461261e565b60036020526000908152604090205481565b61030961044236600461261e565b6109b4565b6103d86104553660046125a0565b6109c5565b6102ac7f000000000000000000000000000000000000000000000000000000000000000081565b61030961048f36600461261e565b600f6020526000908152604090205481565b6103096104af36600461261e565b600b6020526000908152604090205481565b6103096104cf3660046125cc565b6109ff565b6102ac6104e23660046125cc565b610a0a565b6103096104f536600461261e565b610a34565b61030961050836600461261e565b60076020526000908152604090205481565b6103d861052836600461263b565b610ab9565b61030961053b3660046125e5565b600860209081526000928352604080842090915290825290205481565b6103d8610566366004612673565b610b80565b61030961057936600461261e565b60136020526000908152604090205481565b6105ae61059936600461261e565b600d6020526000908152604090205460ff1681565b6040519015158152602001610290565b6103d86105cc3660046125a0565b610c6e565b600c54610309565b6103096105e736600461261e565b610c7c565b61030960025481565b61030961060336600461261e565b60056020526000908152604090205481565b6103d861062336600461261e565b610c9a565b61030960115481565b61030961063f36600461261e565b610cef565b6103d861065236600461263b565b610cfa565b6103d8610665366004612673565b610db4565b61027f6106783660046125cc565b6010602052600090815260409020805460019091015482565b61027f61069f3660046125a0565b610e94565b60006106af82610ead565b92915050565b60006106c18383610ef8565b9392505050565b60006106c18383610f4d565b6001546001600160a01b031633146107225760405162461bcd60e51b815260206004820152600c60248201526b2737ba1037b832b930ba37b960a11b60448201526064015b60405180910390fd5b6001600160a01b038116600090815260056020526040902054421161077d5760405162461bcd60e51b815260206004820152601160248201527014995dd85c991cc81b9bdd08195b991959607a1b6044820152606401610719565b6001600160a01b0381166000908152600d602052604090205460ff166107d85760405162461bcd60e51b815260206004820152601060248201526f2737ba103932bbb0b932103a37b5b2b760811b6044820152606401610719565b6001600160a01b0381166000908152600d60205260409020805460ff19169055600c54600381116108575760405162461bcd60e51b8152602060048201526024808201527f4669727374203320746f6b656e732073686f756c64206e6f742062652072656d6044820152631bdd995960e21b6064820152608401610719565b600360005b828210156108b7576000600c838154811061087957610879612744565b6000918252602090912001546001600160a01b039081169150851681036108a45760019150506108b7565b50816108af81612770565b92505061085c565b806109045760405162461bcd60e51b815260206004820181905260248201527f466972737420746f6b656e7320666f7262696464656e20746f2072656d6f76656044820152606401610719565b600c610911600185612789565b8154811061092157610921612744565b600091825260209091200154600c80546001600160a01b03909216918490811061094d5761094d612744565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600c80548061098c5761098c6127a0565b600082815260209020810160001990810180546001600160a01b031916905501905550505050565b60006001600160a01b0382166106af565b6109d1828260006111ce565b6001600160a01b03909316600090815260076020908152604080832060069092529091209390935590915550565b60006106af826113be565b600c8181548110610a1a57600080fd5b6000918252602090912001546001600160a01b0316905081565b6001600160a01b0381166000908152600560205260408120544210610a5b57506000919050565b6001600160a01b038216600090815260056020526040812054610a7f904290612789565b6001600160a01b038416600090815260046020526040902054909150670de0b6b3a764000090610aaf90836127b6565b6106c191906127d5565b336001600160a01b037f000000000000000000000000649bdf58b09a0cd4ac848b42c4b5e1390a72a49a1614610b015760405162461bcd60e51b8152600401610719906127f7565b60008211610b215760405162461bcd60e51b81526004016107199061281a565b6000610b2c82610ead565b9050610b3881846113e0565b806001600160a01b03167f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436484604051610b7391815260200190565b60405180910390a2505050565b336001600160a01b037f000000000000000000000000649bdf58b09a0cd4ac848b42c4b5e1390a72a49a1614610bc85760405162461bcd60e51b8152600401610719906127f7565b6040516331a9108f60e11b8152600481018390526000907f000000000000000000000000060fa7ad32c510f12550c7a967999810dafc56976001600160a01b031690636352211e90602401602060405180830381865afa158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c54919061283f565b9050610c69610c6284610ead565b8383611436565b505050565b610c788282611730565b5050565b6001600160a01b0381166000908152600b60205260408120546106af565b6001546001600160a01b03163314610ce35760405162461bcd60e51b815260206004820152600c60248201526b2737ba1037b832b930ba37b960a11b6044820152606401610719565b610cec81611ac1565b50565b60006106af82611bd0565b336001600160a01b037f000000000000000000000000649bdf58b09a0cd4ac848b42c4b5e1390a72a49a1614610d425760405162461bcd60e51b8152600401610719906127f7565b60008211610d625760405162461bcd60e51b81526004016107199061281a565b6000610d6d82610ead565b9050610d798184611c7b565b806001600160a01b03167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c84604051610b7391815260200190565b60405163430c208160e01b8152336004820152602481018390527f000000000000000000000000060fa7ad32c510f12550c7a967999810dafc56976001600160a01b03169063430c208190604401602060405180830381865afa158015610e1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e43919061285c565b610e815760405162461bcd60e51b815260206004820152600f60248201526e2737ba103a37b5b2b71037bbb732b960891b6044820152606401610719565b610c78610e8d83610ead565b8233611436565b600080610ea18484611cc2565b915091505b9250929050565b600081806001600160a01b038116146106af5760405162461bcd60e51b815260206004820152600d60248201526c15dc9bdb99c818dbdb9d995c9d609a1b6044820152606401610719565b6001600160a01b0382166000908152600f6020526040812054808203610f225760009150506106af565b6001600160a01b0384166000908152600e60205260409020610f45908285611d4c565b949350505050565b6001600160a01b0381166000908152600f60205260408120548103610f74575060006106af565b6001600160a01b03808416600081815260086020908152604080832094871683529381528382205492825260128152838220828052905291822054610fb99190611e92565b90506000610fc78483610ef8565b6001600160a01b0385166000908152600f602052604081205491925090610ff090600190612789565b90506000811561110257825b611007600184612789565b8111611100576001600160a01b0387166000818152600e60208181526040808420868552808352818520825180840190935280548352600190810154838501529585529290915292829061105c90869061287e565b8152602001908152602001600020604051806040016040529081600082015481526020016001820154815250509050600061109b8b8460000151611cc2565b50905060006110ae8c8460000151611cc2565b509050670de0b6b3a76400006110c48383612789565b85602001516110d391906127b6565b6110dd91906127d5565b6110e7908761287e565b95505050505080806110f890612770565b915050610ffc565b505b6001600160a01b0386166000908152600e6020908152604080832085845282528083208151808301909252805480835260019091015492820192909252919061114c908a90611cc2565b506001600160a01b03808b166000908152600960209081526040808320938d1683529290522054909150670de0b6b3a76400009061118b908390611e92565b6111948b611bd0565b61119e9190612789565b83602001516111ad91906127b6565b6111b791906127d5565b6111c1908461287e565b9998505050505050505050565b6001600160a01b038316600090815260066020908152604080832054600790925282205460115483929190830361120857925090506113b6565b6001600160a01b03871660009081526004602052604081205490036112335792504291506113b69050565b600061123e836113be565b9050600061125a60016011546112549190612789565b89611ea9565b9050801561133357815b61126f600183612789565b811161133157600081815260106020908152604091829020825180840190935280548352600101549082018190521561131e5760006010816112b285600161287e565b81526020019081526020016000206040518060400160405290816000820154815260200160018201548152505090506000806112fd8e8460000151866000015187602001518d611eb8565b909250905061130c828961287e565b97506113198e8983611f3c565b975050505b508061132981612770565b915050611264565b505b86156113ad5760008181526010602090815260409182902082518084019093528054835260010154908201819052156113ab57600061138c8b6113758d612045565b8451611381908a611e92565b85602001518a611eb8565b509050611399818661287e565b94506113a68b8642611f3c565b429550505b505b50909350909150505b935093915050565b6011546000908082036113d45750600092915050565b6106c160108285611d4c565b6113e8612069565b80600a60008282546113fa9190612789565b90915550506001600160a01b0382166000908152600b602052604081208054839290611427908490612789565b90915550610c789050826120ed565b6000546001146114795760405162461bcd60e51b815260206004820152600e60248201526d1499595b9d1c985b9d0818d85b1b60921b6044820152606401610719565b600260009081555b825181101561171c576114b183828151811061149f5761149f612744565b602002602001015160001960016111ce565b600760008685815181106114c7576114c7612744565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006006600088878151811061150757611507612744565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000849190505583919050555050600061156284838151811061155457611554612744565b602002602001015186610f4d565b9050426008600086858151811061157b5761157b612744565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020016000206000876001600160a01b03166001600160a01b0316815260200190815260200160002081905550600760008584815181106115e0576115e0612744565b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020546009600086858151811061161f5761161f612744565b6020908102919091018101516001600160a01b0390811683528282019390935260409182016000908120938a168152929052902055801561169157611691838286858151811061167157611671612744565b60200260200101516001600160a01b03166121709092919063ffffffff16565b8382815181106116a3576116a3612744565b60200260200101516001600160a01b0316336001600160a01b03167fe4394b3c25e336e9d6b7fcefab7e3234f1c6b373f13655dc3920664b080b098d83866040516117019291909182526001600160a01b0316602082015260400190565b60405180910390a3508061171481612770565b915050611481565b50611726836120ed565b5050600160005550565b6000546001146117735760405162461bcd60e51b815260206004820152600e60248201526d1499595b9d1c985b9d0818d85b1b60921b6044820152606401610719565b60026000556001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116908316036117f35760405162461bcd60e51b815260206004820152601760248201527f57726f6e6720746f6b656e20666f7220726577617264730000000000000000006044820152606401610719565b600081116118135760405162461bcd60e51b81526004016107199061281a565b6001600160a01b0382166000908152600d602052604090205460ff1661186f5760405162461bcd60e51b8152602060048201526011602482015270151bdad95b881b9bdd08185b1b1bddd959607a1b6044820152606401610719565b6001600160a01b038216600090815260046020526040812054900361189a5761189a82600042611f3c565b6118a88260001960016111ce565b6001600160a01b0384166000908152600760209081526040808320600683528184209490945593909255600590915220544210611932576118f46001600160a01b0383163330846121d3565b62093a8061190a670de0b6b3a7640000836127b6565b61191491906127d5565b6001600160a01b038316600090815260046020526040902055611a52565b6001600160a01b038216600090815260056020526040812054611956904290612789565b6001600160a01b0384166000908152600460205260408120549192509061197d90836127b6565b9050611991670de0b6b3a7640000826127d5565b83116119f65760405162461bcd60e51b815260206004820152602e60248201527f416d6f756e742073686f756c6420626520686967686572207468616e2072656d60448201526d61696e696e67207265776172647360901b6064820152608401610719565b611a0b6001600160a01b0385163330866121d3565b62093a8081611a22670de0b6b3a7640000866127b6565b611a2c919061287e565b611a3691906127d5565b6001600160a01b03851660009081526004602052604090205550505b611a5f62093a804261287e565b6001600160a01b0383166000818152600560205260409081902092909255905133907ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf5082690611ab09085815260200190565b60405180910390a350506001600055565b600c54600a11611b0c5760405162461bcd60e51b8152602060048201526016602482015275546f6f206d616e792072657761726420746f6b656e7360501b6044820152606401610719565b6001600160a01b0381166000908152600d602052604090205460ff1615611b6a5760405162461bcd60e51b8152602060048201526012602482015271105b1c9958591e481c9959da5cdd195c995960721b6044820152606401610719565b6001600160a01b03166000818152600d60205260408120805460ff19166001908117909155600c805491820181559091527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c70180546001600160a01b0319169091179055565b6000600254600003611bf857506001600160a01b031660009081526007602052604090205490565b6002546001600160a01b03831660009081526004602090815260408083205460068352818420546005909352922054611c319190611ea9565b611c3a85612045565b611c449190612789565b611c4e91906127b6565b611c5891906127d5565b6001600160a01b0383166000908152600760205260409020546106af919061287e565b611c83612069565b80600a6000828254611c95919061287e565b90915550506001600160a01b0382166000908152600b60205260408120805483929061142790849061287e565b6001600160a01b0382166000908152601360205260408120548190808203611cf1576000809250925050610ea6565b6001600160a01b038516600090815260126020526040812090611d15828488611d4c565b6000908152602092835260409081902081518083019092528054808352600190910154919093018190529791965090945050505050565b600082600003611d925760405162461bcd60e51b8152602060048201526011602482015270456d70747920636865636b706f696e747360781b6044820152606401610719565b81846000611da1600187612789565b81526020019081526020016000206000015411611dca57611dc3600184612789565b90506106c1565b600080805260208590526040902054821015611de8575060006106c1565b600080611df6600186612789565b90505b81811115611e895760006002611e0f8484612789565b611e1991906127d5565b611e239083612789565b60008181526020898152604091829020825180840190935280548084526001909101549183019190915291925090869003611e63575092506106c1915050565b8051861115611e7457819350611e82565b611e7f600183612789565b92505b5050611df9565b50949350505050565b600081831015611ea257816106c1565b5090919050565b6000818310611ea257816106c1565b6000806000611ec78785611e92565b6001600160a01b038916600090815260056020908152604080832054600490925290912054919250908690611f05611eff8a89611e92565b84611ea9565b611f0f8585611ea9565b611f199190612789565b611f2391906127b6565b611f2d91906127d5565b99919850909650505050505050565b6001600160a01b0383166000908152601360205260409020548015801590611f9857506001600160a01b03841660009081526012602052604081208391611f84600185612789565b815260200190815260200160002060000154145b15611fdb576001600160a01b03841660009081526012602052604081208491611fc2600185612789565b815260208101919091526040016000206001015561203f565b60408051808201825283815260208082018681526001600160a01b03881660009081526012835284812086825290925292902090518155905160019182015561202590829061287e565b6001600160a01b0385166000908152601360205260409020555b50505050565b6001600160a01b0381166000908152600560205260408120546106af904290611ea9565b600c5460005b81811015610c78576000600c828154811061208c5761208c612744565b6000918252602090912001546001600160a01b031690506120b18160001960016111ce565b6001600160a01b0390921660009081526007602090815260408083206006909252909120929092559055806120e581612770565b91505061206f565b6001600160a01b0381166000908152600360205260408120546002805491928392612119908490612789565b9091555050506001600160a01b0381166000908152600b602090815260408083205460039092528220819055600280549192839261215890849061287e565b909155506121689050828261220b565b610c786122fc565b6040516001600160a01b038316602482015260448101829052610c6990849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526123a0565b6040516001600160a01b038085166024830152831660448201526064810182905261203f9085906323b872dd60e01b9060840161219c565b6001600160a01b0382166000908152600f60205260409020544290801580159061226957506001600160a01b0384166000908152600e602052604081208391612255600185612789565b815260200190815260200160002060000154145b15612293576001600160a01b0384166000908152600e602052604081208491611fc2600185612789565b60408051808201825283815260208082018681526001600160a01b0388166000908152600e83528481208682529092529290209051815590516001918201556122dd90829061287e565b6001600160a01b0385166000908152600f602052604090205550505050565b60115442811580159061232e5750806010600061231a600186612789565b815260200190815260200160002060000154145b1561235d5760025460106000612345600186612789565b81526020810191909152604001600020600101555050565b6040805180820182528281526002546020808301918252600086815260109091529290922090518155905160019182015561239990839061287e565b6011555050565b60006123f5826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166124729092919063ffffffff16565b805190915015610c695780806020019051810190612413919061285c565b610c695760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610719565b60606001600160a01b0384163b6124cb5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610719565b600080856001600160a01b0316856040516124e691906128c2565b6000604051808303816000865af19150503d8060008114612523576040519150601f19603f3d011682016040523d82523d6000602084013e612528565b606091505b5091509150612538828286612542565b9695505050505050565b606083156125515750816106c1565b8251156125615782518084602001fd5b8160405162461bcd60e51b815260040161071991906128de565b6001600160a01b0381168114610cec57600080fd5b803561259b8161257b565b919050565b600080604083850312156125b357600080fd5b82356125be8161257b565b946020939093013593505050565b6000602082840312156125de57600080fd5b5035919050565b600080604083850312156125f857600080fd5b82356126038161257b565b915060208301356126138161257b565b809150509250929050565b60006020828403121561263057600080fd5b81356106c18161257b565b6000806040838503121561264e57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561268657600080fd5b8235915060208084013567ffffffffffffffff808211156126a657600080fd5b818601915086601f8301126126ba57600080fd5b8135818111156126cc576126cc61265d565b8060051b604051601f19603f830116810181811085821117156126f1576126f161265d565b60405291825284820192508381018501918983111561270f57600080fd5b938501935b828510156127345761272585612590565b84529385019392850192612714565b8096505050505050509250929050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016127825761278261275a565b5060010190565b60008282101561279b5761279b61275a565b500390565b634e487b7160e01b600052603160045260246000fd5b60008160001904831182151516156127d0576127d061275a565b500290565b6000826127f257634e487b7160e01b600052601260045260246000fd5b500490565b6020808252600990820152682737ba103b37ba32b960b91b604082015260600190565b6020808252600b908201526a16995c9bc8185b5bdd5b9d60aa1b604082015260600190565b60006020828403121561285157600080fd5b81516106c18161257b565b60006020828403121561286e57600080fd5b815180151581146106c157600080fd5b600082198211156128915761289161275a565b500190565b60005b838110156128b1578181015183820152602001612899565b8381111561203f5750506000910152565b600082516128d4818460208701612896565b9190910192915050565b60208152600082518060208401526128fd816040850160208701612896565b601f01601f1916919091016040019291505056fea26469706673582212203a96e6088b0b370ddb7df141bb1eacf5da3a10634caf16402c5764bbdca6564964736f6c634300080d0033

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
[ Download: CSV Export  ]
[ 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.