BERA Price: $3.62 (-1.73%)

Contract

0xeA7844869097ed447Bf57d85b24142c21694E805

Overview

BERA Balance

Berachain LogoBerachain LogoBerachain Logo0 BERA

BERA Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw40858532025-04-23 12:18:122 hrs ago1745410692IN
0xeA784486...21694E805
0 BERA0.000112180.5
Get Reward40843492025-04-23 11:28:413 hrs ago1745407721IN
0xeA784486...21694E805
0 BERA0.000026820.1
Withdraw40843402025-04-23 11:28:243 hrs ago1745407704IN
0xeA784486...21694E805
0 BERA0.000000290.001321
Withdraw40837132025-04-23 11:07:473 hrs ago1745406467IN
0xeA784486...21694E805
0 BERA00.00000005
Withdraw40805252025-04-23 9:23:175 hrs ago1745400197IN
0xeA784486...21694E805
0 BERA0.000112180.5
Stake40803952025-04-23 9:19:035 hrs ago1745399943IN
0xeA784486...21694E805
0 BERA0.000057430.24482688
Stake40784322025-04-23 8:14:306 hrs ago1745396070IN
0xeA784486...21694E805
0 BERA0.000022020.1
Withdraw40702912025-04-23 3:46:1911 hrs ago1745379979IN
0xeA784486...21694E805
0 BERA00.00000031
Get Reward40699362025-04-23 3:34:3711 hrs ago1745379277IN
0xeA784486...21694E805
0 BERA0.000000120.000502
Get Reward40689732025-04-23 3:02:5411 hrs ago1745377374IN
0xeA784486...21694E805
0 BERA0.00012880.50237093
Get Reward40630132025-04-22 23:46:4815 hrs ago1745365608IN
0xeA784486...21694E805
0 BERA0.000128190.50000033
Get Reward For U...40548882025-04-22 19:20:2419 hrs ago1745349624IN
0xeA784486...21694E805
0 BERA00.00000005
Get Reward40534102025-04-22 18:32:0920 hrs ago1745346729IN
0xeA784486...21694E805
0 BERA0.000000020.0001001
Get Reward40475492025-04-22 15:20:2623 hrs ago1745335226IN
0xeA784486...21694E805
0 BERA0.000256390.99999999
Stake40387852025-04-22 10:35:1028 hrs ago1745318110IN
0xeA784486...21694E805
0 BERA0.000000490.00212198
Get Reward40351102025-04-22 8:35:5130 hrs ago1745310951IN
0xeA784486...21694E805
0 BERA0.000038450.15
Get Reward40320552025-04-22 6:57:0132 hrs ago1745305021IN
0xeA784486...21694E805
0 BERA00.00000005
Get Reward40207312025-04-22 0:50:1738 hrs ago1745283017IN
0xeA784486...21694E805
0 BERA0.000000380.001509
Stake40150242025-04-21 21:42:4941 hrs ago1745271769IN
0xeA784486...21694E805
0 BERA00.0000001
Get Reward40108982025-04-21 19:25:4643 hrs ago1745263546IN
0xeA784486...21694E805
0 BERA0.000008440.03100002
Get Reward For U...40107342025-04-21 19:20:1843 hrs ago1745263218IN
0xeA784486...21694E805
0 BERA00.00000005
Get Reward40067552025-04-21 17:08:3145 hrs ago1745255311IN
0xeA784486...21694E805
0 BERA0.000128190.5
Stake40010382025-04-21 14:03:202 days ago1745244200IN
0xeA784486...21694E805
0 BERA0.000033040.15
Stake39997192025-04-21 13:20:362 days ago1745241636IN
0xeA784486...21694E805
0 BERA0.000192820.821936
Stake39981552025-04-21 12:29:292 days ago1745238569IN
0xeA784486...21694E805
0 BERA00.00000005
View all transactions

Latest 1 internal transaction

Parent Transaction Hash Block From To
31230132025-04-01 13:07:1822 days ago1743512838  Contract Creation0 BERA
Loading...
Loading

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

Contract Name:
InfraredVault

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
File 1 of 41 : InfraredVault.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import {IRewardVault as IBerachainRewardsVault} from
    "@berachain/pol/interfaces/IRewardVault.sol";
import {IRewardVaultFactory as IBerachainRewardsVaultFactory} from
    "@berachain/pol/interfaces/IRewardVaultFactory.sol";

import {Errors} from "src/utils/Errors.sol";
import {MultiRewards} from "src/core/MultiRewards.sol";

import {ERC20} from "@solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";

import {IInfrared} from "src/interfaces/IInfrared.sol";
import {IInfraredVault} from "src/interfaces/IInfraredVault.sol";

/**
 * @title InfraredVault
 * @notice This contract is the vault for staking tokens, and receiving rewards from the Proof of Liquidity protocol.
 * @dev This contract uses the MultiRewards contract to distribute rewards to vault stakers, this is taken from curve.fi. (inspired by Synthetix).
 * @dev Does not support staking tokens with non-standard ERC20 transfer tax behavior.
 */
contract InfraredVault is MultiRewards, IInfraredVault {
    using SafeTransferLib for ERC20;

    /// @notice Maximum number of reward tokens that can be supported
    /// @dev Limited to prevent gas issues with reward calculations
    uint256 public constant MAX_NUM_REWARD_TOKENS = 10;

    /// @notice The infrared contract address acts a vault factory and coordinator
    address public immutable infrared;

    // The address of the berachain rewards vault
    IBerachainRewardsVault public immutable rewardsVault;

    /// Modifier to check that the caller is infrared contract
    modifier onlyInfrared() {
        if (msg.sender != infrared) revert Errors.Unauthorized(msg.sender);
        _;
    }

    constructor(address _stakingToken, uint256 _rewardsDuration)
        MultiRewards(_stakingToken)
    {
        // infrared factory/coordinator
        infrared = msg.sender;

        if (_stakingToken == address(0)) revert Errors.ZeroAddress();
        if (_rewardsDuration == 0) revert Errors.ZeroAmount();

        // set the berachain rewards vault and operator as infrared
        rewardsVault = _createRewardsVaultIfNecessary(infrared, _stakingToken);
        rewardsVault.setOperator(infrared);

        address _ibgt = address(IInfrared(infrared).ibgt());

        _addReward(_ibgt, infrared, _rewardsDuration);

        // to be able to recover rewards which where distributed during periods where there was no stake
        // infrared will have a stake of 1 wei in the vault
        _totalSupply = _totalSupply + 1;
        _balances[msg.sender] = _balances[msg.sender] + 1;
    }

    /**
     * @notice Gets or creates the berachain rewards vault for given staking token
     * @param _infrared The address of Infrared
     * @param _stakingToken The address of the staking token for this vault
     * @return The address of the berachain rewards vault
     */
    function _createRewardsVaultIfNecessary(
        address _infrared,
        address _stakingToken
    ) private returns (IBerachainRewardsVault) {
        IBerachainRewardsVaultFactory rewardsFactory =
            IInfrared(_infrared).rewardsFactory();
        address rewardsVaultAddress = rewardsFactory.getVault(_stakingToken);
        if (rewardsVaultAddress == address(0)) {
            rewardsVaultAddress =
                rewardsFactory.createRewardVault(_stakingToken);
        }
        return IBerachainRewardsVault(rewardsVaultAddress);
    }

    /*//////////////////////////////////////////////////////////////
                            STAKE/WITHDRAW/CLAIM
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Transfers to berachain low level module on staking of LP tokens with the vault after transferring tokens in
     * @param amount The amount of staking token transferred in to the contract
     */
    function onStake(uint256 amount) internal override {
        stakingToken.safeApprove(address(rewardsVault), amount);
        rewardsVault.stake(amount);
    }

    /**
     * @notice Redeems from berachain low level module on withdraw of LP tokens from the vault before transferring tokens out
     * @param amount The amount of staking token transferred out of the contract
     */
    function onWithdraw(uint256 amount) internal override {
        rewardsVault.withdraw(amount);
    }

    /**
     * @notice hook called after the reward is claimed to harvest the rewards from the berachain rewards vault
     */
    function onReward() internal override {
        IInfrared(infrared).harvestVault(address(stakingToken));
    }

    /*//////////////////////////////////////////////////////////////
                            INFRARED ONLY
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IInfraredVault
    function updateRewardsDuration(
        address _rewardsToken,
        uint256 _rewardsDuration
    ) external onlyInfrared {
        if (_rewardsToken == address(0)) revert Errors.ZeroAddress();
        if (_rewardsDuration == 0) revert Errors.ZeroAmount();
        _setRewardsDuration(_rewardsToken, _rewardsDuration);
    }

    /// @inheritdoc IInfraredVault
    function unpauseStaking() external onlyInfrared {
        if (!paused()) return;
        _unpause();
    }

    /// @inheritdoc IInfraredVault
    function pauseStaking() external onlyInfrared {
        if (paused()) return;
        _pause();
    }

    /// @inheritdoc IInfraredVault
    function addReward(address _rewardsToken, uint256 _rewardsDuration)
        external
        onlyInfrared
    {
        if (_rewardsToken == address(0)) revert Errors.ZeroAddress();
        if (_rewardsDuration == 0) revert Errors.ZeroAmount();
        if (
            rewardTokens.length == MAX_NUM_REWARD_TOKENS
                && _rewardsToken != address(IInfrared(infrared).ir())
        ) {
            revert Errors.MaxNumberOfRewards();
        }
        _addReward(_rewardsToken, infrared, _rewardsDuration);
    }

    /// @inheritdoc IInfraredVault
    function removeReward(address _rewardsToken) external onlyInfrared {
        if (_rewardsToken == address(0)) revert Errors.ZeroAddress();
        _removeReward(_rewardsToken);
    }

    /// @inheritdoc IInfraredVault
    function notifyRewardAmount(address _rewardToken, uint256 _reward)
        external
        onlyInfrared
    {
        if (_rewardToken == address(0)) revert Errors.ZeroAddress();
        if (_reward == 0) revert Errors.ZeroAmount();
        _notifyRewardAmount(_rewardToken, _reward);
    }

    /// @inheritdoc IInfraredVault
    function recoverERC20(address _to, address _token, uint256 _amount)
        external
        onlyInfrared
    {
        if (_to == address(0) || _token == address(0)) {
            revert Errors.ZeroAddress();
        }
        if (_amount == 0) revert Errors.ZeroAmount();
        _recoverERC20(_to, _token, _amount);
    }

    /*//////////////////////////////////////////////////////////////
                            Getters
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IInfraredVault
    function getAllRewardTokens() external view returns (address[] memory) {
        return rewardTokens;
    }

    /// @inheritdoc IInfraredVault
    function getAllRewardsForUser(address _user)
        external
        view
        returns (UserReward[] memory)
    {
        uint256 len = rewardTokens.length;
        UserReward[] memory tempRewards = new UserReward[](len);
        uint256 count = 0;

        for (uint256 i = 0; i < len; i++) {
            uint256 amount = earned(_user, rewardTokens[i]);
            if (amount > 0) {
                tempRewards[count] =
                    UserReward({token: rewardTokens[i], amount: amount});
                count++;
            }
        }

        // Create a new array with the exact size of non-zero rewards
        UserReward[] memory userRewards = new UserReward[](count);
        for (uint256 j = 0; j < count; j++) {
            userRewards[j] = tempRewards[j];
        }

        return userRewards;
    }
}

File 2 of 41 : IRewardVault.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

import { IPOLErrors } from "./IPOLErrors.sol";
import { IStakingRewards } from "../../base/IStakingRewards.sol";

interface IRewardVault is IPOLErrors, IStakingRewards {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @notice Emitted when a delegate has staked on behalf of an account.
    /// @param account The account whose delegate has staked.
    /// @param delegate The delegate that has staked.
    /// @param amount The amount of staked tokens.
    event DelegateStaked(address indexed account, address indexed delegate, uint256 amount);

    /// @notice Emitted when a delegate has withdrawn on behalf of an account.
    /// @param account The account whose delegate has withdrawn.
    /// @param delegate The delegate that has withdrawn.
    /// @param amount The amount of withdrawn tokens.
    event DelegateWithdrawn(address indexed account, address indexed delegate, uint256 amount);

    /// @notice Emitted when a token has been recovered.
    /// @param token The token that has been recovered.
    /// @param amount The amount of token recovered.
    event Recovered(address token, uint256 amount);

    /// @notice Emitted when the msg.sender has set an operator to handle its rewards.
    /// @param account The account that has set the operator.
    /// @param operator The operator that has been set.
    event OperatorSet(address account, address operator);

    /// @notice Emitted when the distributor is set.
    /// @param distributor The address of the distributor.
    event DistributorSet(address indexed distributor);

    /// @notice Emitted when the manager of an incentive token is changed.
    /// @param token The address of the incentive token.
    /// @param newManager The new manager of the incentive token.
    /// @param oldManager The old manager of the incentive token.
    event IncentiveManagerChanged(address indexed token, address newManager, address oldManager);

    /// @notice Emitted when an incentive token is whitelisted.
    /// @param token The address of the token that has been whitelisted.
    /// @param minIncentiveRate The minimum amount of the token to incentivize per BGT emission.
    /// @param manager The address of the manager that can addIncentive for this incentive token.
    event IncentiveTokenWhitelisted(address indexed token, uint256 minIncentiveRate, address manager);

    /// @notice Emitted when an incentive token is removed.
    /// @param token The address of the token that has been removed.
    event IncentiveTokenRemoved(address indexed token);

    /// @notice Emitted when maxIncentiveTokensCount is updated.
    /// @param maxIncentiveTokensCount The max count of incentive tokens.
    event MaxIncentiveTokensCountUpdated(uint8 maxIncentiveTokensCount);

    /// @notice Emitted when validator share of incentives are processed to the operator address of a validator.
    event IncentivesProcessed(bytes indexed pubkey, address indexed token, uint256 bgtEmitted, uint256 amount);

    /// @notice Emitted when validator share of incentives fail to be processed to the operator address of a validator.
    event IncentivesProcessFailed(bytes indexed pubkey, address indexed token, uint256 bgtEmitted, uint256 amount);

    /// @notice Emitted when incentives are added to the vault.
    /// @param token The incentive token.
    /// @param sender The address that added the incentive.
    /// @param amount The amount of the incentive.
    /// @param incentiveRate The amount of the token to incentivize per BGT emission.
    event IncentiveAdded(address indexed token, address sender, uint256 amount, uint256 incentiveRate);

    /// @notice Emitted when the BGT booster share of the incentive is processed.
    /// @param pubkey The pubkey of the validator.
    /// @param token The address of the incentive token.
    /// @param bgtEmitted The amount of BGT emitted by the validator.
    /// @param amount The amount of the incentive.
    event BGTBoosterIncentivesProcessed(
        bytes indexed pubkey, address indexed token, uint256 bgtEmitted, uint256 amount
    );

    /// @notice Emitted when the BGT booster share of the incentive fails to be processed.
    /// @param pubkey The pubkey of the validator.
    /// @param token The address of the incentive token.
    /// @param bgtEmitted The amount of BGT emitted by the validator.
    /// @param amount The amount of the incentive.
    event BGTBoosterIncentivesProcessFailed(
        bytes indexed pubkey, address indexed token, uint256 bgtEmitted, uint256 amount
    );

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          GETTERS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @notice Get the address that is allowed to distribute rewards.
    /// @return The address that is allowed to distribute rewards.
    function distributor() external view returns (address);

    /// @notice Get the operator for an account.
    /// @param account The account to get the operator for.
    /// @return The operator for the account.
    function operator(address account) external view returns (address);

    /// @notice Get the count of active incentive tokens.
    /// @return The count of active incentive tokens.
    function getWhitelistedTokensCount() external view returns (uint256);

    /// @notice Get the list of whitelisted tokens.
    /// @return The list of whitelisted tokens.
    function getWhitelistedTokens() external view returns (address[] memory);

    /// @notice Get the total amount staked by delegates.
    /// @return The total amount staked by delegates.
    function getTotalDelegateStaked(address account) external view returns (uint256);

    /// @notice Get the amount staked by a delegate on behalf of an account.
    /// @return The amount staked by a delegate.
    function getDelegateStake(address account, address delegate) external view returns (uint256);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         ADMIN                              */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Initialize the vault, this is only callable once and by the factory since its the deployer.
     * @param _berachef The address of the berachef.
     * @param _bgt The address of the BGT token.
     * @param _distributor The address of the distributor.
     * @param _stakingToken The address of the staking token.
     */
    function initialize(address _berachef, address _bgt, address _distributor, address _stakingToken) external;

    /// @notice Allows the factory owner to set the contract that is allowed to distribute rewards.
    /// @param _rewardDistribution The address that is allowed to distribute rewards.
    function setDistributor(address _rewardDistribution) external;

    /// @notice Allows the distributor to notify the reward amount.
    /// @param pubkey The pubkey of the validator.
    /// @param reward The amount of reward to notify.
    function notifyRewardAmount(bytes calldata pubkey, uint256 reward) external;

    /// @notice Allows the factory owner to recover any ERC20 token from the vault.
    /// @param tokenAddress The address of the token to recover.
    /// @param tokenAmount The amount of token to recover.
    function recoverERC20(address tokenAddress, uint256 tokenAmount) external;

    /// @notice Allows the factory owner to update the duration of the rewards.
    /// @param _rewardsDuration The new duration of the rewards.
    function setRewardsDuration(uint256 _rewardsDuration) external;

    /// @notice Allows the factory owner to whitelist a token to incentivize with.
    /// @param token The address of the token to whitelist.
    /// @param minIncentiveRate The minimum amount of the token to incentivize per BGT emission.
    /// @param manager The address of the manager that can addIncentive for this token.
    function whitelistIncentiveToken(address token, uint256 minIncentiveRate, address manager) external;

    /// @notice Allows the factory vault manager to remove a whitelisted incentive token.
    /// @param token The address of the token to remove.
    function removeIncentiveToken(address token) external;

    /// @notice Allows the factory owner to update the maxIncentiveTokensCount.
    /// @param _maxIncentiveTokensCount The new maxIncentiveTokens count.
    function setMaxIncentiveTokensCount(uint8 _maxIncentiveTokensCount) external;

    /// @notice Allows the factory vault pauser to pause the vault.
    function pause() external;

    /// @notice Allows the factory vault manager to unpause the vault.
    function unpause() external;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MUTATIVE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @notice Exit the vault with the staked tokens and claim the reward.
    /// @dev Only the account holder can call this function, not the operator.
    /// @dev Clears out the user self-staked balance and rewards.
    /// @param recipient The address to send the 'BGT' reward to.
    function exit(address recipient) external;

    /// @notice Claim the reward.
    /// @dev The operator only handles BGT, not STAKING_TOKEN.
    /// @dev Callable by the operator or the account holder.
    /// @param account The account to get the reward for.
    /// @param recipient The address to send the reward to.
    /// @return The amount of the reward claimed.
    function getReward(address account, address recipient) external returns (uint256);

    /// @notice Stake tokens in the vault.
    /// @param amount The amount of tokens to stake.
    function stake(uint256 amount) external;

    /// @notice Stake tokens on behalf of another account.
    /// @param account The account to stake for.
    /// @param amount The amount of tokens to stake.
    function delegateStake(address account, uint256 amount) external;

    /// @notice Withdraw the staked tokens from the vault.
    /// @param amount The amount of tokens to withdraw.
    function withdraw(uint256 amount) external;

    /// @notice Withdraw tokens staked on behalf of another account by the delegate (msg.sender).
    /// @param account The account to withdraw for.
    /// @param amount The amount of tokens to withdraw.
    function delegateWithdraw(address account, uint256 amount) external;

    /// @notice Allows msg.sender to set another address to claim and manage their rewards.
    /// @param _operator The address that will be allowed to claim and manage rewards.
    function setOperator(address _operator) external;

    /// @notice Update the manager of an incentive token.
    /// @dev Permissioned function, only allow factory owner to update the manager.
    /// @param token The address of the incentive token.
    /// @param newManager The new manager of the incentive token.
    function updateIncentiveManager(address token, address newManager) external;

    /// @notice Add an incentive token to the vault.
    /// @notice The incentive token's transfer should not exceed a gas usage of 500k units.
    /// In case the transfer exceeds 500k gas units, your incentive will fail to be transferred to the validator and
    /// its delegates.
    /// @param token The address of the token to add as an incentive.
    /// @param amount The amount of the token to add as an incentive.
    /// @param incentiveRate The amount of the token to incentivize per BGT emission.
    /// @dev Permissioned function, only callable by incentive token manager.
    function addIncentive(address token, uint256 amount, uint256 incentiveRate) external;
}

File 3 of 41 : IRewardVaultFactory.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

import { IPOLErrors } from "../interfaces/IPOLErrors.sol";

interface IRewardVaultFactory is IPOLErrors {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          EVENTS                             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Emitted when a new vault is created.
     * @param stakingToken The address of the staking token.
     * @param vault The address of the vault.
     */
    event VaultCreated(address indexed stakingToken, address indexed vault);

    /**
     * @notice Emitted when the BGTIncentiveDistributor contract is set.
     * @param newBGTIncentiveDistributor The address of the new BGTIncentiveDistributor contract.
     * @param oldBGTIncentiveDistributor The address of the old BGTIncentiveDistributor contract.
     */
    event BGTIncentiveDistributorSet(
        address indexed newBGTIncentiveDistributor, address indexed oldBGTIncentiveDistributor
    );

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          ADMIN                             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Sets the BGTIncentiveDistributor contract.
     * @dev Only callable by the admin.
     * @param _bgtIncentiveDistributor The address of the new BGTIncentiveDistributor contract.
     */
    function setBGTIncentiveDistributor(address _bgtIncentiveDistributor) external;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         VAULT CREATION                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Creates a new reward vault vault for the given staking token.
     * @dev Reverts if the staking token is not a contract.
     * @param stakingToken The address of the staking token.
     * @return The address of the new vault.
     */
    function createRewardVault(address stakingToken) external returns (address);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          READS                             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Gets the VAULT_MANAGER_ROLE.
     * @return The VAULT_MANAGER_ROLE.
     */
    function VAULT_MANAGER_ROLE() external view returns (bytes32);

    /**
     * @notice Gets the VAULT_PAUSER_ROLE.
     * @return The VAULT_PAUSER_ROLE.
     */
    function VAULT_PAUSER_ROLE() external view returns (bytes32);

    /**
     * @notice Gets the vault for the given staking token.
     * @param stakingToken The address of the staking token.
     * @return The address of the vault.
     */
    function getVault(address stakingToken) external view returns (address);

    /**
     * @notice Gets the number of vaults that have been created.
     * @return The number of vaults.
     */
    function allVaultsLength() external view returns (uint256);

    /**
     * @notice Gets the address of the BGTIncentiveDistributor contract.
     * @return The address of the BGTIncentiveDistributor contract.
     */
    function bgtIncentiveDistributor() external view returns (address);

    /**
     * @notice Predicts the address of the reward vault for the given staking token.
     * @param stakingToken The address of the staking token.
     * @return The address of the reward vault.
     */
    function predictRewardVaultAddress(address stakingToken) external view returns (address);
}

File 4 of 41 : Errors.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

library Errors {
    // General errors.
    error ZeroAddress();
    error ZeroAmount();
    error UnderFlow();
    error InvalidArrayLength();
    error AlreadySet();
    error NotPauser();
    error InsufficientBalance();
    error InvalidFeeToken();
    error FeeTokenNotWhitelisted();
    error InsufficientFeeTokenBalance();

    // ValidatorSet errors.
    error ValidatorAlreadyExists();
    error FailedToAddValidator();
    error ValidatorDoesNotExist();

    // InfraredVault errors.
    error MaxNumberOfRewards();
    error Unauthorized(address sender);
    error IBGTNotRewardToken();
    error IBGTNotStakingToken();
    error StakedInRewardsVault();
    error NoRewardsVault();
    error RewardRateDecreased();
    error RegistrationPaused();
    error RewardTokenNotWhitelisted();

    // InfraredValidators errors.
    error InvalidValidator();
    error InvalidOperator();
    error InvalidDepositAmount();
    error ValidatorAlreadyRemoved();

    // Infrared errors.
    error VaultNotSupported();
    error InvalidNonce();
    error VaultNotStaked();
    error ClaimDistrRewardsFailed();
    error ClaimableRewardsExist();
    error DuplicateAssetAddress();
    error VaultDeploymentFailed();
    error RewardTokenNotSupported();
    error BGTBalanceMismatch();
    error NotInfrared();
    error NotInitialized();
    error InvalidFee();
    error InvalidCommissionRate();
    error InvalidDelegatee();
    error InvalidWeight();
    error MaxProtocolFeeAmount();
    error BoostExceedsSupply();
    error ETHTransferFailed();
    error TokensReservedForProtocolFees();
    error NoRewardsToClaim();
    error VaultAlreadyUpToDate();

    // iBERA erros
    error InvalidAmount();
    error InvalidShares();
    error WithdrawalsNotEnabled();
    error InvalidSignature();
    error InvalidReceiver();
    error CallFailed();
    error InvalidReserves();
    error UnauthorizedOperator();
    error ValidatorForceExited();
    error CanNotCompoundAccumuldatedBERA();
    error ExceedsMaxEffectiveBalance();
    error HandleForceExitsBeforeDeposits();
    error OperatorAlreadySet();
}

File 5 of 41 : MultiRewards.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import {IMultiRewards} from "../interfaces/IMultiRewards.sol";

import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {ReentrancyGuard} from
    "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

import {ERC20} from "@solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";

/**
 * @title MultiRewards
 * @dev Fork of https://github.com/curvefi/multi-rewards with hooks on stake/withdraw of LP tokens
 */
abstract contract MultiRewards is ReentrancyGuard, Pausable, IMultiRewards {
    using SafeTransferLib for ERC20;

    /*//////////////////////////////////////////////////////////////
                                STATE
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice The token that users stake to earn rewards
     * @dev This is the base token that users deposit into the contract
     */
    ERC20 public immutable stakingToken;

    /**
     * @notice Stores reward-related data for each reward token
     * @dev Maps reward token addresses to their Reward struct containing distribution parameters
     */
    mapping(address => Reward) public override rewardData;

    /**
     * @notice Array of all reward token addresses
     * @dev Used to iterate through all reward tokens when updating or claiming rewards
     */
    address[] public rewardTokens;

    /**
     * @notice Tracks the reward per token paid to each user for each reward token
     * @dev Maps user address to reward token address to amount already paid
     * Used to calculate new rewards since last claim
     */
    mapping(address => mapping(address => uint256)) public
        userRewardPerTokenPaid;

    /**
     * @notice Tracks the unclaimed rewards for each user for each reward token
     * @dev Maps user address to reward token address to unclaimed amount
     */
    mapping(address => mapping(address => uint256)) public rewards;

    /**
     * @notice The total amount of staking tokens in the contract
     * @dev Used to calculate rewards per token
     */
    uint256 internal _totalSupply;

    /**
     * @notice Maps user addresses to their staked token balance
     * @dev Internal mapping used to track individual stake amounts
     */
    mapping(address => uint256) internal _balances;

    /*//////////////////////////////////////////////////////////////
                        MODIFIERS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Updates the reward for the given account before executing the
     * function body.
     * @param account address The account to update the reward for.
     */
    modifier updateReward(address account) {
        for (uint256 i; i < rewardTokens.length; i++) {
            address token = rewardTokens[i];

            uint256 latestRewardPerToken = rewardPerToken(token);
            rewardData[token].rewardPerTokenStored = latestRewardPerToken;
            rewardData[token].lastUpdateTime = lastTimeRewardApplicable(token);

            if (account != address(0)) {
                rewards[account][token] = earned(account, token);
                userRewardPerTokenPaid[account][token] = latestRewardPerToken;
            }
        }
        _;
    }

    /*//////////////////////////////////////////////////////////////
                        CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Constructs the MultiRewards contract.
     * @param _stakingToken address The token that users stake to earn rewards.
     */
    constructor(address _stakingToken) {
        stakingToken = ERC20(_stakingToken);
    }

    /*//////////////////////////////////////////////////////////////
                               READS
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IMultiRewards
    function totalSupply() external view returns (uint256) {
        return _totalSupply;
    }

    /// @inheritdoc IMultiRewards
    function balanceOf(address account)
        external
        view
        returns (uint256 _balance)
    {
        return _balances[account];
    }

    /// @inheritdoc IMultiRewards
    function lastTimeRewardApplicable(address _rewardsToken)
        public
        view
        returns (uint256)
    {
        // min value between timestamp and period finish
        uint256 periodFinish = rewardData[_rewardsToken].periodFinish;
        uint256 ts = block.timestamp;
        return ts < periodFinish ? ts : periodFinish;
    }

    /// @inheritdoc IMultiRewards
    function rewardPerToken(address _rewardsToken)
        public
        view
        returns (uint256)
    {
        if (_totalSupply == 0) {
            return rewardData[_rewardsToken].rewardPerTokenStored;
        }
        return rewardData[_rewardsToken].rewardPerTokenStored
            + (
                lastTimeRewardApplicable(_rewardsToken)
                    - rewardData[_rewardsToken].lastUpdateTime
            ) * rewardData[_rewardsToken].rewardRate * 1e18 / _totalSupply;
    }

    /// @inheritdoc IMultiRewards
    function earned(address account, address _rewardsToken)
        public
        view
        returns (uint256)
    {
        return (
            _balances[account]
                * (
                    rewardPerToken(_rewardsToken)
                        - userRewardPerTokenPaid[account][_rewardsToken]
                )
        ) / 1e18 + rewards[account][_rewardsToken];
    }

    /// @inheritdoc IMultiRewards
    function getRewardForDuration(address _rewardsToken)
        external
        view
        returns (uint256)
    {
        return rewardData[_rewardsToken].rewardRate
            * rewardData[_rewardsToken].rewardsDuration;
    }

    /*//////////////////////////////////////////////////////////////
                            WRITES
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IMultiRewards
    function stake(uint256 amount)
        external
        nonReentrant
        whenNotPaused
        updateReward(msg.sender)
    {
        require(amount > 0, "Cannot stake 0");
        _totalSupply = _totalSupply + amount;
        _balances[msg.sender] = _balances[msg.sender] + amount;

        // transfer staking token in then hook stake, for hook to have access to collateral
        stakingToken.safeTransferFrom(msg.sender, address(this), amount);
        onStake(amount);
        emit Staked(msg.sender, amount);
    }

    /**
     * @notice Hook called in the stake function after transfering staking token in
     * @param amount The amount of staking token transferred in to the contract
     */
    function onStake(uint256 amount) internal virtual;

    /// @inheritdoc IMultiRewards
    function withdraw(uint256 amount)
        public
        nonReentrant
        updateReward(msg.sender)
    {
        require(amount > 0, "Cannot withdraw 0");
        _totalSupply = _totalSupply - amount;
        _balances[msg.sender] = _balances[msg.sender] - amount;

        // hook withdraw then transfer staking token out
        onWithdraw(amount);
        stakingToken.safeTransfer(msg.sender, amount);
        emit Withdrawn(msg.sender, amount);
    }

    /**
     * @notice Hook called in withdraw function before transferring staking token out
     * @param amount The amount of staking token to be transferred out of the contract
     */
    function onWithdraw(uint256 amount) internal virtual;

    /// @inheritdoc IMultiRewards
    function getRewardForUser(address _user)
        public
        nonReentrant
        updateReward(_user)
    {
        onReward();
        uint256 len = rewardTokens.length;
        for (uint256 i; i < len; i++) {
            address _rewardsToken = rewardTokens[i];
            uint256 reward = rewards[_user][_rewardsToken];
            if (reward > 0) {
                (bool success, bytes memory data) = _rewardsToken.call{
                    gas: 200000
                }(
                    abi.encodeWithSelector(
                        ERC20.transfer.selector, _user, reward
                    )
                );
                if (success && (data.length == 0 || abi.decode(data, (bool)))) {
                    rewards[_user][_rewardsToken] = 0;
                    emit RewardPaid(_user, _rewardsToken, reward);
                } else {
                    continue;
                }
            }
        }
    }

    /**
     * @notice Hook called in getRewardForUser function
     */
    function onReward() internal virtual;

    /// @inheritdoc IMultiRewards
    function getReward() public {
        getRewardForUser(msg.sender);
    }

    /// @inheritdoc IMultiRewards
    function exit() external {
        withdraw(_balances[msg.sender]);
        getReward();
    }

    /*//////////////////////////////////////////////////////////////
                            RESTRICTED
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Adds a reward token to the contract.
     * @param _rewardsToken       address The address of the reward token.
     * @param _rewardsDistributor address The address of the rewards distributor.
     * @param _rewardsDuration    uint256 The duration of the rewards period.
     */
    function _addReward(
        address _rewardsToken,
        address _rewardsDistributor,
        uint256 _rewardsDuration
    ) internal {
        require(rewardData[_rewardsToken].rewardsDuration == 0);
        rewardTokens.push(_rewardsToken);
        rewardData[_rewardsToken].rewardsDistributor = _rewardsDistributor;
        rewardData[_rewardsToken].rewardsDuration = _rewardsDuration;
        emit RewardStored(_rewardsToken, _rewardsDuration);
    }

    /**
     * @notice Removes a reward token from the contract.
     * @param _rewardsToken address The address of the reward token.
     */
    function _removeReward(address _rewardsToken) internal {
        require(block.timestamp >= rewardData[_rewardsToken].periodFinish);
        // Remove from the array
        for (uint256 i; i < rewardTokens.length; i++) {
            if (rewardTokens[i] == _rewardsToken) {
                rewardTokens[i] = rewardTokens[rewardTokens.length - 1];
                rewardTokens.pop();
                break;
            }
        }

        delete rewardData[_rewardsToken];
        emit RewardRemoved(_rewardsToken);
    }

    /**
     * @notice Notifies the contract that reward tokens is being sent to the contract.
     * @param _rewardsToken address The address of the reward token.
     * @param reward        uint256 The amount of reward tokens is being sent to the contract.
     */
    function _notifyRewardAmount(address _rewardsToken, uint256 reward)
        internal
        updateReward(address(0))
    {
        // handle the transfer of reward tokens via `transferFrom` to reduce the number
        // of transactions required and ensure correctness of the reward amount
        ERC20(_rewardsToken).safeTransferFrom(msg.sender, address(this), reward);
        // add in the prior residual amount and account for new residual
        // @dev residual used to account for precision loss when dividing reward by rewardsDuration
        reward = reward + rewardData[_rewardsToken].rewardResidual;
        rewardData[_rewardsToken].rewardResidual =
            reward % rewardData[_rewardsToken].rewardsDuration;
        reward = reward - rewardData[_rewardsToken].rewardResidual;

        if (block.timestamp >= rewardData[_rewardsToken].periodFinish) {
            rewardData[_rewardsToken].rewardRate =
                reward / rewardData[_rewardsToken].rewardsDuration;
        } else {
            uint256 remaining =
                rewardData[_rewardsToken].periodFinish - block.timestamp;
            uint256 leftover = remaining * rewardData[_rewardsToken].rewardRate;

            // Calculate total and its residual
            uint256 totalAmount =
                reward + leftover + rewardData[_rewardsToken].rewardResidual;
            rewardData[_rewardsToken].rewardResidual =
                totalAmount % rewardData[_rewardsToken].rewardsDuration;

            // Remove residual before setting rate
            totalAmount = totalAmount - rewardData[_rewardsToken].rewardResidual;
            rewardData[_rewardsToken].rewardRate =
                totalAmount / rewardData[_rewardsToken].rewardsDuration;
        }

        rewardData[_rewardsToken].lastUpdateTime = block.timestamp;
        rewardData[_rewardsToken].periodFinish =
            block.timestamp + rewardData[_rewardsToken].rewardsDuration;
        emit RewardAdded(_rewardsToken, reward);
    }

    /**
     * @notice Recovers ERC20 tokens sent to the contract.
     * @dev Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders
     * @param to           address The address to send the tokens to.
     * @param tokenAddress address The address of the token to withdraw.
     * @param tokenAmount  uint256 The amount of tokens to withdraw.
     */
    function _recoverERC20(
        address to,
        address tokenAddress,
        uint256 tokenAmount
    ) internal {
        require(
            rewardData[tokenAddress].lastUpdateTime == 0,
            "Cannot withdraw reward token"
        );
        ERC20(tokenAddress).safeTransfer(to, tokenAmount);
        emit Recovered(tokenAddress, tokenAmount);
    }

    /**
     * @notice Updates the reward duration for a reward token.
     * @param _rewardsToken    address The address of the reward token.
     * @param _rewardsDuration uint256 The new duration of the rewards period.
     */
    function _setRewardsDuration(
        address _rewardsToken,
        uint256 _rewardsDuration
    ) internal {
        require(_rewardsDuration > 0, "Reward duration must be non-zero");

        if (block.timestamp < rewardData[_rewardsToken].periodFinish) {
            uint256 remaining =
                rewardData[_rewardsToken].periodFinish - block.timestamp;
            uint256 leftover = remaining * rewardData[_rewardsToken].rewardRate;

            // Calculate total and its residual
            uint256 totalAmount =
                leftover + rewardData[_rewardsToken].rewardResidual;
            rewardData[_rewardsToken].rewardResidual =
                totalAmount % _rewardsDuration;

            // Remove residual before setting rate
            totalAmount = totalAmount - rewardData[_rewardsToken].rewardResidual;
            rewardData[_rewardsToken].rewardRate =
                totalAmount / _rewardsDuration;
        }

        rewardData[_rewardsToken].lastUpdateTime = block.timestamp;
        rewardData[_rewardsToken].periodFinish =
            block.timestamp + _rewardsDuration;

        rewardData[_rewardsToken].rewardsDuration = _rewardsDuration;
        emit RewardsDurationUpdated(_rewardsToken, _rewardsDuration);
    }
}

File 6 of 41 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 7 of 41 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
            // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
            success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)

            // Set success to whether the call reverted, if not we check it either
            // returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
            // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
            success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)

            // Set success to whether the call reverted, if not we check it either
            // returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
            // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
            success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)

            // Set success to whether the call reverted, if not we check it either
            // returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "APPROVE_FAILED");
    }
}

File 8 of 41 : IInfrared.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ERC20} from "@solmate/tokens/ERC20.sol";
import {IBeraChef} from "@berachain/pol/interfaces/IBeraChef.sol";
import {IRewardVaultFactory as IBerachainRewardsVaultFactory} from
    "@berachain/pol/interfaces/IRewardVaultFactory.sol";
import {IVoter} from "src/voting/interfaces/IVoter.sol";
import {IInfraredBERA} from "src/interfaces/IInfraredBERA.sol";
import {IInfraredGovernanceToken} from
    "src/interfaces/IInfraredGovernanceToken.sol";
import {IWBERA} from "src/interfaces/IWBERA.sol";
import {InfraredBGT} from "src/core/InfraredBGT.sol";
import {IBribeCollector} from "src/interfaces/IBribeCollector.sol";
import {IInfraredDistributor} from "src/interfaces/IInfraredDistributor.sol";
import {IInfraredVault} from "src/interfaces/IInfraredVault.sol";
import {DataTypes} from "src/utils/DataTypes.sol";
import {ValidatorTypes} from "src/core/libraries/ValidatorTypes.sol";
import {ConfigTypes} from "src/core/libraries/ConfigTypes.sol";

interface IInfrared {
    /**
     * @notice Checks if a token is a whitelisted reward token
     * @param _token The address of the token to check
     * @return bool True if the token is whitelisted, false otherwise
     */
    function whitelistedRewardTokens(address _token)
        external
        view
        returns (bool);

    /**
     * @notice Returns the infrared vault address for a given staking token
     * @param _asset The address of the staking asset
     * @return IInfraredVault The vault associated with the asset
     */
    function vaultRegistry(address _asset)
        external
        view
        returns (IInfraredVault);

    /**
     * @notice The InfraredBGT liquid staked token
     * @return IInfraredBGT The InfraredBGT token contract address
     */
    function ibgt() external view returns (InfraredBGT);

    /**
     * @notice The Berachain rewards vault factory address
     * @return IBerachainRewardsVaultFactory instance of the rewards factory contract address
     */
    function rewardsFactory()
        external
        view
        returns (IBerachainRewardsVaultFactory);

    /**
     * @notice The Berachain chef contract for distributing validator rewards
     * @return IBeraChef instance of the BeraChef contract address
     */
    function chef() external view returns (IBeraChef);

    /**
     * @notice The InfraredBGT vault
     * @return IInfraredVault instance of the iBGT vault contract address
     */
    function ibgtVault() external view returns (IInfraredVault);

    /**
     * @notice The unclaimed Infrared protocol fees of token accumulated by contract
     * @param token address The token address for the accumulated fees
     * @return uint256 The amount of accumulated fees
     */
    function protocolFeeAmounts(address token)
        external
        view
        returns (uint256);

    /**
     * @notice Protocol fee rates to charge for various harvest function distributions
     * @param i The index of the fee rate
     * @return uint256 The fee rate
     */
    function fees(uint256 i) external view returns (uint256);

    /**
     * @notice Wrapped bera
     * @return IWBERA The wbera token contract address
     */
    function wbera() external view returns (IWBERA);

    /**
     * @notice Honey ERC20 token
     * @return ERC20 The honey token contract address
     */
    function honey() external view returns (ERC20);

    /**
     * @notice bribe collector contract
     * @return IBribeCollector The bribe collector contract address
     */
    function collector() external view returns (IBribeCollector);

    /**
     * @notice Infrared distributor for BGT rewards to validators
     * @return IInfraredDistributor instance of the distributor contract address
     */
    function distributor() external view returns (IInfraredDistributor);

    /**
     * @notice IR voter
     * @return IVoter instance of the voter contract address
     */
    function voter() external view returns (IVoter);

    /**
     * @notice collects all iBERA realted fees and revenue
     * @return returns IInfraredBERAFeeReceivor instanace of iBeraFeeReceivor
     */
    function ibera() external view returns (IInfraredBERA);

    /**
     * @notice The IR token
     * @return IR instance of the IR token contract address
     */
    function ir() external view returns (IInfraredGovernanceToken);

    /**
     * @notice The rewards duration
     * @dev Used as gloabl variabel to set the rewards duration for all new reward tokens on InfraredVaults
     * @return uint256 The reward duration period, in seconds
     */
    function rewardsDuration() external view returns (uint256);

    /**
     * @notice Registers a new vault for a given asset
     * @dev Infrared.sol must be admin over MINTER_ROLE on InfraredBGT to grant minter role to deployed vault
     * @param _asset The address of the asset, such as a specific LP token
     * @return vault The address of the newly created InfraredVault contract
     * @custom:emits NewVault with the caller, asset address, and new vault address.
     */
    function registerVault(address _asset)
        external
        returns (IInfraredVault vault);

    /**
     * @notice Adds a new reward token to a specific staking vault
     * @dev Only callable by governance when contract is initialized
     * @param _stakingToken The address of the staking token associated with the vault
     * @param _rewardsToken The address of the token to be added as a reward
     * @param _rewardsDuration The duration period for the rewards distribution, in seconds
     * @custom:error ZeroAmount if _rewardsDuration is 0
     * @custom:error RewardTokenNotWhitelisted if _rewardsToken is not whitelisted
     * @custom:error NoRewardsVault if vault doesn't exist for _stakingToken
     */
    function addReward(
        address _stakingToken,
        address _rewardsToken,
        uint256 _rewardsDuration
    ) external;

    /**
     * @notice Adds reward incentives to a specific staking vault
     * @dev Transfers reward tokens from caller to this contract, then notifies vault of new rewards
     * @param _stakingToken The address of the staking token associated with the vault
     * @param _rewardsToken The address of the token being added as incentives
     * @param _amount The amount of reward tokens to add as incentives
     * @custom:error ZeroAmount if _amount is 0
     * @custom:error NoRewardsVault if vault doesn't exist for _stakingToken
     * @custom:error RewardTokenNotWhitelisted if reward token hasn't been configured for the vault
     * @custom:access Callable when contract is initialized
     * @custom:security Requires caller to have approved this contract to spend _rewardsToken
     */
    function addIncentives(
        address _stakingToken,
        address _rewardsToken,
        uint256 _amount
    ) external;

    /**
     * @notice Updates the whitelist status of a reward token
     * @param _token The address of the token to whitelist or remove from whitelist
     * @param _whitelisted A boolean indicating if the token should be whitelisted
     */
    function updateWhiteListedRewardTokens(address _token, bool _whitelisted)
        external;

    /**
     * @notice Sets the new duration for reward distributions in InfraredVaults
     * @param _rewardsDuration The new reward duration period, in seconds
     * @dev Only callable by governance
     */
    function updateRewardsDuration(uint256 _rewardsDuration) external;

    /**
     * @notice Updates the rewards duration for a specific reward token on a specific vault
     * @param _stakingToken The address of the staking asset associated with the vault
     * @param _rewardsToken The address of the reward token to update the duration for
     * @param _rewardsDuration The new reward duration period, in seconds
     * @dev Only callable by governance
     */
    function updateRewardsDurationForVault(
        address _stakingToken,
        address _rewardsToken,
        uint256 _rewardsDuration
    ) external;

    /**
     * @notice Pauses staking functionality on a specific vault
     * @param _asset The address of the staking asset associated with the vault to pause
     * @dev Only callable by pauser or governance, will revert if vault doesn't exist
     */
    function pauseStaking(address _asset) external;

    /**
     * @notice Un-pauses staking functionality on a specific vault
     * @param _asset The address of the staking asset associated with the vault to pause
     * @dev Only callable by gov, will revert if vault doesn't exist
     */
    function unpauseStaking(address _asset) external;

    /**
     * @notice Claims lost rewards on a specific vault
     * @param _asset The address of the staking asset associated with the vault to claim lost rewards on
     * @dev Only callable by governance, will revert if vault doesn't exist
     */
    function claimLostRewardsOnVault(address _asset) external;

    /**
     * @notice Recovers ERC20 tokens sent accidentally to the contract
     * @param _to The address to receive the recovered tokens
     * @param _token The address of the token to recover
     * @param _amount The amount of the token to recover
     */
    function recoverERC20(address _to, address _token, uint256 _amount)
        external;

    /**
     * @notice Recover ERC20 tokens from a vault.
     * @param _asset  address The address of the staking asset that the vault is for.
     * @param _to     address The address to send the tokens to.
     * @param _token  address The address of the token to recover.
     * @param _amount uint256 The amount of the token to recover.
     */
    function recoverERC20FromVault(
        address _asset,
        address _to,
        address _token,
        uint256 _amount
    ) external;

    /**
     * @notice Delegates BGT votes to `_delegatee` address.
     * @param _delegatee  address The address to delegate votes to
     */
    function delegateBGT(address _delegatee) external;

    /**
     * @notice Updates the weight for iBERA bribes
     * @param _weight uint256 The weight value
     */
    function updateInfraredBERABribeSplit(uint256 _weight) external;

    /**
     * @notice Updates the fee rate charged on different harvest functions
     * @notice Please harvest all assosiated rewards for a given type before updating
     * @dev Fee rate in units of 1e6 or hundredths of 1 bip
     * @param _t   FeeType The fee type
     * @param _fee uint256 The fee rate to update to
     */
    function updateFee(ConfigTypes.FeeType _t, uint256 _fee) external;

    /**
     * @notice Sets the address of the IR contract
     * @dev Infrared must be granted MINTER_ROLE on IR to set the address
     * @param _IR The address of the IR contract
     */
    function setIR(address _IR) external;

    /**
     * @notice Sets the address of the iBGT contract
     * @dev Infrared must be granted MINTER_ROLE on IBGT to set the address
     * @param _ibgt The address of the iBGT contract
     */
    function setIBGT(address _ibgt) external;

    /**
     * @notice Updates the mint rate for IR
     * @param _IRMintRate The new mint rate for IR
     */
    function updateIRMintRate(uint256 _IRMintRate) external;

    /**
     * @notice Claims accumulated protocol fees in contract
     * @param _to     address The recipient of the fees
     * @param _token  address The token to claim fees in
     */
    function claimProtocolFees(address _to, address _token) external;

    /**
     * @notice Claims all the BGT base and commission rewards minted to this contract for validators.
     */
    function harvestBase() external;

    /**
     * @notice Credits all accumulated rewards to the operator
     */
    function harvestOperatorRewards() external;

    /**
     * @notice Claims all the BGT rewards for the vault associated with the given staking token.
     * @param _asset address The address of the staking asset that the vault is for.
     */
    function harvestVault(address _asset) external;

    /**
     * @notice Claims all the bribes rewards in the contract forwarded from Berachain POL.
     * @param _tokens address[] memory The addresses of the tokens to harvest in the contract.
     */
    function harvestBribes(address[] memory _tokens) external;

    /**
     * @notice Collects bribes from bribe collector and distributes to wiBERA and iBGT Infrared vaults.
     * @notice _token The payout token for the bribe collector.
     * @notice _amount The amount of payout received from bribe collector.
     */
    function collectBribes(address _token, uint256 _amount) external;

    /**
     * @notice Claims all the BGT staker rewards from boosting validators.
     * @dev Sends rewards to iBGT vault.
     */
    function harvestBoostRewards() external;

    /**
     * @notice Adds validators to the set of `InfraredValidators`.
     * @param _validators Validator[] memory The validators to add.
     */
    function addValidators(ValidatorTypes.Validator[] memory _validators)
        external;

    /**
     * @notice Removes validators from the set of `InfraredValidators`.
     * @param _pubkeys bytes[] memory The pubkeys of the validators to remove.
     */
    function removeValidators(bytes[] memory _pubkeys) external;

    /**
     * @notice Replaces a validator in the set of `InfraredValidators`.
     * @param _current bytes The pubkey of the validator to replace.
     * @param _new     bytes The new validator pubkey.
     */
    function replaceValidator(bytes calldata _current, bytes calldata _new)
        external;

    /**
     * @notice Queue `_amts` of tokens to `_validators` for boosts.
     * @param _pubkeys     bytes[] memory The pubkeys of the validators to queue boosts for.
     * @param _amts        uint128[] memory The amount of BGT to boost with.
     */
    function queueBoosts(bytes[] memory _pubkeys, uint128[] memory _amts)
        external;

    /**
     * @notice Removes `_amts` from previously queued boosts to `_validators`.
     * @dev `_pubkeys` need not be in the current validator set in case just removed but need to cancel.
     * @param _pubkeys     bytes[] memory The pubkeys of the validators to remove boosts for.
     * @param _amts        uint128[] memory The amounts of BGT to remove from the queued boosts.
     */
    function cancelBoosts(bytes[] memory _pubkeys, uint128[] memory _amts)
        external;

    /**
     * @notice Activates queued boosts for `_pubkeys`.
     * @param _pubkeys   bytes[] memory The pubkeys of the validators to activate boosts for.
     */
    function activateBoosts(bytes[] memory _pubkeys) external;

    /**
     * @notice Queues a drop boost of the validators removing an amount of BGT for sender.
     * @dev Reverts if `user` does not have enough boosted balance to cover amount.
     * @param pubkeys     bytes[] memory The pubkeys of the validators to remove boost from.
     * @param amounts Amounts of BGT to remove from the queued drop boosts.
     */
    function queueDropBoosts(
        bytes[] calldata pubkeys,
        uint128[] calldata amounts
    ) external;

    /**
     * @notice Cancels a queued drop boost of the validator removing an amount of BGT for sender.
     * @param pubkeys     bytes[] memory The pubkeys of the validators to remove boost from.
     * @param amounts Amounts of BGT to remove from the queued drop boosts.
     */
    function cancelDropBoosts(
        bytes[] calldata pubkeys,
        uint128[] calldata amounts
    ) external;

    /**
     * @notice Drops an amount of BGT from an existing boost of validators by user.
     * @param pubkeys     bytes[] memory The pubkeys of the validators to remove boost from.
     */
    function dropBoosts(bytes[] calldata pubkeys) external;

    /**
     * @notice Queues a new cutting board on BeraChef for reward weight distribution for validator
     * @param _pubkey             bytes                         The pubkey of the validator to queue the cutting board for
     * @param _startBlock         uint64                        The start block for reward weightings
     * @param _weights            IBeraChef.Weight[] calldata   The weightings used when distributor calls chef to distribute validator rewards
     */
    function queueNewCuttingBoard(
        bytes calldata _pubkey,
        uint64 _startBlock,
        IBeraChef.Weight[] calldata _weights
    ) external;

    /**
     * @notice Gets the set of infrared validator pubkeys.
     * @return validators Validator[] memory The set of infrared validators.
     */
    function infraredValidators()
        external
        view
        returns (ValidatorTypes.Validator[] memory validators);

    /**
     * @notice Gets the number of infrared validators in validator set.
     * @return num uint256 The number of infrared validators in validator set.
     */
    function numInfraredValidators() external view returns (uint256);

    /**
     * @notice Checks if a validator is an infrared validator.
     * @param _pubkey    bytes      The pubkey of the validator to check.
     * @return _isValidator bool       Whether the validator is an infrared validator.
     */
    function isInfraredValidator(bytes memory _pubkey)
        external
        view
        returns (bool);

    /**
     * @notice Gets the BGT balance for this contract
     * @return bgtBalance The BGT balance held by this address
     */
    function getBGTBalance() external view returns (uint256 bgtBalance);

    /**
     * @notice Emitted when a new vault is registered
     * @param _sender The address that initiated the vault registration
     * @param _asset The address of the asset for which the vault is registered
     * @param _vault The address of the newly created vault
     */
    event NewVault(
        address _sender, address indexed _asset, address indexed _vault
    );

    /**
     * @notice Emitted when pause status for new vault registration has changed
     * @param pause True if new vault creation is paused
     */
    event VaultRegistrationPauseStatus(bool pause);

    /**
     * @notice Emitted when InfraredBGT tokens are supplied to distributor.
     * @param _ibera token the rewards are denominated in
     * @param _distributor The address of the distributor receiving the InfraredBGT tokens.
     * @param _amt The amount of WBERA tokens supplied to distributor.
     */
    event OperatorRewardsDistributed(
        address indexed _ibera, address indexed _distributor, uint256 _amt
    );

    /**
     * @notice Emitted when InfraredBGT tokens are supplied to a vault.
     * @param _vault The address of the vault receiving the InfraredBGT and IR tokens.
     * @param _ibgtAmt The amount of InfraredBGT tokens supplied to vault.
     * @param _iredAmt The amount of IR tokens supplied to vault as additional reward from protocol.
     */
    event InfraredBGTSupplied(
        address indexed _vault, uint256 _ibgtAmt, uint256 _iredAmt
    );

    /**
     * @notice Emitted when rewards are supplied to a vault.
     * @param _vault The address of the vault receiving the reward.
     * @param _token The address of the token being supplied as a reward.
     * @param _amt The amount of the reward token supplied.
     */
    event RewardSupplied(
        address indexed _vault, address indexed _token, uint256 _amt
    );

    /**
     * @notice Emitted when rewards are supplied to a vault.
     * @param _recipient The address receiving the bribe.
     * @param _token The address of the token being supplied as a bribe reward.
     * @param _amt The amount of the bribe reward token supplied.
     */
    event BribeSupplied(
        address indexed _recipient, address indexed _token, uint256 _amt
    );

    /**
     * @notice Emitted when tokens are recovered from the contract.
     * @param _sender The address that initiated the recovery.
     * @param _token The address of the token being recovered.
     * @param _amount The amount of the token recovered.
     */
    event Recovered(address _sender, address indexed _token, uint256 _amount);

    /**
     * @notice Emitted when a reward token is marked as unsupported.
     * @param _token The address of the reward token.
     */
    event RewardTokenNotSupported(address _token);

    /**
     * @notice Emitted when the InfraredBGT token address is updated.
     * @param _sender The address that initiated the update.
     * @param _oldIbgt The previous address of the InfraredBGT token.
     * @param _newIbgt The new address of the InfraredBGT token.
     */
    event InfraredBGTUpdated(
        address _sender, address _oldIbgt, address _newIbgt
    );

    /**
     * @notice Emitted when the InfraredBGT vault address is updated.
     * @param _sender The address that initiated the update.
     * @param _oldIbgtVault The previous address of the InfraredBGT vault.
     * @param _newIbgtVault The new address of the InfraredBGT vault.
     */
    event InfraredBGTVaultUpdated(
        address _sender, address _oldIbgtVault, address _newIbgtVault
    );

    /**
     * @notice Emitted when reward tokens are whitelisted or unwhitelisted.
     * @param _sender The address that initiated the update.
     * @param _token The address of the token being updated.
     * @param _wasWhitelisted The previous whitelist status of the token.
     * @param _isWhitelisted The new whitelist status of the token.
     */
    event WhiteListedRewardTokensUpdated(
        address _sender,
        address indexed _token,
        bool _wasWhitelisted,
        bool _isWhitelisted
    );

    /**
     * @notice Emitted when the rewards duration is updated
     * @param _sender The address that initiated the update
     * @param _oldDuration The previous rewards duration
     * @param _newDuration The new rewards duration
     */
    event RewardsDurationUpdated(
        address _sender, uint256 _oldDuration, uint256 _newDuration
    );

    /**
     * @notice Emitted when a weight is updated.
     * @param _sender The address that initiated the update.
     * @param _oldWeight The old value of the weight.
     * @param _newWeight The new value of the weight.
     */
    event InfraredBERABribeSplitUpdated(
        address _sender, uint256 _oldWeight, uint256 _newWeight
    );

    /**
     * @notice Emitted when protocol fee rate is updated.
     * @param _sender The address that initiated the update.
     * @param _feeType The fee type updated.
     * @param _oldFeeRate The old protocol fee rate.
     * @param _newFeeRate The new protocol fee rate.
     */
    event FeeUpdated(
        address _sender,
        ConfigTypes.FeeType _feeType,
        uint256 _oldFeeRate,
        uint256 _newFeeRate
    );

    /**
     * @notice Emitted when protocol fees claimed.
     * @param _sender The address that initiated the claim.
     * @param _to The address to send protocol fees to.
     * @param _token The address of the token protocol fees in.
     * @param _amount The amount of protocol fees claimed.
     */
    event ProtocolFeesClaimed(
        address _sender, address _to, address _token, uint256 _amount
    );

    /**
     * @notice Emitted when protocol fees are received.
     * @param _token The address of the token protocol fees in.
     * @param _amt The amount of protocol fees received.
     * @param _voterAmt The amount of protocol fees received by the voter.
     */
    event ProtocolFees(address indexed _token, uint256 _amt, uint256 _voterAmt);

    /**
     * @notice Emitted when base + commission rewards are harvested.
     * @param _sender The address that initiated the harvest.
     * @param _bgtAmt The amount of BGT harvested.
     */
    event BaseHarvested(address _sender, uint256 _bgtAmt);

    /**
     * @notice Emitted when a vault harvests its rewards.
     * @param _sender The address that initiated the harvest.
     * @param _asset The asset associated with the vault being harvested.
     * @param _vault The address of the vault being harvested.
     * @param _bgtAmt The amount of BGT harvested.
     */
    event VaultHarvested(
        address _sender,
        address indexed _asset,
        address indexed _vault,
        uint256 _bgtAmt
    );

    /**
     * @notice Emitted when bribes are harvested then collected by collector.
     * @param _sender The address that initiated the bribe collection.
     * @param _token The payout token from bribe collection.
     * @param _amtWiberaVault The amount of collected bribe sent to the wrapped iBERA vault.
     * @param _amtIbgtVault The amount of collected bribe sent to the iBGT vault.
     */
    event BribesCollected(
        address _sender,
        address _token,
        uint256 _amtWiberaVault,
        uint256 _amtIbgtVault
    );

    /**
     * @notice Emitted when a validator harvests its rewards.
     * @param _sender The address that initiated the harvest.
     * @param _validator The public key of the validator.
     * @param _rewards An array of tokens and amounts harvested.
     * @param _bgtAmt The amount of BGT included in the rewards.
     */
    event ValidatorHarvested(
        address _sender,
        bytes indexed _validator,
        DataTypes.Token[] _rewards,
        uint256 _bgtAmt
    );

    /**
     * @notice Emitted when validators are added.
     * @param _sender The address that initiated the addition.
     * @param _validators An array of validators that were added.
     */
    event ValidatorsAdded(
        address _sender, ValidatorTypes.Validator[] _validators
    );

    /**
     * @notice Emitted when validators are removed from validator set.
     * @param _sender The address that initiated the removal.
     * @param _pubkeys An array of validators' pubkeys that were removed.
     */
    event ValidatorsRemoved(address _sender, bytes[] _pubkeys);

    /**
     * @notice Emitted when a validator is replaced with a new validator.
     * @param _sender The address that initiated the replacement.
     * @param _current The pubkey of the current validator being replaced.
     * @param _new The pubkey of the new validator.
     */
    event ValidatorReplaced(address _sender, bytes _current, bytes _new);

    /**
     * @notice Emitted when BGT tokens are queued for boosts to validators.
     * @param _sender The address that initiated the boost.
     * @param _pubkeys The addresses of the validators to which tokens are queued for boosts.
     * @param _amts The amounts of tokens that were queued.
     */
    event QueuedBoosts(address _sender, bytes[] _pubkeys, uint128[] _amts);

    /**
     * @notice Emitted when existing queued boosts to validators are cancelled.
     * @param _sender The address that initiated the cancellation.
     * @param _pubkeys The pubkeys of the validators to which tokens were queued for boosts.
     * @param _amts The amounts of tokens to remove from boosts.
     */
    event CancelledBoosts(address _sender, bytes[] _pubkeys, uint128[] _amts);

    /**
     * @notice Emitted when an existing boost to a validator is activated.
     * @param _sender The address that initiated the activation.
     * @param _pubkeys The addresses of the validators which were boosted.
     */
    event ActivatedBoosts(address _sender, bytes[] _pubkeys);

    /**
     * @notice Emitted when an user queues a drop boost for a validator.
     * @param user The address of the user.
     * @param pubkeys The addresses of the validators to which tokens were queued for boosts.
     * @param amounts The amounts of tokens to remove from boosts.
     */
    event QueueDropBoosts(
        address indexed user, bytes[] indexed pubkeys, uint128[] amounts
    );

    /**
     * @notice Emitted when an user cancels a queued drop boost for a validator.
     * @param user The address of the user.
     * @param pubkeys The addresses of the validators to which tokens were queued for boosts.
     * @param amounts The amounts of tokens to remove from boosts.
     */
    event CancelDropBoosts(
        address indexed user, bytes[] indexed pubkeys, uint128[] amounts
    );

    /**
     * @notice Emitted when sender removes an amount of BGT boost from a validator
     * @param _sender The address that initiated the cancellation.
     * @param _pubkeys The addresses of the validators to which tokens were queued for boosts.
     */
    event DroppedBoosts(address indexed _sender, bytes[] _pubkeys);

    /**
     * @notice Emitted when tokens are undelegated from a validator.
     * @param _sender The address that initiated the undelegation.
     * @param _pubkey The pubkey of the validator from which tokens are undelegated.
     * @param _amt The amount of tokens that were undelegated.
     */
    event Undelegated(address _sender, bytes _pubkey, uint256 _amt);

    /**
     * @notice Emitted when tokens are redelegated from one validator to another.
     * @param _sender The address that initiated the redelegation.
     * @param _from The public key of the validator from which tokens are redelegated.
     * @param _to The public key of the validator to which tokens are redelegated.
     * @param _amt The amount of tokens that were redelegated.
     */
    event Redelegated(address _sender, bytes _from, bytes _to, uint256 _amt);

    /**
     * @notice Emitted when the IR token is set.
     * @param _sender The address that initiated the update.
     * @param _IR The address of the IRdd token.
     */
    event IRSet(address _sender, address _IR);

    /**
     *
     * @param oldMintRate The old mint rate for IR
     * @param newMintRate The new mint rate for IR
     * @param sender The address that initiated the update
     */
    event UpdatedIRMintRate(
        uint256 oldMintRate, uint256 newMintRate, address sender
    );

    /**
     * @notice Emitted when the iBGT token is set.
     * @param _sender The address that initiated the update.
     * @param _ibgt The address of the iBGT token.
     */
    event IBGTSet(address _sender, address _ibgt);

    /**
     * @notice Emitted when the voter contract is set.
     * @param _sender The address that initiated the update.
     * @param _voter The address of the voter contract.
     */
    event VoterSet(address _sender, address _voter);
}

File 9 of 41 : IInfraredVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IRewardVault as IBerachainRewardsVault} from
    "@berachain/pol/interfaces/IRewardVault.sol";
import {IMultiRewards} from "./IMultiRewards.sol";

interface IInfraredVault is IMultiRewards {
    /**
     * @notice A struct to hold a user's reward information
     * @param token The address of the reward token
     * @param amount The amount of reward tokens
     */
    struct UserReward {
        address token;
        uint256 amount;
    }

    /**
     * @notice Returns all reward tokens
     * @return An array of reward token addresses
     */
    function getAllRewardTokens() external view returns (address[] memory);

    /**
     * @notice Returns all rewards for a user
     * @notice Only up to date since the `lastUpdateTime`
     * @param _user The address of the user
     * @return An array of UserReward structs
     */
    function getAllRewardsForUser(address _user)
        external
        view
        returns (UserReward[] memory);

    /**
     * @notice Returns the Infrared protocol coordinator
     * @return The address of the Infrared contract
     */
    function infrared() external view returns (address);

    /**
     * @notice Returns the associated Berachain rewards vault
     * @return The rewards vault contract instance
     */
    function rewardsVault() external view returns (IBerachainRewardsVault);

    /**
     * @notice Updates reward duration for a specific reward token
     * @dev Only callable by Infrared contract
     * @param _rewardsToken The address of the reward token
     * @param _rewardsDuration The new duration in seconds
     * @custom:access-control Requires INFRARED_ROLE
     */
    function updateRewardsDuration(
        address _rewardsToken,
        uint256 _rewardsDuration
    ) external;

    /**
     * @notice Pauses staking functionality on a specific vault
     * @custom:access-control Requires INFRARED_ROLE
     */
    function pauseStaking() external;

    /**
     * @notice Un-pauses staking functionality on a specific vault
     * @custom:access-control Requires INFRARED_ROLE
     */
    function unpauseStaking() external;

    /**
     * @notice Adds a new reward token to the vault
     * @dev Cannot exceed maximum number of reward tokens
     * @param _rewardsToken The reward token to add
     * @param _rewardsDuration The reward period duration
     * @custom:access-control Requires INFRARED_ROLE
     */
    function addReward(address _rewardsToken, uint256 _rewardsDuration)
        external;

    /**
     * @notice Used to remove malicious or unused reward tokens
     * @param _rewardsToken The reward token to remove
     * @custom:access-control Requires INFRARED_ROLE
     */
    function removeReward(address _rewardsToken) external;

    /**
     * @notice Notifies the vault of newly added rewards
     * @dev Updates internal reward rate calculations
     * @param _rewardToken The reward token address
     * @param _reward The amount of new rewards
     */
    function notifyRewardAmount(address _rewardToken, uint256 _reward)
        external;

    /**
     * @notice Recovers accidentally sent tokens
     * @dev Cannot recover staking token or active reward tokens
     * @param _to The address to receive the recovered tokens
     * @param _token The token to recover
     * @param _amount The amount to recover
     */
    function recoverERC20(address _to, address _token, uint256 _amount)
        external;
}

File 10 of 41 : IPOLErrors.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

import { IStakingRewardsErrors } from "../../base/IStakingRewardsErrors.sol";

/// @notice Interface of POL errors
interface IPOLErrors is IStakingRewardsErrors {
    // Signature: 0xf2d81d95
    error NotApprovedSender();
    // Signature: 0x1db3b859
    error NotDelegate();
    // Signature: 0x53f0a596
    error NotBGT();
    // Signature: 0x1b0eb4ec
    error NotBlockRewardController();
    // Signature: 0x385296d5
    error NotDistributor();
    // Signature: 0x73fcd3fe
    error NotFeeCollector();
    // Signature: 0x36407850
    error NotWhitelistedVault();
    // Signature:0x7c214f04
    error NotOperator();
    // Signature: 0xad3a8b9e
    error NotEnoughBalance();
    // Signature: 0xadd377f6
    error InvalidActivateBoostDelay();
    // Signature: 0x2f14f4f9
    error InvalidDropBoostDelay();
    // Signature: 0x14969061
    error NotEnoughBoostedBalance();
    // Signature: 0xe8966d7a
    error NotEnoughTime();
    // Signature: 0xec2caa0d
    error InvalidStartBlock();
    // Signature: 0x3be31f8c
    error RewardAllocationAlreadyQueued();
    // Signature: 0x13134d24
    error InvalidRewardAllocationWeights();
    // Signature: 0xf6fae721
    error TooManyWeights();
    // Signature: 0x3e38573f
    error InvalidateDefaultRewardAllocation();
    // Signature: 0xd92e233d
    error ZeroAddress();
    // Signature: 0x462a2bb2
    error RewardAllocationBlockDelayTooLarge();
    // Signature: 0x08519afa
    error NotFactoryVault();
    // Signature: 0x978dc040
    error ZeroPercentageWeight();
    // Signature: 0xab396d11
    error InvalidCommissionValue();
    // Signature: 0x0c32c4fa
    error CommissionChangeAlreadyQueued();
    // Signature: 0xe9269446
    error CommissionNotQueuedOrDelayNotPassed();
    // Signature: 0xc1abde53
    error InvalidCommissionChangeDelay();

    /*                   BLOCK REWARD CONTROLLLER                  */

    // Signature: 0x2e2dab43
    error InvalidBaseRate();
    // Signature: 0x22be2284
    error InvalidRewardRate();
    // Signature: 0x15482337
    error InvalidMinBoostedRewardRate();
    // Signature: 0xb7b2319a
    error InvalidBoostMultiplier();
    // Signature: 0x347f95b2
    error InvalidRewardConvexity();

    /*                           STAKING                           */

    // Signature: 0x09ee12d5
    error NotAContract();
    // Signature: 0xe4ea100b
    error CannotRecoverRewardToken();
    // Signature: 0x1b813803
    error CannotRecoverStakingToken();
    // Signature: 0x2899103f
    error CannotRecoverIncentiveToken();
    // Signature: 0x38432c89
    error IncentiveRateTooHigh();
    // Signature: 0x5ee4de0e
    error NotIncentiveManager();

    // Signature: 0xf84835a0
    error TokenNotWhitelisted();
    // Signature: 0x8d1473a6
    error InsufficientDelegateStake();
    // Signature: 0x08e88f46
    error InsufficientSelfStake();
    // Signature: 0xfbf97e07
    error TokenAlreadyWhitelistedOrLimitReached();
    // Signature: 0xad57d95d
    error AmountLessThanMinIncentiveRate();
    // Signature: 0xfbf1123c
    error InvalidMaxIncentiveTokensCount();

    // Signature: 0x546c7600
    error PayoutAmountIsZero();
    // Signature: 0x89c622a2
    error DonateAmountLessThanPayoutAmount();
    // Signature: 0xa4cc22ed
    error MaxNumWeightsPerRewardAllocationIsZero();
    // Signature: 0x0b5c3aff
    error MinIncentiveRateIsZero();
    // Signature: 0x8e7572da
    error InvariantCheckFailed();

    /*                         BEACON ROOTS                        */

    // Signature: 0x1390f2a1
    error IndexOutOfRange();
    // Signature: 0x09bde339
    error InvalidProof();
    // Signature: 0x0a431b2a
    error TimestampAlreadyProcessed();

    /*                        BEACON DEPOSIT                       */

    /// @dev Error thrown when the deposit amount is too small, to prevent dust deposits.
    // Signature: 0x0e1eddda
    error InsufficientDeposit();

    /// @dev Error thrown when the deposit amount is not a multiple of Gwei.
    // Signature: 0x40567b38
    error DepositNotMultipleOfGwei();

    /// @dev Error thrown when the deposit amount is too high, since it is a uint64.
    // Signature: 0x2aa66734
    error DepositValueTooHigh();

    /// @dev Error thrown when the public key length is not 48 bytes.
    // Signature: 0x9f106472
    error InvalidPubKeyLength();

    /// @dev Error thrown when the withdrawal credentials length is not 32 bytes.
    // Signature: 0xb39bca16
    error InvalidCredentialsLength();

    /// @dev Error thrown when the signature length is not 96 bytes.
    // Signature: 0x4be6321b
    error InvalidSignatureLength();

    /// @dev Error thrown when the input operator is zero address on the first deposit.
    // Signature: 0x51969a7a
    error ZeroOperatorOnFirstDeposit();

    /// @dev Error thrown when the operator is already set and caller passed non-zero operator.
    // Signature: 0xc4142b41
    error OperatorAlreadySet();

    /// @dev Error thrown when the caller is not the current operator.
    // Signature: 0x819a0d0b
    error NotNewOperator();

    /*                        BGT INCENTIVE DISTRIBUTOR                */
    // Signature: 0x1ec5aa51
    error InvalidArray();
    // Signature: 0x68b5c198
    error InvalidDistribution();
    // Signature: 0x9dd854d3
    error InvalidMerkleRoot();
    // Signature: 0x0995309b
    error RewardInactive();
    // Signature: 0x5958c647
    error InvalidRewardClaimDelay();
    // Signature: 0x75f9d00c
    error InsufficientIncentiveTokens();
    // Signature: 0xc1ab6dc1
    error InvalidToken();
}

File 11 of 41 : IStakingRewards.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

import { IStakingRewardsErrors } from "./IStakingRewardsErrors.sol";

/// @notice Interface of staking rewards
interface IStakingRewards is IStakingRewardsErrors {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @notice Emitted when a reward has been added to the vault.
    /// @param reward The amount of reward added, scaled by PRECISION.
    event RewardAdded(uint256 reward);

    /// @notice Emitted when the staking balance of an account has increased.
    /// @param account The account that has staked.
    /// @param amount The amount of staked tokens.
    event Staked(address indexed account, uint256 amount);

    /// @notice Emitted when the staking balance of an account has decreased.
    /// @param account The account that has withdrawn.
    /// @param amount The amount of withdrawn tokens.
    event Withdrawn(address indexed account, uint256 amount);

    /// @notice Emitted when a reward has been claimed.
    /// @param account The account whose reward has been claimed.
    /// @param to The address that the reward was sent to. (user or operator).
    /// @param reward The amount of reward claimed.
    event RewardPaid(address indexed account, address to, uint256 reward);

    /// @notice Emitted when the reward duration has been updated.
    /// @param newDuration The new duration of the reward.
    event RewardsDurationUpdated(uint256 newDuration);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          GETTERS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @notice Get the balance of the staked tokens for an account.
    /// @param account The account to get the balance for.
    /// @return The balance of the staked tokens.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Get the reward balance for a specific account.
    /// @param account The account to retrieve the reward balance for.
    /// @return The current reward balance of the specified account.
    function rewards(address account) external view returns (uint256);

    /// @notice Get the user reward per token paid.
    /// @param account The account to retrieve the reward for.
    /// @return The current reward balance of the specified account.
    function userRewardPerTokenPaid(address account) external view returns (uint256);

    /// @notice Retrieves the amount of reward earned by a specific account.
    /// @param account The account to calculate the reward for.
    /// @return The amount of reward earned by the account.
    function earned(address account) external view returns (uint256);

    /// @notice Retrieves the total reward vested over the specified duration.
    /// @return The total reward vested over the duration.
    function getRewardForDuration() external view returns (uint256);

    /// @notice Returns the timestamp of the last reward distribution. This is either the current timestamp (if rewards
    /// are still being actively distributed) or the timestamp when the reward duration ended (if all rewards have
    /// already been distributed).
    /// @return The timestamp of the last reward distribution.
    function lastTimeRewardApplicable() external view returns (uint256);

    /// @notice Retrieves the current value of the global reward per token accumulator. This value is the sum of the
    /// last checkpoint value and the accumulated value since the last checkpoint. It should increase monotonically
    /// over time as more rewards are distributed.
    /// @return The current value of the global reward per token accumulator scaled by 1e18.
    function rewardPerToken() external view returns (uint256);

    /// @notice Get the total supply of the staked tokens in the vault.
    /// @return The total supply of the staked tokens in the vault.
    function totalSupply() external view returns (uint256);

    /// @notice Get the end of the current reward period.
    /// @return The end of the current reward period.
    function periodFinish() external view returns (uint256);

    /// @notice Get the reward rate for the current reward period.
    /// @return The reward rate.
    function rewardRate() external view returns (uint256);

    /// @notice Get the time over which the rewards will be distributed.
    /// @return The duration of the rewards cycle.
    function rewardsDuration() external view returns (uint256);

    /// @notice Get the last time the rewards were updated.
    /// @return The last time the rewards were updated.
    function lastUpdateTime() external view returns (uint256);

    /// @notice Get the amount of undistributed rewards.
    /// @return The amount of undistributed rewards.
    function undistributedRewards() external view returns (uint256);

    /// @notice Get the last updated reward per token scaled.
    /// @return The last updated reward per token.
    function rewardPerTokenStored() external view returns (uint256);
}

File 12 of 41 : IMultiRewards.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IMultiRewards {
    /**
     * @notice Emitted when tokens are staked
     * @param user The address of the user who staked
     * @param amount The amount of tokens staked
     */
    event Staked(address indexed user, uint256 amount);

    /**
     * @notice Emitted when tokens are withdrawn
     * @param user The address of the user who withdrew
     * @param amount The amount of tokens withdrawn
     */
    event Withdrawn(address indexed user, uint256 amount);

    /**
     * @notice Emitted when rewards are claimed
     * @param user The address of the user claiming the reward
     * @param rewardsToken The address of the reward token
     * @param reward The amount of rewards claimed
     */
    event RewardPaid(
        address indexed user, address indexed rewardsToken, uint256 reward
    );

    /**
     * @notice Emitted when rewards are added to the contract
     * @param rewardsToken The address of the reward token
     * @param reward The amount of rewards added
     */
    event RewardAdded(address indexed rewardsToken, uint256 reward);

    /**
     * @notice Emitted when a reward is removed from the contract
     */
    event RewardRemoved(address indexed rewardsToken);

    /**
     * @notice Emitted when a rewards distributor is updaRewardAddedd
     * @param rewardsToken The address of the reward token
     * @param newDistributor The address of the new distributor
     */
    event RewardsDistributorUpdated(
        address indexed rewardsToken, address indexed newDistributor
    );

    /**
     * @notice Emitted when the rewards duration for a token is updated
     * @param token The reward token address whose duration was updated
     * @param newDuration The new duration set for the rewards period
     */
    event RewardsDurationUpdated(address token, uint256 newDuration);

    /**
     * @notice Emitted when tokens are recovered from the contract
     * @param token The address of the token that was recovered
     * @param amount The amount of tokens that were recovered
     */
    event Recovered(address token, uint256 amount);

    /**
     * @notice Emitted when new reward data is stored
     * @param rewardsToken The address of the reward token
     * @param rewardsDuration The duration set for the reward period
     */
    event RewardStored(address rewardsToken, uint256 rewardsDuration);

    /**
     * @notice Reward data for a particular reward token
     * @dev Struct containing all relevant information for reward distribution
     */
    struct Reward {
        address rewardsDistributor;
        uint256 rewardsDuration;
        uint256 periodFinish;
        uint256 rewardRate;
        uint256 lastUpdateTime;
        uint256 rewardPerTokenStored;
        uint256 rewardResidual;
    }

    /**
     * @notice Returns the total amount of staked tokens in the contract
     * @return uint256 The total supply of staked tokens
     */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Stakes tokens into the contract
     * @param amount The amount of tokens to stake
     * @dev Transfers `amount` of staking tokens from the user to this contract
     */
    function stake(uint256 amount) external;

    /**
     * @notice Withdraws staked tokens from the contract
     * @param amount The amount of tokens to withdraw
     * @dev Transfers `amount` of staking tokens back to the user
     */
    function withdraw(uint256 amount) external;

    /**
     * @notice Claims all pending rewards for the caller
     * @dev Transfers all accrued rewards to the caller
     */
    function getReward() external;

    /**
     * @notice Withdraws all staked tokens and claims pending rewards
     * @dev Combines withdraw and getReward operations
     */
    function exit() external;

    /**
     * @notice Returns the balance of staked tokens for the given account
     * @param account The account to get the balance for
     * @return The balance of staked tokens
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @notice Calculates the last time reward is applicable for a given rewards token
     * @param _rewardsToken The address of the rewards token
     * @return The timestamp when the reward was last applicable
     */
    function lastTimeRewardApplicable(address _rewardsToken)
        external
        view
        returns (uint256);

    /**
     * @notice Calculates the reward per token for a given rewards token
     * @param _rewardsToken The address of the rewards token
     * @return The reward amount per token
     */
    function rewardPerToken(address _rewardsToken)
        external
        view
        returns (uint256);

    /**
     * @notice Calculates the earned rewards for a given account and rewards token
     * @param account The address of the account
     * @param _rewardsToken The address of the rewards token
     * @return The amount of rewards earned
     */
    function earned(address account, address _rewardsToken)
        external
        view
        returns (uint256);

    /**
     * @notice Calculates the total reward for the duration of a given rewards token
     * @param _rewardsToken The address of the rewards token
     * @return The total reward amount for the duration of a given rewards token
     */
    function getRewardForDuration(address _rewardsToken)
        external
        view
        returns (uint256);

    /**
     * @notice Gets the reward data for a given rewards token
     * @param _rewardsToken The address of the rewards token
     * @return rewardsDistributor The address authorized to distribute rewards
     * @return rewardsDuration The duration of the reward period
     * @return periodFinish The timestamp when rewards finish
     * @return rewardRate The rate of rewards distributed per second
     * @return lastUpdateTime The last time rewards were updated
     * @return rewardPerTokenStored The last calculated reward per token
     */
    function rewardData(address _rewardsToken)
        external
        view
        returns (
            address rewardsDistributor,
            uint256 rewardsDuration,
            uint256 periodFinish,
            uint256 rewardRate,
            uint256 lastUpdateTime,
            uint256 rewardPerTokenStored,
            uint256 rewardResidual
        );

    /**
     * @notice Returns the reward token address at a specific index
     * @param index The index in the reward tokens array
     * @return The address of the reward token at the given index
     */
    function rewardTokens(uint256 index) external view returns (address);

    /**
     * @notice Claims all pending rewards for a specified user
     * @dev Iterates through all reward tokens and transfers any accrued rewards to the user
     * @param _user The address of the user to claim rewards for
     */
    function getRewardForUser(address _user) external;
}

File 13 of 41 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 14 of 41 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

File 15 of 41 : IBeraChef.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

import { IPOLErrors } from "./IPOLErrors.sol";

/// @notice Interface of the BeraChef module
interface IBeraChef is IPOLErrors {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STRUCTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @notice Represents a RewardAllocation entry
    struct RewardAllocation {
        // The block this reward allocation goes into effect.
        uint64 startBlock;
        // The weights of the reward allocation.
        Weight[] weights;
    }

    /// @notice Represents a Weight entry
    struct Weight {
        // The address of the receiver that this weight is for.
        address receiver;
        // The fraction of rewards going to this receiver.
        // the percentage denominator is: ONE_HUNDRED_PERCENT = 10000
        // the actual fraction is: percentageNumerator / ONE_HUNDRED_PERCENT
        // e.g. percentageNumerator for 50% is 5000, because 5000 / 10000 = 0.5
        uint96 percentageNumerator;
    }

    /// @notice The struct of validator queued commission rate changes on incentive tokens.
    /// @param blockNumberLast The last block number commission rate was queued
    /// @param commissionRate The queued commission rate to be used by the validator
    struct QueuedCommissionRateChange {
        uint32 blockNumberLast;
        uint96 commissionRate;
    }

    /// @notice Commission rate of a validator on incentive tokens
    /// @param activationBlock The block number in which the commission rate was activated
    /// @param commissionRate The commission rate to be used by the validator
    struct CommissionRate {
        uint32 activationBlock;
        uint96 commissionRate;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @notice Emitted when the maximum number of weights per reward allocation has been set.
    /// @param maxNumWeightsPerRewardAllocation The maximum number of weights per reward allocation.
    event MaxNumWeightsPerRewardAllocationSet(uint8 maxNumWeightsPerRewardAllocation);

    /// @notice Emitted when the delay in blocks before a new reward allocation can go into effect has been set.
    /// @param rewardAllocationBlockDelay The delay in blocks before a new reward allocation can go into effect.
    event RewardAllocationBlockDelaySet(uint64 rewardAllocationBlockDelay);

    /// @notice Emitted when the vault's whitelisted status have been updated.
    /// @param receiver The address to remove or add as whitelisted vault.
    /// @param isWhitelisted The whitelist status; true if the receiver is being whitelisted, false otherwise.
    /// @param metadata The metadata of the vault.
    event VaultWhitelistedStatusUpdated(address indexed receiver, bool indexed isWhitelisted, string metadata);

    /**
     * @notice Emitted when the metadata of a whitelisted vault has been updated.
     * @param receiver The address of the whitelisted vault.
     * @param metadata The metadata of the vault.
     */
    event WhitelistedVaultMetadataUpdated(address indexed receiver, string metadata);

    /**
     * @notice Emitted when a new reward allocation has been queued.
     * @param valPubkey The validator's pubkey.
     * @param startBlock The block that the reward allocation goes into effect.
     * @param weights The weights of the reward allocation.
     */
    event QueueRewardAllocation(bytes indexed valPubkey, uint64 startBlock, Weight[] weights);

    /**
     * @notice Emitted when a new reward allocation has been activated.
     * @param valPubkey The validator's pubkey.
     * @param startBlock The block that the reward allocation goes into effect.
     * @param weights The weights of the reward allocation.
     */
    event ActivateRewardAllocation(bytes indexed valPubkey, uint64 startBlock, Weight[] weights);

    /**
     * @notice Emitted when the governance module has set a new default reward allocation.
     * @param rewardAllocation The default reward allocation.
     */
    event SetDefaultRewardAllocation(RewardAllocation rewardAllocation);

    /**
     * @notice Emitted when the commission change delay has been set.
     * @param commissionChangeDelay The delay in blocks to activate a queued commission change.
     */
    event CommissionChangeDelaySet(uint64 commissionChangeDelay);

    /**
     * @notice Emitted when a validator's commission rate on incentive tokens has been queued.
     * @param valPubkey The validator's pubkey.
     * @param queuedCommissionRate The queued commission rate of the validator on the incentive tokens.
     */
    event QueuedValCommission(bytes indexed valPubkey, uint96 queuedCommissionRate);

    /**
     * @notice Emitted when the commission rate of a validator on incentive tokens has been set.
     * @param valPubkey The validator's pubkey.
     * @param oldCommissionRate The old commission rate of the validator on the incentive tokens.
     * @param newCommissionRate The new commission rate of the validator on the incentive tokens.
     */
    event ValCommissionSet(bytes indexed valPubkey, uint96 oldCommissionRate, uint96 newCommissionRate);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          GETTERS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Returns the active reward allocation for validator with given pubkey
     * @param valPubkey The validator's pubkey.
     * @return rewardAllocation The active reward allocation.
     */
    function getActiveRewardAllocation(bytes calldata valPubkey) external view returns (RewardAllocation memory);

    /**
     * @notice Returns the queued reward allocation for a validator with given pubkey
     * @param valPubkey The validator's pubkey.
     * @return rewardAllocation The queued reward allocation.
     */
    function getQueuedRewardAllocation(bytes calldata valPubkey) external view returns (RewardAllocation memory);

    /**
     * @notice Returns the active reward allocation set by the validator with given pubkey.
     * @dev This will return active reward allocation set by validators even if its not valid.
     * @param valPubkey The validator's pubkey.
     * @return rewardAllocation The reward allocation.
     */
    function getSetActiveRewardAllocation(bytes calldata valPubkey) external view returns (RewardAllocation memory);

    /**
     * @notice Returns the default reward allocation for validators that do not have a reward allocation.
     * @return rewardAllocation The default reward allocation.
     */
    function getDefaultRewardAllocation() external view returns (RewardAllocation memory);

    /**
     * @notice Returns the status of whether a queued reward allocation is ready to be activated.
     * @param valPubkey The validator's pubkey.
     * @param blockNumber The block number to be queried.
     * @return isReady True if the queued reward allocation is ready to be activated, false otherwise.
     */
    function isQueuedRewardAllocationReady(
        bytes calldata valPubkey,
        uint256 blockNumber
    )
        external
        view
        returns (bool);

    /**
     * @notice Returns the status of whether the BeraChef contract is ready to be used.
     * @dev This function should be used by all contracts that depend on a system call.
     * @dev This will return false if the governance module has not set a default reward allocation yet.
     * @return isReady True if the BeraChef is ready to be used, false otherwise.
     */
    function isReady() external view returns (bool);

    /**
     * @notice Returns the queued commission struct of a validator on an incentive tokens.
     * @param valPubkey The validator's pubkey.
     * @return queuedCommissionRate The queued commission struct of the validator on the incentive tokens.
     */
    function getValQueuedCommissionOnIncentiveTokens(bytes calldata valPubkey)
        external
        view
        returns (QueuedCommissionRateChange memory);

    /**
     * @notice Returns the commission rate of a validator on an incentive tokens.
     * @dev Default commission rate is 5% if the commission was never set.
     * @param valPubkey The validator's pubkey.
     * @return commissionRate The commission rate of the validator on the incentive tokens.
     */
    function getValCommissionOnIncentiveTokens(bytes calldata valPubkey) external view returns (uint96);

    /**
     * @notice Returns the validator's share of the incentive tokens based on the validator's commission rate.
     * @param valPubkey The validator's pubkey.
     * @param incentiveTokenAmount The amount of the incentive tokens.
     * @return validatorShare The validator's share of the incentive tokens.
     */
    function getValidatorIncentiveTokenShare(
        bytes calldata valPubkey,
        uint256 incentiveTokenAmount
    )
        external
        view
        returns (uint256);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ADMIN FUNCTIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @notice Sets the maximum number of weights per reward allocation.
    function setMaxNumWeightsPerRewardAllocation(uint8 _maxNumWeightsPerRewardAllocation) external;

    /// @notice Sets the delay in blocks before a new reward allocation can be queued.
    function setRewardAllocationBlockDelay(uint64 _rewardAllocationBlockDelay) external;

    /**
     * @notice Updates the vault's whitelisted status
     * @notice The caller of this function must be the governance module account.
     * @param receiver The address to remove or add as whitelisted vault.
     * @param isWhitelisted The whitelist status; true if the receiver is being whitelisted, false otherwise.
     * @param metadata The metadata of the vault.
     */
    function setVaultWhitelistedStatus(address receiver, bool isWhitelisted, string memory metadata) external;

    /**
     * @notice Updates the metadata of a whitelisted vault, reverts if vault is not whitelisted.
     * @notice The caller of this function must be the governance module account.
     * @param receiver The address of the whitelisted vault.
     * @param metadata The metadata of the vault, to associate info with the vault in the events log.
     */
    function updateWhitelistedVaultMetadata(address receiver, string memory metadata) external;

    /**
     * @notice Sets the default reward allocation for validators that do not have a reward allocation.
     * @dev The caller of this function must be the governance module account.
     * @param rewardAllocation The default reward allocation.
     */
    function setDefaultRewardAllocation(RewardAllocation calldata rewardAllocation) external;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          SETTERS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /**
     * @notice Add a new reward allocation to the queue for validator with given pubkey. Does not allow overwriting of
     * existing queued reward allocation.
     * @dev The weights of the reward allocation must add up to 100% or 1e4.
     * Only whitelisted pools may be used as well.
     * @param valPubkey The validator's pubkey.
     * @param startBlock The block that the reward allocation goes into effect.
     * @param weights The weights of the reward allocation.
     */
    function queueNewRewardAllocation(
        bytes calldata valPubkey,
        uint64 startBlock,
        Weight[] calldata weights
    )
        external;

    /**
     * @notice Activates the queued reward allocation for a validator if its ready for the current block.
     * @dev Should be called by the distribution contract.
     * @param valPubkey The validator's pubkey.
     */
    function activateReadyQueuedRewardAllocation(bytes calldata valPubkey) external;

    /**
     * @notice Sets the commission change delay.
     * @dev Only owner can call this function.
     * @param _commissionChangeDelay The delay in blocks to activate a queued commission change.
     */
    function setCommissionChangeDelay(uint64 _commissionChangeDelay) external;

    /**
     * @notice Queues a commission rate change for a validator on incentive tokens.
     * @dev The caller of this function must be the validator operator address.
     * @dev Revert if already a commission rate change is queued.
     * @param valPubkey The validator's pubkey.
     * @param commissionRate The commission rate of the validator on the incentive tokens.
     */
    function queueValCommission(bytes calldata valPubkey, uint96 commissionRate) external;

    /**
     * @notice Activates the queued commission rate of a validator on incentive tokens.
     * @param valPubkey The validator's pubkey.
     */
    function activateQueuedValCommission(bytes calldata valPubkey) external;
}

File 16 of 41 : IVoter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @title IVoter
 * @notice Interface for Infrared's voting system that manages votes for POL CuttingBoard allocation
 * and bribe vault creation
 * @dev Handles voting power allocation, managed veNFT deposits, and bribe distribution
 */
interface IVoter {
    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/

    /* @notice Thrown when attempting to vote or deposit when already done in current epoch */
    error AlreadyVotedOrDeposited();
    /* @notice Thrown when attempting to kill an already killed bribe vault */
    error BribeVaultAlreadyKilled();
    /* @notice Thrown when attempting to revive an already active bribe vault */
    error BribeVaultAlreadyRevived();
    /* @notice Thrown when attempting to create a bribe vault that already exists */
    error BribeVaultExists();
    /* @notice Thrown when attempting to interact with a non-existent bribe vault */
    error BribeVaultDoesNotExist(address _stakingToken);
    /* @notice Thrown when attempting to interact with an inactive bribe vault */
    error BribeVaultNotAlive(address _stakingToken);
    /* @notice Thrown when attempting to interact with an inactive managed NFT */
    error InactiveManagedNFT();
    /* @notice Thrown when setting maximum voting number below required threshold */
    error MaximumVotingNumberTooLow();
    /* @notice Thrown when attempting to reset with active votes */
    error NonZeroVotes();
    /* @notice Thrown when token provided is not a valid staking token */
    error NotAStakingToken();
    /* @notice Thrown when caller is not approved or owner of the token */
    error NotApprovedOrOwner();
    /* @notice Thrown when NFT is not in whitelist */
    error NotWhitelistedNFT();
    /* @notice Thrown when token is not in whitelist */
    error NotWhitelistedToken();
    /* @notice Thrown when new value matches current value */
    error SameValue();
    /* @notice Thrown when operation attempted during privileged voting window */
    error SpecialVotingWindow();
    /* @notice Thrown when too many staking tokens provided */
    error TooManyStakingTokens();
    /* @notice Thrown when array lengths don't match */
    error UnequalLengths();
    /* @notice Thrown when balance is zero */
    error ZeroBalance();
    /* @notice Thrown when zero address provided */
    error ZeroAddress();
    /* @notice Thrown when vault is not registered */
    error VaultNotRegistered();
    /* @notice Thrown when caller is not governor */
    error NotGovernor();
    /* @notice Thrown when operation attempted during distribution window */
    error DistributeWindow();

    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Emitted when a new bribe vault is created
     * @param stakingToken The staking token address for which the vault was created
     * @param bribeVault The address of the newly created bribe vault
     * @param creator The address that created the bribe vault
     */
    event BribeVaultCreated(
        address stakingToken, address bribeVault, address creator
    );

    /**
     * @notice Emitted when a bribe vault is killed (disabled)
     * @param bribeVault The address of the killed bribe vault
     */
    event BribeVaultKilled(address indexed bribeVault);

    /**
     * @notice Emitted when a killed bribe vault is revived (re-enabled)
     * @param bribeVault The address of the revived bribe vault
     */
    event BribeVaultRevived(address indexed bribeVault);

    /**
     * @notice Emitted when votes are cast for a staking token
     * @param voter Address of the account casting the vote
     * @param stakingToken The staking token being voted for
     * @param tokenId ID of the veNFT used to vote
     * @param weight Vote weight allocated
     * @param totalWeight New total vote weight for the staking token
     * @param timestamp Block timestamp when vote was cast
     */
    event Voted(
        address indexed voter,
        address indexed stakingToken,
        uint256 indexed tokenId,
        uint256 weight,
        uint256 totalWeight,
        uint256 timestamp
    );

    /**
     * @notice Emitted when votes are withdrawn/reset
     * @param voter Address of the account withdrawing votes
     * @param stakingToken The staking token votes are withdrawn from
     * @param tokenId ID of the veNFT used to vote
     * @param weight Vote weight withdrawn
     * @param totalWeight New total vote weight for the staking token
     * @param timestamp Block timestamp when votes were withdrawn
     */
    event Abstained(
        address indexed voter,
        address indexed stakingToken,
        uint256 indexed tokenId,
        uint256 weight,
        uint256 totalWeight,
        uint256 timestamp
    );

    /**
     * @notice Emitted when an NFT's whitelist status changes
     * @param whitelister Address making the whitelist change
     * @param tokenId ID of the NFT being whitelisted/unwhitelisted
     * @param _bool New whitelist status
     */
    event WhitelistNFT(
        address indexed whitelister, uint256 indexed tokenId, bool indexed _bool
    );

    /**
     * @notice Emitted when a killed bribe vault is skipped
     * @param stakingToken Address of staking token for vault to skip
     * @param tokenId ID of the veNFT used to vote
     */
    event SkipKilledBribeVault(
        address indexed stakingToken, uint256 indexed tokenId
    );

    /**
     * @notice Emitted when maximum voting number is set
     * @param maxVotingNum New maximum number of allowed votes
     */
    event MaxVotingNumSet(uint256 indexed maxVotingNum);

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the VotingEscrow contract address
     * @return Address of the VE token that governs these contracts
     */
    function ve() external view returns (address);

    /**
     * @notice Returns total voting weight across all votes
     * @return Total weight sum of all active votes
     */
    function totalWeight() external view returns (uint256);

    /**
     * @notice Returns maximum number of staking tokens one voter can vote for
     * @return Maximum number of allowed votes per voter
     */
    function maxVotingNum() external view returns (uint256);

    /**
     * @notice Returns global fee distribution vault address
     * @return Address of the fee vault
     */
    function feeVault() external view returns (address);

    /**
     * @notice Returns bribe vault address for a given staking token
     * @param stakingToken Address of staking token
     * @return Address of associated bribe vault
     */
    function bribeVaults(address stakingToken)
        external
        view
        returns (address);

    /**
     * @notice Returns total weight allocated to a staking token
     * @param stakingToken Address of staking token
     * @return Total voting weight for the token
     */
    function weights(address stakingToken) external view returns (uint256);

    /**
     * @notice Returns vote weight allocated by token ID for specific staking token
     * @param tokenId NFT token ID
     * @param stakingToken Address of staking token
     * @return Vote weight allocated
     */
    function votes(uint256 tokenId, address stakingToken)
        external
        view
        returns (uint256);

    /**
     * @notice Returns total vote weight used by specific token ID
     * @param tokenId NFT token ID
     * @return Total used voting weight
     */
    function usedWeights(uint256 tokenId) external view returns (uint256);

    /**
     * @notice Returns timestamp of last vote for a token ID
     * @param tokenId NFT token ID
     * @return Timestamp of last vote
     */
    function lastVoted(uint256 tokenId) external view returns (uint256);

    /**
     * @notice Checks if a token is whitelisted for rewards
     * @param token Address of token to check
     * @return True if token is whitelisted
     */
    function isWhitelistedToken(address token) external view returns (bool);

    /**
     * @notice Checks if NFT is whitelisted for special voting
     * @param tokenId NFT token ID to check
     * @return True if NFT is whitelisted
     */
    function isWhitelistedNFT(uint256 tokenId) external view returns (bool);

    /**
     * @notice Checks if bribe vault is active
     * @param bribeVault Address of bribe vault to check
     * @return True if vault is active
     */
    function isAlive(address bribeVault) external view returns (bool);

    /**
     * @notice Returns number of staking tokens with active bribe vaults
     * @return Count of staking tokens with bribe vaults
     */
    function length() external view returns (uint256);

    /*//////////////////////////////////////////////////////////////
                        TIME HELPER FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Calculates start of epoch containing timestamp
     * @param _timestamp Input timestamp
     * @return Start of epoch time
     */
    function epochStart(uint256 _timestamp) external pure returns (uint256);

    /**
     * @notice Calculates start of next epoch after timestamp
     * @param _timestamp Input timestamp
     * @return Start of next epoch time
     */
    function epochNext(uint256 _timestamp) external pure returns (uint256);

    /**
     * @notice Calculates start of voting window for epoch containing timestamp
     * @param _timestamp Input timestamp
     * @return Vote window start time
     */
    function epochVoteStart(uint256 _timestamp)
        external
        pure
        returns (uint256);

    /**
     * @notice Calculates end of voting window for epoch containing timestamp
     * @param _timestamp Input timestamp
     * @return Vote window end time
     */
    function epochVoteEnd(uint256 _timestamp) external pure returns (uint256);

    /*//////////////////////////////////////////////////////////////
                        EXTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice Updates voting balances in rewards contracts for a token ID
     * @dev Should be called after any action that affects vote weight
     * @param _tokenId veNFT token ID to update
     */
    function poke(uint256 _tokenId) external;

    /**
     * @notice Distributes voting weight to multiple staking tokens
     * @dev Weight is allocated proportionally based on provided weights
     * @param _tokenId veNFT token ID voting with
     * @param _stakingTokenVote Array of staking token addresses receiving votes
     * @param _weights Array of weights to allocate to each token
     */
    function vote(
        uint256 _tokenId,
        address[] calldata _stakingTokenVote,
        uint256[] calldata _weights
    ) external;

    /**
     * @notice Resets voting state for a token ID
     * @dev Required before making changes to veNFT state
     * @param _tokenId veNFT token ID to reset
     */
    function reset(uint256 _tokenId) external;

    /**
     * @notice Deposits veNFT into a managed NFT
     * @dev NFT will be re-locked to max time on withdrawal
     * @param _tokenId veNFT token ID to deposit
     * @param _mTokenId Managed NFT token ID to deposit into
     */
    function depositManaged(uint256 _tokenId, uint256 _mTokenId) external;

    /**
     * @notice Withdraws veNFT from a managed NFT
     * @dev Withdrawing locks NFT to max lock time
     * @param _tokenId veNFT token ID to withdraw
     */
    function withdrawManaged(uint256 _tokenId) external;

    /**
     * @notice Claims bribes from multiple sources for a veNFT
     * @param _bribes Array of bribe vault addresses to claim from
     * @param _tokens Array of reward tokens to claim for each vault
     * @param _tokenId veNFT token ID to claim for
     */
    function claimBribes(
        address[] memory _bribes,
        address[][] memory _tokens,
        uint256 _tokenId
    ) external;

    /**
     * @notice Claims fee rewards for a veNFT
     * @param _tokens Array of fee tokens to claim
     * @param _tokenId veNFT token ID to claim for
     */
    function claimFees(address[] memory _tokens, uint256 _tokenId) external;

    /**
     * @notice Updates maximum allowed votes per voter
     * @param _maxVotingNum New maximum number of allowed votes
     */
    function setMaxVotingNum(uint256 _maxVotingNum) external;

    /**
     * @notice Updates whitelist status for veNFT for privileged voting
     * @param _tokenId veNFT token ID to update
     * @param _bool New whitelist status
     */
    function whitelistNFT(uint256 _tokenId, bool _bool) external;

    /**
     * @notice Creates new bribe vault for staking token
     * @param _stakingToken Address of staking token
     * @param _rewardTokens Array of reward token addresses
     * @return Address of created bribe vault
     */
    function createBribeVault(
        address _stakingToken,
        address[] calldata _rewardTokens
    ) external returns (address);

    /**
     * @notice Disables a bribe vault
     * @param _stakingToken Address of staking token for vault to disable
     */
    function killBribeVault(address _stakingToken) external;

    /**
     * @notice Re-enables a disabled bribe vault
     * @param _stakingToken Address of staking token for vault to re-enable
     */
    function reviveBribeVault(address _stakingToken) external;
}

File 17 of 41 : IInfraredBERA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IInfraredBERA is IERC20, IAccessControl {
    /// @notice Emitted when InfraredBERA is minted
    /// @param receiver The address receiving the minted shares
    /// @param amount The amount of BERA deposited
    /// @param shares The amount of shares minted
    event Mint(address indexed receiver, uint256 amount, uint256 shares);

    /// @notice Emitted when InfraredBERA is burned
    /// @param receiver The address receiving the withdrawn BERA
    /// @param nonce The withdrawal nonce
    /// @param amount The amount of BERA to withdraw
    /// @param shares The amount of shares burned
    /// @param fee The fee paid for withdrawal
    event Burn(
        address indexed receiver,
        uint256 nonce,
        uint256 amount,
        uint256 shares,
        uint256 fee
    );

    /// @notice Emitted when accumulated rewards are swept
    /// @param amount The amount of BERA swept
    event Sweep(uint256 amount);

    /// @notice Emitted when validator stake is registered
    /// @param pubkey The validator's public key
    /// @param delta The change in stake amount
    /// @param stake The new total stake amount
    event Register(bytes pubkey, int256 delta, uint256 stake);

    /// @notice Emitted when fee shareholders rate is updated
    /// @param from Previous fee rate
    /// @param to New fee rate
    event SetFeeShareholders(uint16 from, uint16 to);

    /// @notice Emitted when deposit signature is updated
    /// @param pubkey The validator's public key
    /// @param from Previous signature
    /// @param to New signature
    event SetDepositSignature(bytes pubkey, bytes from, bytes to);

    /// @notice Emitted when withdrawal flag is updated
    /// @param flag New withdrawal flag value
    event WithdrawalFlagSet(bool flag);

    /// @notice The `Infrared.sol` smart contract
    function infrared() external view returns (address);

    /// @notice The `InfraredBERADepositor.sol` smart contract
    function depositor() external view returns (address);

    /// @notice The `InfraredBERAWithdrawor.sol` smart contract
    function withdrawor() external view returns (address);

    /// @notice The `InfraredBERAFeeReceivor.sol` smart contract
    function receivor() external view returns (address);

    /// @notice The total amount of `BERA` deposited by the system
    function deposits() external view returns (uint256);

    /// @notice Returns the amount of `BERA` staked in validator with given pubkey
    function stakes(bytes calldata pubkey) external view returns (uint256);

    /// @notice Returns whether initial deposit has been staked to validator with given pubkey
    function staked(bytes calldata pubkey) external view returns (bool);

    /// @notice Returns whether a validator pubkey has exited
    function hasExited(bytes calldata pubkey) external view returns (bool);

    /// @notice Returns the deposit signature to use for given pubkey
    function signatures(bytes calldata pubkey)
        external
        view
        returns (bytes memory);

    /// @notice The fee divisor for protocol + operator + voter fees. 1/N, where N is the divisor. example 100 = 1/100 = 1%
    function feeDivisorShareholders() external view returns (uint16);

    /// @notice Pending deposits yet to be forwarded to CL
    function pending() external view returns (uint256);

    /// @notice Confirmed deposits sent to CL, total - future deposits
    function confirmed() external view returns (uint256);

    /// @notice Checks if account has the keeper role
    /// @param account The address to check
    /// @return True if the account has the keeper role
    function keeper(address account) external view returns (bool);

    /// @notice Checks if account has the governance role
    /// @param account The address to check
    /// @return True if the account has the governance role
    function governor(address account) external view returns (bool);

    /// @notice Checks if a given pubkey is a validator in the `Infrared` contract
    /// @param pubkey The pubkey to check
    /// @return True if the pubkey is a validator
    function validator(bytes calldata pubkey) external view returns (bool);

    /// @notice Previews the amount of InfraredBERA shares that would be minted for a given BERA amount
    /// @param beraAmount The amount of BERA to simulate depositing
    /// @return shares The amount of InfraredBERA shares that would be minted, returns 0 if the operation would fail
    function previewMint(uint256 beraAmount)
        external
        view
        returns (uint256 shares);

    /// @notice Previews the amount of BERA that would be received for burning InfraredBERA shares
    /// @param shareAmount The amount of InfraredBERA shares to simulate burning
    /// @return beraAmount The amount of BERA that would be received, returns 0 if the operation would fail
    /// @return fee The fee that would be charged for the burn operation
    function previewBurn(uint256 shareAmount)
        external
        view
        returns (uint256 beraAmount, uint256 fee);

    /// @notice Initiializer for `InfraredBERA`
    /// @param _gov The address of the governance contract
    /// @param _keeper The address of the keeper contract
    /// @param _infrared The address of the `Infrared.sol` contract
    /// @param _depositor The address of the `InfraredBERADepositor.sol` contract
    /// @param _withdrawor The address of the `InfraredBERAWithdrawor.sol` contract
    /// @param _receivor The address of the `InfraredBERAFeeReceivor.sol` contract
    function initialize(
        address _gov,
        address _keeper,
        address _infrared,
        address _depositor,
        address _withdrawor,
        address _receivor
    ) external payable;

    /// @notice Internal function to update top level accounting and compound rewards
    function compound() external;

    /// @notice Compounds accumulated EL yield in fee receivor into deposits
    /// @dev Called internally at bof whenever InfraredBERA minted or burned
    /// @dev Only sweeps if amount transferred from fee receivor would exceed min deposit thresholds
    function sweep() external payable;

    /// @notice Collects yield from fee receivor and mints ibera shares to Infrared
    /// @dev Called in `RewardsLib::harvestOperatorRewards()` in `Infrared.sol`
    /// @dev Only Infrared can call this function
    /// @return sharesMinted The amount of ibera shares
    function collect() external returns (uint256 sharesMinted);

    /// @notice Mints `ibera` to the `receiver` in exchange for `bera`
    /// @dev takes in msg.value as amount to mint `ibera` with
    /// @param receiver The address to mint `ibera` to
    /// @return shares The amount of `ibera` minted
    function mint(address receiver) external payable returns (uint256 shares);

    /// @notice Burns `ibera` from the `msg.sender` and sets a receiver to get the `BERA` in exchange for `iBERA`
    /// @param receiver The address to send the `BERA` to
    /// @param shares The amount of `ibera` to burn
    /// @return nonce The nonce of the withdrawal. Queue based system for withdrawals
    /// @return amount The amount of `BERA` withdrawn for the exchange of `iBERA`
    function burn(address receiver, uint256 shares)
        external
        payable
        returns (uint256 nonce, uint256 amount);

    /// @notice Updates the accounted for stake of a validator pubkey
    /// @param pubkey The pubkey of the validator
    /// @param delta The change in stake
    function register(bytes calldata pubkey, int256 delta) external;

    /// @notice Sets the fee shareholders taken on yield from EL coinbase priority fees + MEV
    /// @param to The new fee shareholders represented as an integer denominator (1/x)%
    function setFeeDivisorShareholders(uint16 to) external;

    /// @notice Sets the deposit signature for a given pubkey. Ensure that the pubkey has signed the correct deposit amount of `INITIAL_DEPOSIT`
    /// @param pubkey The pubkey to set the deposit signature for
    /// @param signature The signature to set for the pubkey
    function setDepositSignature(
        bytes calldata pubkey,
        bytes calldata signature
    ) external;

    /// @notice Whether withdrawals are currently enabled
    function withdrawalsEnabled() external view returns (bool);
}

File 18 of 41 : IInfraredGovernanceToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {IERC20Mintable} from "./IERC20Mintable.sol";

interface IInfraredGovernanceToken is IERC20Mintable, IAccessControl {
    /// @notice The address of the InfraredBGT token
    function ibgt() external view returns (address);
    /// @notice The address of the Infrared contract
    function infrared() external view returns (address);
    /// @notice returns paused status of the contract
    function paused() external view returns (bool);
}

File 19 of 41 : IWBERA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @notice The deposit function allows users to deposit EVM balance (BERA) into the contract.
 * It mints an equivalent amount of WBERA tokens and assigns them to the sender.
 */
interface IWBERA is IERC20 {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
}

File 20 of 41 : InfraredBGT.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import {ERC20PresetMinterPauser} from "../vendors/ERC20PresetMinterPauser.sol";

/**
 * @title InfraredBGT
 * @notice This contract is the InfraredBGT token.
 */
contract InfraredBGT is ERC20PresetMinterPauser {
    /**
     * @notice Constructor for InfraredBGT
     * @param _admin The admin address to controll the roles of the contract
     * @param _minter The minter address of the contract
     * @param _pauser The pauser address of the contract
     * @param _burner The burner address of the contract
     */
    constructor(
        address _admin,
        address _minter,
        address _pauser,
        address _burner
    )
        ERC20PresetMinterPauser(
            "Infrared BGT",
            "iBGT",
            _admin,
            _minter,
            _pauser,
            _burner
        )
    {}
}

File 21 of 41 : IBribeCollector.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {IPOLErrors} from "@berachain/pol/interfaces/IPOLErrors.sol";

interface IBribeCollector is IPOLErrors {
    /**
     * @notice Emitted when the payout amount is updated by the governor
     * @param oldPayoutAmount Previous payout amount
     * @param newPayoutAmount New payout amount set
     */
    event PayoutAmountSet(
        uint256 indexed oldPayoutAmount, uint256 indexed newPayoutAmount
    );

    /**
     * @notice Emitted when the fees are claimed
     * @param caller Caller of the `claimFees` function
     * @param recipient The address to which collected POL bribes will be transferred
     * @param feeToken The address of the fee token to collect
     * @param amount The amount of fee token to transfer
     */
    event FeesClaimed(
        address indexed caller,
        address indexed recipient,
        address indexed feeToken,
        uint256 amount
    );

    /**
     * @notice Token used for fee payments when claiming bribes
     * @return Address of the payout token
     */
    function payoutToken() external view returns (address);

    /**
     * @notice The amount of payout token that is required to claim POL bribes for all tokens
     * @dev This works as first come first serve basis. whoever pays this much amount of the payout amount first will
     * get the fees
     */
    function payoutAmount() external view returns (uint256);

    /**
     * @notice Update the payout amount to a new value. Must be called by governor
     * @param _newPayoutAmount The value that will be the new payout amount
     */
    function setPayoutAmount(uint256 _newPayoutAmount) external;

    /**
     * @notice Claims accumulated bribes in exchange for payout token
     * @dev Caller must approve payoutAmount of payout token to this contract.
     * @param _recipient The Address to receive claimed tokens
     * @param _feeTokens Array of token addresses to claim
     * @param _feeAmounts Array of amounts to claim for each fee token
     */
    function claimFees(
        address _recipient,
        address[] calldata _feeTokens,
        uint256[] calldata _feeAmounts
    ) external;
}

File 22 of 41 : IInfraredDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ERC20} from "@solmate/tokens/ERC20.sol";

/**
 * @title Infrared Distributor Interface
 * @notice Interface for distributing validator commissions and rewards
 * @dev Handles reward distribution snapshots and claiming logic for validators
 */
interface IInfraredDistributor {
    /**
     * @notice Emitted when validator is added to commission-eligible set
     * @param pubkey Validator's public key
     * @param operator Address authorized to claim rewards
     * @param amountCumulative Starting point for commission stream
     */
    event Added(bytes pubkey, address operator, uint256 amountCumulative);

    /**
     * @notice Emitted when validator is removed from commission-eligible set
     * @param pubkey Validator's public key
     * @param operator Address previously authorized for claims
     * @param amountCumulative Final point for commission stream
     */
    event Removed(bytes pubkey, address operator, uint256 amountCumulative);

    /**
     * @notice Emitted when validator is fully purged from registry
     * @param pubkey Validator's public key
     * @param validator Address being purged
     */
    event Purged(bytes pubkey, address validator);

    /**
     * @notice Emitted when new commission rewards are added
     * @param amount New rewards being distributed
     * @param num Current number of eligible validators
     */
    event Notified(uint256 amount, uint256 num);

    /**
     * @notice Emitted when validator claims their commission
     * @param pubkey Claiming validator's public key
     * @param validator Address authorized for claims
     * @param recipient Address receiving the commission
     * @param amount Amount of commission claimed
     */
    event Claimed(
        bytes pubkey, address validator, address recipient, uint256 amount
    );

    /**
     * @notice Reward accumulation checkpoints for validators
     * @dev Used to calculate claimable rewards between snapshots
     */
    struct Snapshot {
        /**
         * @notice Last claimed reward accumulator value
         */
        uint256 amountCumulativeLast;
        /**
         * @notice Final reward accumulator value (set on removal)
         */
        uint256 amountCumulativeFinal;
    }

    /**
     * @notice Token used for reward distributions
     * @return The ERC20 token interface of the reward token
     */
    function token() external view returns (ERC20);

    /**
     * @notice Tracks reward amount accumulation per validator
     * @return Current cumulative amount of rewards
     */
    function amountsCumulative() external view returns (uint256);

    /**
     * @notice Get validator's reward snapshots
     * @param pubkey Validator's public key
     * @return amountCumulativeLast Last claimed accumulator value
     * @return amountCumulativeFinal Final accumulator value if removed
     * @dev Returns (0,0) if validator doesn't exist
     */
    function getSnapshot(bytes calldata pubkey)
        external
        view
        returns (uint256 amountCumulativeLast, uint256 amountCumulativeFinal);

    /**
     * @notice Get validator's registered claim address
     * @param pubkey Validator's public key
     * @return Address authorized to claim validator rewards
     */
    function getValidator(bytes calldata pubkey)
        external
        view
        returns (address);

    /**
     * @notice Register new validator for rewards
     * @dev Only callable by Infrared contract
     * @param pubkey Validator's public key
     * @param validator Address authorized to claim rewards
     * @custom:access-control Requires INFRARED_ROLE
     * @custom:error ValidatorAlreadyExists if validator already registered
     */
    function add(bytes calldata pubkey, address validator) external;

    /**
     * @notice Removes validator from reward-eligible set
     * @dev Only callable by Infrared contract
     * @param pubkey Validator's public key
     * @custom:access-control Requires INFRARED_ROLE
     */
    function remove(bytes calldata pubkey) external;

    /**
     * @notice Purges validator from registry completely
     * @dev Only possible after all rewards are claimed
     * @param pubkey Validator's public key
     * @custom:error ClaimableRewardsExist if unclaimed rewards remain
     */
    function purge(bytes calldata pubkey) external;

    /**
     * @notice Distributes new commission rewards to validator set
     * @param amount Amount to distribute equally among validators
     * @custom:error ZeroAmount if amount is 0
     * @custom:error InvalidValidator if no validators exist
     */
    function notifyRewardAmount(uint256 amount) external;

    /**
     * @notice Claims outstanding commission rewards
     * @param pubkey Validator's public key
     * @param recipient Address to receive the claimed rewards
     * @custom:error InvalidValidator if caller not authorized
     * @custom:error ZeroAmount if no rewards to claim
     */
    function claim(bytes calldata pubkey, address recipient) external;
}

File 23 of 41 : DataTypes.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {EnumerableSet} from
    "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

library DataTypes {
    // Struct for ERC20 token information.
    struct Token {
        address tokenAddress;
        uint256 amount;
    }

    /// @dev The address of the native asset as of EIP-7528.
    address public constant NATIVE_ASSET =
        0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
}

File 24 of 41 : ValidatorTypes.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

library ValidatorTypes {
    /// @notice Validator information for validator set
    struct Validator {
        /// pubkey of the validator for beacon deposit contract
        bytes pubkey;
        /// address of the validator for claiming infrared rewards
        address addr;
    }
}

File 25 of 41 : ConfigTypes.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

library ConfigTypes {
    /// @notice Fee type enum for determining rates to charge on reward distribution.
    enum FeeType {
        HarvestOperatorFeeRate,
        HarvestOperatorProtocolRate,
        HarvestVaultFeeRate,
        HarvestVaultProtocolRate,
        HarvestBribesFeeRate,
        HarvestBribesProtocolRate,
        HarvestBoostFeeRate,
        HarvestBoostProtocolRate
    }
}

File 26 of 41 : IStakingRewardsErrors.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

/// @notice Interface of staking rewards errors
interface IStakingRewardsErrors {
    // Signature: 0xf4ba521f
    error InsolventReward();
    // Signature: 0xf1bc94d2
    error InsufficientStake();
    // Signature: 0x49835af0
    error RewardCycleNotEnded();
    // Signature: 0x5ce91fd0
    error StakeAmountIsZero();
    // Signature: 0xe5cfe957
    error TotalSupplyOverflow();
    // Signature: 0xa393d14b
    error WithdrawAmountIsZero();
    // Signature: 0x359f174d
    error RewardsDurationIsZero();
}

File 27 of 41 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 28 of 41 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

File 29 of 41 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

File 30 of 41 : IERC20Mintable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IERC20Mintable is IERC20 {
    function MINTER_ROLE() external view returns (bytes32);

    function mint(address to, uint256 amount) external;
}

File 31 of 41 : ERC20PresetMinterPauser.sol
// SPDX-License-Identifier: BUSL-1.1
// OpenZeppelin Contracts (last updated v4.5.0)
// (token/ERC20/presets/ERC20PresetMinterPauser.sol)

/* solhint-disable */
pragma solidity ^0.8.0;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {AccessControlEnumerable} from
    "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {CustomPausable} from "./CustomPausable.sol";

/**
 * @dev {ERC20} token, including:
 *
 *  - ability for holders to burn (destroy) their tokens
 *  - a minter role that allows for token minting (creation)
 *  - a pauser role that allows to stop all token transfers
 *
 * This contract uses {AccessControl} to lock permissioned functions using the
 * different roles - head to its documentation for details.
 *
 * The account that deploys the contract will be granted the minter and pauser
 * roles, as well as the default admin role, which will let it grant both minter
 * and pauser roles to other accounts.
 *
 * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._
 */
contract ERC20PresetMinterPauser is
    Context,
    AccessControlEnumerable,
    CustomPausable
{
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
     * account that deploys the contract.
     *
     * See {ERC20-constructor}.
     */
    constructor(
        string memory name,
        string memory symbol,
        address _admin,
        address _minter,
        address _pauser,
        address _burner
    ) ERC20(name, symbol) {
        _grantRole(DEFAULT_ADMIN_ROLE, _admin);

        _grantRole(MINTER_ROLE, _minter);
        _grantRole(PAUSER_ROLE, _pauser);
        if (_burner != address(0)) {
            _grantRole(BURNER_ROLE, _burner);
        }
    }

    /**
     * @dev Creates `amount` new tokens for `to`.
     *
     * See {ERC20-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     */
    function mint(address to, uint256 amount) public virtual whenNotPaused {
        require(
            hasRole(MINTER_ROLE, _msgSender()),
            "ERC20PresetMinterPauser: must have minter role to mint"
        );
        _mint(to, amount);
    }

    /**
     * @dev Burn `amount` new tokens from `from`.
     *
     * See {ERC20-_burn}.
     *
     * Requirements:
     *
     * - the caller must have the `BURNER_ROLE`.
     */
    function burn(uint256 amount) public virtual whenNotPaused {
        require(
            hasRole(BURNER_ROLE, _msgSender()),
            "ERC20PresetMinterPauser: must have burner role to burn"
        );
        _burn(msg.sender, amount);
    }

    /**
     * @dev Pauses all token transfers.
     *
     * See {ERC20Pausable} and {Pausable-_pause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function pause() public virtual {
        require(
            hasRole(PAUSER_ROLE, _msgSender()),
            "ERC20PresetMinterPauser: must have pauser role to pause"
        );
        _pause();
    }

    /**
     * @dev Unpauses all token transfers.
     *
     * See {ERC20Pausable} and {Pausable-_unpause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function unpause() public virtual {
        require(
            hasRole(PAUSER_ROLE, _msgSender()),
            "ERC20PresetMinterPauser: must have pauser role to unpause"
        );
        _unpause();
    }

    function _update(address from, address to, uint256 value)
        internal
        virtual
        override(CustomPausable)
    {
        super._update(from, to, value);
    }
}

File 32 of 41 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }
}

File 33 of 41 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     *
     * ```solidity
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

File 34 of 41 : AccessControlEnumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/AccessControlEnumerable.sol)

pragma solidity ^0.8.20;

import {IAccessControlEnumerable} from "./IAccessControlEnumerable.sol";
import {AccessControl} from "../AccessControl.sol";
import {EnumerableSet} from "../../utils/structs/EnumerableSet.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(bytes32 role => EnumerableSet.AddressSet) private _roleMembers;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Return all accounts that have `role`
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function getRoleMembers(bytes32 role) public view virtual returns (address[] memory) {
        return _roleMembers[role].values();
    }

    /**
     * @dev Overload {AccessControl-_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
        bool granted = super._grantRole(role, account);
        if (granted) {
            _roleMembers[role].add(account);
        }
        return granted;
    }

    /**
     * @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
        bool revoked = super._revokeRole(role, account);
        if (revoked) {
            _roleMembers[role].remove(account);
        }
        return revoked;
    }
}

File 35 of 41 : CustomPausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/ERC20Pausable.sol)

pragma solidity ^0.8.20;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";

/**
 * @dev ERC-20 token with pausable token transfers, minting and burning.
 *
 * Useful for scenarios such as preventing trades until the end of an evaluation
 * period, or having an emergency switch for freezing all token transfers in the
 * event of a large bug.
 *
 * IMPORTANT: This contract does not include public pause and unpause functions. In
 * addition to inheriting this contract, you must define both functions, invoking the
 * {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
 * access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
 * make the contract pause mechanism of the contract unreachable, and thus unusable.
 */
abstract contract CustomPausable is ERC20, Pausable {
    /**
     * @dev See {ERC20-_update}.
     *
     * This contract modifies the default behavior of ERC20Pausable.
     *
     * - When paused, transfers are not paused.
     * - The `whenNotPaused` modifier has been removed to allow transfers even when the contract is paused.
     * - Only minting and burning operations are paused when the contract is paused.
     */
    function _update(address from, address to, uint256 value)
        internal
        virtual
        override
    {
        super._update(from, to, value);
    }
}

File 36 of 41 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 37 of 41 : draft-IERC6093.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC-20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC-721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC-1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

File 38 of 41 : IAccessControlEnumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/IAccessControlEnumerable.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "../IAccessControl.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC-165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

File 39 of 41 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

File 40 of 41 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 41 of 41 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * 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[ERC 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);
}

Settings
{
  "remappings": [
    "@forge-std/=lib/forge-std/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@solmate/=lib/solmate/src/",
    "solady/src/=lib/solady/src/",
    "solady/test/=lib/solady/test/",
    "@berachain/=lib/contracts/src/",
    "@prb/=lib/prb-test/src/",
    "@forge-safe/=lib/forge-safe/src/",
    "@mock/=lib/contracts/test/mock/",
    "@openzeppelin-gov-ext/=lib/contracts/node_modules/@openzeppelin/contracts-upgradeable/governance/extensions/",
    "@openzeppelin-gov/=lib/contracts/node_modules/@openzeppelin/contracts-upgradeable/governance/",
    "@pythnetwork/=lib/contracts/node_modules/@pythnetwork/pyth-sdk-solidity/",
    "contracts/=lib/contracts/src/",
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-safe/=lib/forge-safe/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-foundry-upgrades/=lib/contracts/lib/openzeppelin-foundry-upgrades/src/",
    "prb-test/=lib/prb-test/src/",
    "solidity-stringutils/=lib/contracts/lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/",
    "solmate/=lib/solmate/src/",
    "surl/=lib/forge-safe/lib/surl/",
    "transient-goodies/=lib/contracts/lib/transient-goodies/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_stakingToken","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"MaxNumberOfRewards","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardsToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"rewardsToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardsToken","type":"address"}],"name":"RewardRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"rewardsToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardsDuration","type":"uint256"}],"name":"RewardStored","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardsToken","type":"address"},{"indexed":true,"internalType":"address","name":"newDistributor","type":"address"}],"name":"RewardsDistributorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"RewardsDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"MAX_NUM_REWARD_TOKENS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"_balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"_rewardsToken","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAllRewardTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getAllRewardsForUser","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct IInfraredVault.UserReward[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"}],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getRewardForUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"infrared","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"}],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardToken","type":"address"},{"internalType":"uint256","name":"_reward","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"}],"name":"removeReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardData","outputs":[{"internalType":"address","name":"rewardsDistributor","type":"address"},{"internalType":"uint256","name":"rewardsDuration","type":"uint256"},{"internalType":"uint256","name":"periodFinish","type":"uint256"},{"internalType":"uint256","name":"rewardRate","type":"uint256"},{"internalType":"uint256","name":"lastUpdateTime","type":"uint256"},{"internalType":"uint256","name":"rewardPerTokenStored","type":"uint256"},{"internalType":"uint256","name":"rewardResidual","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"}],"name":"rewardPerToken","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":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsVault","outputs":[{"internalType":"contract IRewardVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpauseStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"},{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"updateRewardsDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userRewardPerTokenPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561000f575f80fd5b50600436106101c6575f3560e01c806372f702f3116100fe578063bcd110141161009e578063ef790a821161006e578063ef790a82146104b7578063f1229777146104ca578063f65ae959146104dd578063f999c506146104e5575f80fd5b8063bcd110141461045f578063c004ac6114610472578063e70b9e2714610485578063e9fad8ee146104af575f80fd5b80639feb8f50116100d95780639feb8f5014610413578063a4d5e67c14610426578063a694fc3a14610439578063b66503cf1461044c575f80fd5b806372f702f3146103d15780637bb7bed1146103f857806393f4bcde1461040b575f80fd5b80633d18b912116101695780635c975abb116101445780635c975abb14610356578063638634ee1461036c5780637035ab981461037f57806370a08231146103a9575f80fd5b80633d18b9121461029457806348e5d9f81461029c5780635579ed011461032f575f80fd5b8063211dc32d116101a4578063211dc32d1461020f57806321f96c65146102225780632a170546146102615780632e1a7d4d14610281575f80fd5b80631171bda9146101ca57806312edb24c146101df57806318160ddd146101fd575b5f80fd5b6101dd6101d8366004612104565b6104ed565b005b6101e76105a8565b6040516101f49190612142565b60405180910390f35b6006545b6040519081526020016101f4565b61020161021d36600461218d565b610608565b6102497f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc12681565b6040516001600160a01b0390911681526020016101f4565b61027461026f3660046121c4565b6106a0565b6040516101f491906121e6565b6101dd61028f366004612232565b610870565b6101dd610a57565b6102f06102aa3660046121c4565b600260208190525f918252604090912080546001820154928201546003830154600484015460058501546006909501546001600160a01b03909416959492939192909187565b604080516001600160a01b0390981688526020880196909652948601939093526060850191909152608084015260a083015260c082015260e0016101f4565b6102497f000000000000000000000000347106734e7b129dde92333a0007d64a4b08e26681565b60015460ff1660405190151581526020016101f4565b61020161037a3660046121c4565b610a62565b61020161038d36600461218d565b600460209081525f928352604080842090915290825290205481565b6102016103b73660046121c4565b6001600160a01b03165f9081526007602052604090205490565b6102497f00000000000000000000000026bbc26415c6316890565f5f73017f85ee70b60c81565b610249610406366004612232565b610a95565b6101dd610abd565b6101dd610421366004612249565b610b1b565b6101dd6104343660046121c4565b610c9d565b6101dd610447366004612232565b610d18565b6101dd61045a366004612249565b610ef8565b61020161046d3660046121c4565b610f94565b6101dd610480366004612249565b610fc0565b61020161049336600461218d565b600560209081525f928352604080842090915290825290205481565b6101dd61105c565b6101dd6104c53660046121c4565b61107c565b6102016104d83660046121c4565b611318565b610201600a81565b6101dd6113c3565b336001600160a01b037f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc126161461053d5760405163472511eb60e11b81523360048201526024015b60405180910390fd5b6001600160a01b038316158061055a57506001600160a01b038216155b156105785760405163d92e233d60e01b815260040160405180910390fd5b805f0361059857604051631f2a200560e01b815260040160405180910390fd5b6105a3838383611420565b505050565b606060038054806020026020016040519081016040528092919081815260200182805480156105fe57602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116105e0575b5050505050905090565b6001600160a01b038083165f818152600560209081526040808320948616808452948252808320549383526004825280832094835293905291822054670de0b6b3a76400009061065785611318565b6106619190612287565b6001600160a01b0386165f90815260076020526040902054610683919061229a565b61068d91906122c5565b61069791906122d8565b90505b92915050565b6003546060905f8167ffffffffffffffff8111156106c0576106c06122eb565b60405190808252806020026020018201604052801561070457816020015b604080518082019091525f80825260208201528152602001906001900390816106de5790505b5090505f805b838110156107bb575f610743876003848154811061072a5761072a6122ff565b5f918252602090912001546001600160a01b0316610608565b905080156107b257604051806040016040528060038481548110610769576107696122ff565b5f91825260209182902001546001600160a01b03168252018290528451859085908110610798576107986122ff565b602002602001018190525082806107ae90612313565b9350505b5060010161070a565b505f8167ffffffffffffffff8111156107d6576107d66122eb565b60405190808252806020026020018201604052801561081a57816020015b604080518082019091525f80825260208201528152602001906001900390816107f45790505b5090505f5b8281101561086657838181518110610839576108396122ff565b6020026020010151828281518110610853576108536122ff565b602090810291909101015260010161081f565b5095945050505050565b6108786114e4565b335f5b600354811015610959575f60038281548110610899576108996122ff565b5f9182526020822001546001600160a01b031691506108b782611318565b6001600160a01b0383165f90815260026020526040902060050181905590506108df82610a62565b6001600160a01b038084165f9081526002602052604090206004019190915584161561094f5761090f8483610608565b6001600160a01b038086165f8181526005602090815260408083209488168084529482528083209590955591815260048252838120928152919052208190555b505060010161087b565b505f821161099d5760405162461bcd60e51b8152602060048201526011602482015270043616e6e6f74207769746864726177203607c1b6044820152606401610534565b816006546109ab9190612287565b600655335f908152600760205260409020546109c8908390612287565b335f908152600760205260409020556109e08261150c565b610a146001600160a01b037f00000000000000000000000026bbc26415c6316890565f5f73017f85ee70b60c163384611585565b60405182815233907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5906020015b60405180910390a250610a5460015f55565b50565b610a603361107c565b565b6001600160a01b0381165f9081526002602081905260408220015442818110610a8b5781610a8d565b805b949350505050565b60038181548110610aa4575f80fd5b5f918252602090912001546001600160a01b0316905081565b336001600160a01b037f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc1261614610b085760405163472511eb60e11b8152336004820152602401610534565b60015460ff1615610a6057610a60611614565b336001600160a01b037f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc1261614610b665760405163472511eb60e11b8152336004820152602401610534565b6001600160a01b038216610b8d5760405163d92e233d60e01b815260040160405180910390fd5b805f03610bad57604051631f2a200560e01b815260040160405180910390fd5b600354600a148015610c5057507f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc1266001600160a01b0316635db4cd216040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c16573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c3a919061232b565b6001600160a01b0316826001600160a01b031614155b15610c6e5760405163aa4f98e760e01b815260040160405180910390fd5b610c99827f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc12683611666565b5050565b336001600160a01b037f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc1261614610ce85760405163472511eb60e11b8152336004820152602401610534565b6001600160a01b038116610d0f5760405163d92e233d60e01b815260040160405180910390fd5b610a5481611730565b610d206114e4565b610d286118c8565b335f5b600354811015610e09575f60038281548110610d4957610d496122ff565b5f9182526020822001546001600160a01b03169150610d6782611318565b6001600160a01b0383165f9081526002602052604090206005018190559050610d8f82610a62565b6001600160a01b038084165f90815260026020526040902060040191909155841615610dff57610dbf8483610608565b6001600160a01b038086165f8181526005602090815260408083209488168084529482528083209590955591815260048252838120928152919052208190555b5050600101610d2b565b505f8211610e4a5760405162461bcd60e51b815260206004820152600e60248201526d043616e6e6f74207374616b6520360941b6044820152606401610534565b81600654610e5891906122d8565b600655335f90815260076020526040902054610e759083906122d8565b335f81815260076020526040902091909155610ebd907f00000000000000000000000026bbc26415c6316890565f5f73017f85ee70b60c6001600160a01b03169030856118ec565b610ec682611989565b60405182815233907f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d90602001610a42565b336001600160a01b037f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc1261614610f435760405163472511eb60e11b8152336004820152602401610534565b6001600160a01b038216610f6a5760405163d92e233d60e01b815260040160405180910390fd5b805f03610f8a57604051631f2a200560e01b815260040160405180910390fd5b610c998282611a2a565b6001600160a01b0381165f908152600260205260408120600181015460039091015461069a919061229a565b336001600160a01b037f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc126161461100b5760405163472511eb60e11b8152336004820152602401610534565b6001600160a01b0382166110325760405163d92e233d60e01b815260040160405180910390fd5b805f0361105257604051631f2a200560e01b815260040160405180910390fd5b610c998282611da3565b335f9081526007602052604090205461107490610870565b610a60610a57565b6110846114e4565b805f5b600354811015611165575f600382815481106110a5576110a56122ff565b5f9182526020822001546001600160a01b031691506110c382611318565b6001600160a01b0383165f90815260026020526040902060050181905590506110eb82610a62565b6001600160a01b038084165f9081526002602052604090206004019190915584161561115b5761111b8483610608565b6001600160a01b038086165f8181526005602090815260408083209488168084529482528083209590955591815260048252838120928152919052208190555b5050600101611087565b5061116e611f78565b6003545f5b8181101561130c575f6003828154811061118f5761118f6122ff565b5f9182526020808320909101546001600160a01b03888116845260058352604080852091909216808552925290912054909150801561130157604080516001600160a01b038881166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291515f9283929086169162030d409161122791612346565b5f604051808303815f8787f1925050503d805f8114611261576040519150601f19603f3d011682016040523d82523d5f602084013e611266565b606091505b5091509150818015611290575080511580611290575080806020019051810190611290919061235c565b156112f5576001600160a01b038881165f8181526005602090815260408083209489168084529482528083209290925590518681527f540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e910160405180910390a36112fe565b50505050611304565b50505b50505b600101611173565b505050610a5460015f55565b5f6006545f0361134057506001600160a01b03165f9081526002602052604090206005015490565b6006546001600160a01b0383165f908152600260205260409020600381015460049091015461136e85610a62565b6113789190612287565b611382919061229a565b61139490670de0b6b3a764000061229a565b61139e91906122c5565b6001600160a01b0383165f9081526002602052604090206005015461069a91906122d8565b336001600160a01b037f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc126161461140e5760405163472511eb60e11b8152336004820152602401610534565b60015460ff16610a6057610a6061200a565b6001600160a01b0382165f90815260026020526040902060040154156114885760405162461bcd60e51b815260206004820152601c60248201527f43616e6e6f742077697468647261772072657761726420746f6b656e000000006044820152606401610534565b61149c6001600160a01b0383168483611585565b604080516001600160a01b0384168152602081018390527f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa2891015b60405180910390a1505050565b60025f540361150657604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b604051632e1a7d4d60e01b8152600481018290527f000000000000000000000000347106734e7b129dde92333a0007d64a4b08e2666001600160a01b031690632e1a7d4d906024015b5f604051808303815f87803b15801561156c575f80fd5b505af115801561157e573d5f803e3d5ffd5b5050505050565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f5114161516156115cf5750823b153d17155b8061160e5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610534565b50505050565b61161c612045565b6001805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6001600160a01b0383165f908152600260205260409020600101541561168a575f80fd5b6003805460018082019092557fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0180546001600160a01b03199081166001600160a01b038781169182179093555f8181526002602090815260409182902080549094169488169490941783559190930184905580519283529082018390527f7589b0732052d2ded19f37e278ed2ae0d7d2e93b21d3931b73c5200a1367165391016114d7565b6001600160a01b0381165f9081526002602081905260409091200154421015611757575f80fd5b5f5b60035481101561184d57816001600160a01b031660038281548110611780576117806122ff565b5f918252602090912001546001600160a01b03160361184557600380546117a990600190612287565b815481106117b9576117b96122ff565b5f91825260209091200154600380546001600160a01b0390921691839081106117e4576117e46122ff565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555060038054806118205761182061237b565b5f8281526020902081015f1990810180546001600160a01b031916905501905561184d565b600101611759565b506001600160a01b0381165f81815260026020819052604080832080546001600160a01b0319168155600181018490559182018390556003820183905560048201839055600582018390556006909101829055517f755c47ac85b75fe2251607db5a480aac818b88bb535814bf1e3c4784ae4f6baa9190a250565b60015460ff1615610a605760405163d93c066560e01b815260040160405180910390fd5b5f6040516323b872dd60e01b81526001600160a01b03851660048201526001600160a01b038416602482015282604482015260205f6064835f8a5af191505080601f3d1160015f5114161516156119455750833b153d17155b8061157e5760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610534565b6119dd6001600160a01b037f00000000000000000000000026bbc26415c6316890565f5f73017f85ee70b60c167f000000000000000000000000347106734e7b129dde92333a0007d64a4b08e26683612068565b60405163534a7e1d60e11b8152600481018290527f000000000000000000000000347106734e7b129dde92333a0007d64a4b08e2666001600160a01b03169063a694fc3a90602401611555565b5f805b600354811015611b0b575f60038281548110611a4b57611a4b6122ff565b5f9182526020822001546001600160a01b03169150611a6982611318565b6001600160a01b0383165f9081526002602052604090206005018190559050611a9182610a62565b6001600160a01b038084165f90815260026020526040902060040191909155841615611b0157611ac18483610608565b6001600160a01b038086165f8181526005602090815260408083209488168084529482528083209590955591815260048252838120928152919052208190555b5050600101611a2d565b50611b216001600160a01b0384163330856118ec565b6001600160a01b0383165f90815260026020526040902060060154611b4690836122d8565b6001600160a01b0384165f90815260026020526040902060010154909250611b6e908361238f565b6001600160a01b0384165f908152600260205260409020600601819055611b959083612287565b6001600160a01b0384165f90815260026020819052604090912001549092504210611bff576001600160a01b0383165f90815260026020526040902060010154611bdf90836122c5565b6001600160a01b0384165f90815260026020526040902060030155611d17565b6001600160a01b0383165f90815260026020819052604082200154611c25904290612287565b6001600160a01b0385165f9081526002602052604081206003015491925090611c4e908361229a565b6001600160a01b0386165f9081526002602052604081206006015491925090611c7783876122d8565b611c8191906122d8565b6001600160a01b0387165f90815260026020526040902060010154909150611ca9908261238f565b6001600160a01b0387165f908152600260205260409020600601819055611cd09082612287565b6001600160a01b0387165f90815260026020526040902060010154909150611cf890826122c5565b6001600160a01b0387165f908152600260205260409020600301555050505b6001600160a01b0383165f9081526002602052604090204260048201819055600190910154611d45916122d8565b6001600160a01b0384165f81815260026020819052604091829020019290925590517fac24935fd910bc682b5ccb1a07b718cadf8cf2f6d1404c4f3ddc3662dae40e2990611d969085815260200190565b60405180910390a2505050565b5f8111611df25760405162461bcd60e51b815260206004820181905260248201527f526577617264206475726174696f6e206d757374206265206e6f6e2d7a65726f6044820152606401610534565b6001600160a01b0382165f9081526002602081905260409091200154421015611eeb576001600160a01b0382165f90815260026020819052604082200154611e3b904290612287565b6001600160a01b0384165f9081526002602052604081206003015491925090611e64908361229a565b6001600160a01b0385165f9081526002602052604081206006015491925090611e8d90836122d8565b9050611e99848261238f565b6001600160a01b0386165f908152600260205260409020600601819055611ec09082612287565b9050611ecc84826122c5565b6001600160a01b0386165f908152600260205260409020600301555050505b6001600160a01b0382165f908152600260205260409020426004909101819055611f169082906122d8565b6001600160a01b0383165f818152600260208181526040928390209182019490945560010184905580519182529181018390527fad2f86b01ed93b4b3a150d448c61a4f5d8d38075d3c0c64cc0a26fd6e1f49545910160405180910390a15050565b604051630517811f60e11b81526001600160a01b037f00000000000000000000000026bbc26415c6316890565f5f73017f85ee70b60c811660048301527f000000000000000000000000b71b3daea39012fb0f2b14d2a9c86da9292fc1261690630a2f023e906024015f604051808303815f87803b158015611ff8575f80fd5b505af115801561160e573d5f803e3d5ffd5b6120126118c8565b6001805460ff1916811790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611649565b60015460ff16610a6057604051638dfc202b60e01b815260040160405180910390fd5b5f60405163095ea7b360e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f5114161516156120b25750823b153d17155b8061160e5760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401610534565b6001600160a01b0381168114610a54575f80fd5b5f805f60608486031215612116575f80fd5b8335612121816120f0565b92506020840135612131816120f0565b929592945050506040919091013590565b602080825282518282018190525f918401906040840190835b818110156121825783516001600160a01b031683526020938401939092019160010161215b565b509095945050505050565b5f806040838503121561219e575f80fd5b82356121a9816120f0565b915060208301356121b9816120f0565b809150509250929050565b5f602082840312156121d4575f80fd5b81356121df816120f0565b9392505050565b602080825282518282018190525f918401906040840190835b8181101561218257835180516001600160a01b0316845260209081015181850152909301926040909201916001016121ff565b5f60208284031215612242575f80fd5b5035919050565b5f806040838503121561225a575f80fd5b8235612265816120f0565b946020939093013593505050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561069a5761069a612273565b808202811582820484141761069a5761069a612273565b634e487b7160e01b5f52601260045260245ffd5b5f826122d3576122d36122b1565b500490565b8082018082111561069a5761069a612273565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f6001820161232457612324612273565b5060010190565b5f6020828403121561233b575f80fd5b81516121df816120f0565b5f82518060208501845e5f920191825250919050565b5f6020828403121561236c575f80fd5b815180151581146121df575f80fd5b634e487b7160e01b5f52603160045260245ffd5b5f8261239d5761239d6122b1565b50069056fea2646970667358221220b4f1d19b5b60178344ac1562c2e7e829f6f843ffe709d5b69073ab906982d04864736f6c634300081a0033

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.