ETH Price: $2,688.16 (-0.33%)
Gas: 0.62 Gwei

Contract

0x8787ca6A0aFffd8F634D09d7c2eE8D15b326e3f6
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Claim Tokens192904902024-02-23 13:08:59359 days ago1708693739IN
0x8787ca6A...5b326e3f6
0 ETH0.0285454344.41672551
Claim Token192347062024-02-15 17:05:35367 days ago1708016735IN
0x8787ca6A...5b326e3f6
0 ETH0.0366292337.8144095
Claim Tokens192278502024-02-14 18:01:35368 days ago1707933695IN
0x8787ca6A...5b326e3f6
0 ETH0.0391065733.46343365
Claim Tokens191573152024-02-04 20:27:35378 days ago1707078455IN
0x8787ca6A...5b326e3f6
0 ETH0.0142177414.60425747
Claim Tokens191373602024-02-02 1:09:23381 days ago1706836163IN
0x8787ca6A...5b326e3f6
0 ETH0.0124789716.99327678
Claim Tokens191364812024-02-01 22:12:35381 days ago1706825555IN
0x8787ca6A...5b326e3f6
0 ETH0.0287984222.92413503
Claim Tokens190969652024-01-27 9:16:35386 days ago1706346995IN
0x8787ca6A...5b326e3f6
0 ETH0.0008364412.52766273
Claim Tokens190969652024-01-27 9:16:35386 days ago1706346995IN
0x8787ca6A...5b326e3f6
0 ETH0.0111287312.52766273
Claim Tokens190956212024-01-27 4:45:23387 days ago1706330723IN
0x8787ca6A...5b326e3f6
0 ETH0.0081880711.79557509
Claim Tokens190688302024-01-23 10:39:59390 days ago1706006399IN
0x8787ca6A...5b326e3f6
0 ETH0.0122315716.62521595
Claim Tokens190385892024-01-19 4:42:23395 days ago1705639343IN
0x8787ca6A...5b326e3f6
0 ETH0.0126907622.57696695
Claim Tokens190330082024-01-18 9:58:47395 days ago1705571927IN
0x8787ca6A...5b326e3f6
0 ETH0.0255806129.31102969
Claim Tokens190128572024-01-15 14:24:23398 days ago1705328663IN
0x8787ca6A...5b326e3f6
0 ETH0.0153094326.20522081
Claim Tokens190121012024-01-15 11:51:35398 days ago1705319495IN
0x8787ca6A...5b326e3f6
0 ETH0.0151922121.00873678
Claim Tokens190003252024-01-13 20:22:35400 days ago1705177355IN
0x8787ca6A...5b326e3f6
0 ETH0.0094475514.31802751
Claim Tokens189955162024-01-13 4:13:59401 days ago1705119239IN
0x8787ca6A...5b326e3f6
0 ETH0.0097740813.99268851
Claim Tokens189946082024-01-13 1:11:23401 days ago1705108283IN
0x8787ca6A...5b326e3f6
0 ETH0.0020473917.29978222
Claim Tokens189946042024-01-13 1:10:35401 days ago1705108235IN
0x8787ca6A...5b326e3f6
0 ETH0.0108813315.89282189
Claim Tokens189898442024-01-12 9:12:23401 days ago1705050743IN
0x8787ca6A...5b326e3f6
0 ETH0.0108676419.33363569
Claim Tokens189887752024-01-12 5:37:23402 days ago1705037843IN
0x8787ca6A...5b326e3f6
0 ETH0.0166596614.55674929
Claim Tokens189795222024-01-10 22:27:35403 days ago1704925655IN
0x8787ca6A...5b326e3f6
0 ETH0.0387447658.25684311
Claim Tokens189732382024-01-10 1:21:11404 days ago1704849671IN
0x8787ca6A...5b326e3f6
0 ETH0.0090978615.30490323
Claim Tokens189725852024-01-09 23:09:23404 days ago1704841763IN
0x8787ca6A...5b326e3f6
0 ETH0.010165624.92950567
Claim Tokens189713082024-01-09 18:51:59404 days ago1704826319IN
0x8787ca6A...5b326e3f6
0 ETH0.0008604416.56995298
Claim Tokens189713082024-01-09 18:51:59404 days ago1704826319IN
0x8787ca6A...5b326e3f6
0 ETH0.0076431815.60052872
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FeeDistributor

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 19 : FeeDistributor.sol
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

import {Math} from "../lib/openzeppelin-contracts/contracts/utils/math/Math.sol";
import {SafeCast} from "../lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol";

import {ERC20} from "../lib/solmate/src/tokens/ERC20.sol";
import {SafeTransferLib} from "../lib/solmate/src/utils/SafeTransferLib.sol";
import {ReentrancyGuard} from "../lib/solmate/src/utils/ReentrancyGuard.sol";

import "./lib/OptionalOnlyCaller.sol";
import "./interfaces/IVotingEscrow.sol";
import "./interfaces/IFeeDistributor.sol";

// solhint-disable not-rely-on-time

/**
 * @title FeeDistributor
 * @author opnxj - Modified from ZeframLou/fee-distributor: https://github.com/ZeframLou/fee-distributor
 * @notice Distributes any tokens transferred to the contract (e.g. Protocol fees and any token emissions) among vetoken
 * holders proportionally based on a snapshot of the week at which the tokens are sent to the FeeDistributor contract.
 * @dev Supports distributing arbitrarily many different tokens. In order to start distributing a new token to vetoken
 * holders simply transfer the tokens to the `FeeDistributor` contract and then call `checkpointToken`.
 */
contract FeeDistributor is
    IFeeDistributor,
    OptionalOnlyCaller,
    ReentrancyGuard
{
    /// -----------------------------------------------------------------------
    /// Library usage
    /// -----------------------------------------------------------------------

    using SafeTransferLib for ERC20;

    /// -----------------------------------------------------------------------
    /// Errors
    /// -----------------------------------------------------------------------

    error FeeDistributor__InputLengthMismatch();
    error FeeDistributor__VotingEscrowZeroTotalSupply();
    error FeeDistributor__CannotStartBeforeCurrentWeek();

    /// -----------------------------------------------------------------------
    /// Immutable params
    /// -----------------------------------------------------------------------

    uint256 private immutable _startTime;
    IVotingEscrow private immutable _votingEscrow;

    /// -----------------------------------------------------------------------
    /// Storage variables
    /// -----------------------------------------------------------------------

    // Global State
    uint256 private _timeCursor;
    mapping(uint256 => uint256) private _veSupplyCache;

    // Token State

    // `startTime` and `timeCursor` are both timestamps so comfortably fit in a uint64.
    // `cachedBalance` will comfortably fit the total supply of any meaningful token.
    // Should more than 2^128 tokens be sent to this contract then checkpointing this token will fail until enough
    // tokens have been claimed to bring the total balance back below 2^128.
    struct TokenState {
        uint64 startTime;
        uint64 timeCursor;
        uint128 cachedBalance;
    }

    mapping(ERC20 => TokenState) private _tokenState;
    mapping(ERC20 => mapping(uint256 => uint256)) private _tokensPerWeek;

    // User State

    // `startTime` and `timeCursor` are timestamps so will comfortably fit in a uint64.
    // For `lastEpochCheckpointed` to overflow would need over 2^128 transactions to the VotingEscrow contract.
    struct UserState {
        uint64 startTime;
        uint64 timeCursor;
        uint128 lastEpochCheckpointed;
    }

    mapping(address => UserState) internal _userState;
    mapping(address => mapping(uint256 => uint256))
        private _userBalanceAtTimestamp;
    mapping(address => mapping(ERC20 => uint256)) private _userTokenTimeCursor;

    /// -----------------------------------------------------------------------
    /// Constructor
    /// -----------------------------------------------------------------------

    constructor(
        IVotingEscrow votingEscrow,
        uint256 startTime
    ) EIP712("FeeDistributor", "1") {
        _votingEscrow = votingEscrow;

        startTime = _roundDownTimestamp(startTime);
        uint256 currentWeek = _roundDownTimestamp(block.timestamp);
        if (startTime < currentWeek) {
            revert FeeDistributor__CannotStartBeforeCurrentWeek();
        }
        if (startTime == currentWeek) {
            // We assume that `votingEscrow` has been deployed in a week previous to this one.
            // If `votingEscrow` did not have a non-zero supply at the beginning of the current week
            // then any tokens which are distributed this week will be lost permanently.
            if (_veTotalSupply(currentWeek) == 0) {
                revert FeeDistributor__VotingEscrowZeroTotalSupply();
            }
        }
        _startTime = startTime;
        _timeCursor = startTime;
    }

    /// -----------------------------------------------------------------------
    /// View functions
    /// -----------------------------------------------------------------------

    /**
     * @notice Returns the VotingEscrow (vetoken) token contract
     */
    function getVotingEscrow() external view override returns (IVotingEscrow) {
        return _votingEscrow;
    }

    /**
     * @notice Returns the global time cursor representing the most earliest uncheckpointed week.
     */
    function getTimeCursor() external view override returns (uint256) {
        return _timeCursor;
    }

    /**
     * @notice Returns the user-level time cursor representing the most earliest uncheckpointed week.
     * @param user - The address of the user to query.
     */
    function getUserTimeCursor(
        address user
    ) external view override returns (uint256) {
        return _userState[user].timeCursor;
    }

    /**
     * @notice Returns the token-level time cursor storing the timestamp at up to which tokens have been distributed.
     * @param token - The ERC20 token address to query.
     */
    function getTokenTimeCursor(
        ERC20 token
    ) external view override returns (uint256) {
        return _tokenState[token].timeCursor;
    }

    /**
     * @notice Returns the user-level time cursor storing the timestamp of the latest token distribution claimed.
     * @param user - The address of the user to query.
     * @param token - The ERC20 token address to query.
     */
    function getUserTokenTimeCursor(
        address user,
        ERC20 token
    ) external view override returns (uint256) {
        return _getUserTokenTimeCursor(user, token);
    }

    /**
     * @notice Returns the user's cached balance of vetoken as of the provided timestamp.
     * @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
     * This function requires `user` to have been checkpointed past `timestamp` so that their balance is cached.
     * @param user - The address of the user of which to read the cached balance of.
     * @param timestamp - The timestamp at which to read the `user`'s cached balance at.
     */
    function getUserBalanceAtTimestamp(
        address user,
        uint256 timestamp
    ) external view override returns (uint256) {
        return _userBalanceAtTimestamp[user][timestamp];
    }

    /**
     * @notice Returns the cached total supply of vetoken as of the provided timestamp.
     * @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
     * This function requires the contract to have been checkpointed past `timestamp` so that the supply is cached.
     * @param timestamp - The timestamp at which to read the cached total supply at.
     */
    function getTotalSupplyAtTimestamp(
        uint256 timestamp
    ) external view override returns (uint256) {
        return _veSupplyCache[timestamp];
    }

    /**
     * @notice Returns the FeeDistributor's cached balance of `token`.
     */
    function getTokenLastBalance(
        ERC20 token
    ) external view override returns (uint256) {
        return _tokenState[token].cachedBalance;
    }

    /**
     * @notice Returns the amount of `token` which the FeeDistributor received in the week beginning at `timestamp`.
     * @param token - The ERC20 token address to query.
     * @param timestamp - The timestamp corresponding to the beginning of the week of interest.
     */
    function getTokensDistributedInWeek(
        ERC20 token,
        uint256 timestamp
    ) external view override returns (uint256) {
        return _tokensPerWeek[token][timestamp];
    }

    /// -----------------------------------------------------------------------
    /// User actions
    /// -----------------------------------------------------------------------

    // Depositing

    /**
     * @notice Deposits tokens to be distributed in the current week.
     * @dev Sending tokens directly to the FeeDistributor instead of using `depositToken` may result in tokens being
     * retroactively distributed to past weeks, or for the distribution to carry over to future weeks.
     *
     * If for some reason `depositToken` cannot be called, in order to ensure that all tokens are correctly distributed
     * manually call `checkpointToken` before and after the token transfer.
     * @param token - The ERC20 token address to distribute.
     * @param amount - The amount of tokens to deposit.
     */
    function depositToken(
        ERC20 token,
        uint256 amount
    ) external override nonReentrant {
        _checkpointToken(token, false);
        token.safeTransferFrom(msg.sender, address(this), amount);
        _checkpointToken(token, true);
    }

    /**
     * @notice Deposits tokens to be distributed in the current week.
     * @dev A version of `depositToken` which supports depositing multiple `tokens` at once.
     * See `depositToken` for more details.
     * @param tokens - An array of ERC20 token addresses to distribute.
     * @param amounts - An array of token amounts to deposit.
     */
    function depositTokens(
        ERC20[] calldata tokens,
        uint256[] calldata amounts
    ) external override nonReentrant {
        if (tokens.length != amounts.length) {
            revert FeeDistributor__InputLengthMismatch();
        }

        uint256 length = tokens.length;
        for (uint256 i = 0; i < length; ) {
            _checkpointToken(tokens[i], false);
            tokens[i].safeTransferFrom(msg.sender, address(this), amounts[i]);
            _checkpointToken(tokens[i], true);

            unchecked {
                ++i;
            }
        }
    }

    // Checkpointing

    /**
     * @notice Caches the total supply of vetoken at the beginning of each week.
     * This function will be called automatically before claiming tokens to ensure the contract is properly updated.
     */
    function checkpoint() external override nonReentrant {
        _checkpointTotalSupply();
    }

    /**
     * @notice Caches the user's balance of vetoken at the beginning of each week.
     * This function will be called automatically before claiming tokens to ensure the contract is properly updated.
     * @param user - The address of the user to be checkpointed.
     */
    function checkpointUser(address user) external override nonReentrant {
        _checkpointUserBalance(user);
    }

    /**
     * @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
     * @dev Any `token` balance held by the FeeDistributor above that which is returned by `getTokenLastBalance`
     * will be distributed evenly across the time period since `token` was last checkpointed.
     *
     * This function will be called automatically before claiming tokens to ensure the contract is properly updated.
     * @param token - The ERC20 token address to be checkpointed.
     */
    function checkpointToken(ERC20 token) external override nonReentrant {
        _checkpointToken(token, true);
    }

    /**
     * @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
     * @dev A version of `checkpointToken` which supports checkpointing multiple tokens.
     * See `checkpointToken` for more details.
     * @param tokens - An array of ERC20 token addresses to be checkpointed.
     */
    function checkpointTokens(
        ERC20[] calldata tokens
    ) external override nonReentrant {
        uint256 tokensLength = tokens.length;
        for (uint256 i = 0; i < tokensLength; ) {
            _checkpointToken(tokens[i], true);

            unchecked {
                ++i;
            }
        }
    }

    // Claiming

    /**
     * @notice Claims all pending distributions of the provided token for a user.
     * @dev It's not necessary to explicitly checkpoint before calling this function, it will ensure the FeeDistributor
     * is up to date before calculating the amount of tokens to be claimed.
     * @param user - The user on behalf of which to claim.
     * @param token - The ERC20 token address to be claimed.
     * @return The amount of `token` sent to `user` as a result of claiming.
     */
    function claimToken(
        address user,
        ERC20 token
    )
        external
        override
        nonReentrant
        optionalOnlyCaller(user)
        returns (uint256)
    {
        _checkpointTotalSupply();
        _checkpointUserBalance(user);
        _checkpointToken(token, false);

        uint256 amount = _claimToken(user, token);
        return amount;
    }

    /**
     * @notice Claims a number of tokens on behalf of a user.
     * @dev A version of `claimToken` which supports claiming multiple `tokens` on behalf of `user`.
     * See `claimToken` for more details.
     * @param user - The user on behalf of which to claim.
     * @param tokens - An array of ERC20 token addresses to be claimed.
     * @return An array of the amounts of each token in `tokens` sent to `user` as a result of claiming.
     */
    function claimTokens(
        address user,
        ERC20[] calldata tokens
    )
        external
        override
        nonReentrant
        optionalOnlyCaller(user)
        returns (uint256[] memory)
    {
        _checkpointTotalSupply();
        _checkpointUserBalance(user);

        uint256 tokensLength = tokens.length;
        uint256[] memory amounts = new uint256[](tokensLength);
        for (uint256 i = 0; i < tokensLength; ) {
            _checkpointToken(tokens[i], false);
            amounts[i] = _claimToken(user, tokens[i]);

            unchecked {
                ++i;
            }
        }

        return amounts;
    }

    /// -----------------------------------------------------------------------
    /// Internal functions
    /// -----------------------------------------------------------------------

    /**
     * @dev It is required that both the global, token and user state have been properly checkpointed
     * before calling this function.
     */
    function _claimToken(address user, ERC20 token) internal returns (uint256) {
        TokenState storage tokenState = _tokenState[token];
        uint256 nextUserTokenWeekToClaim = _getUserTokenTimeCursor(user, token);

        // The first week which cannot be correctly claimed is the earliest of:
        // - A) The global or user time cursor (whichever is earliest), rounded up to the end of the week.
        // - B) The token time cursor, rounded down to the beginning of the week.
        //
        // This prevents the two failure modes:
        // - A) A user may claim a week for which we have not processed their balance, resulting in tokens being locked.
        // - B) A user may claim a week which then receives more tokens to be distributed. However the user has
        //      already claimed for that week so their share of these new tokens are lost.
        uint256 firstUnclaimableWeek = Math.min(
            _roundUpTimestamp(
                Math.min(_timeCursor, _userState[user].timeCursor)
            ),
            _roundDownTimestamp(tokenState.timeCursor)
        );

        mapping(uint256 => uint256) storage tokensPerWeek = _tokensPerWeek[
            token
        ];
        mapping(uint256 => uint256)
            storage userBalanceAtTimestamp = _userBalanceAtTimestamp[user];

        uint256 amount;
        for (uint256 i = 0; i < 20; ) {
            // We clearly cannot claim for `firstUnclaimableWeek` and so we break here.
            if (nextUserTokenWeekToClaim >= firstUnclaimableWeek) break;

            unchecked {
                amount +=
                    (tokensPerWeek[nextUserTokenWeekToClaim] *
                        userBalanceAtTimestamp[nextUserTokenWeekToClaim]) /
                    _veSupplyCache[nextUserTokenWeekToClaim];
                nextUserTokenWeekToClaim += 1 weeks;
                ++i;
            }
        }
        // Update the stored user-token time cursor to prevent this user claiming this week again.
        _userTokenTimeCursor[user][token] = nextUserTokenWeekToClaim;

        if (amount > 0) {
            unchecked {
                // For a token to be claimable it must have been added to the cached balance so this is safe.
                tokenState.cachedBalance = uint128(
                    tokenState.cachedBalance - amount
                );
            }

            token.safeTransfer(user, amount);
            emit TokensClaimed(user, token, amount, nextUserTokenWeekToClaim);
        }

        return amount;
    }

    /**
     * @dev Calculate the amount of `token` to be distributed to `_votingEscrow` holders since the last checkpoint.
     */
    function _checkpointToken(ERC20 token, bool force) internal {
        TokenState storage tokenState = _tokenState[token];
        uint256 lastTokenTime = tokenState.timeCursor;
        uint256 timeSinceLastCheckpoint;
        if (lastTokenTime == 0) {
            // If it's the first time we're checkpointing this token then start distributing from now.
            // Also mark at which timestamp users should start attempts to claim this token from.
            lastTokenTime = block.timestamp;
            tokenState.startTime = uint64(_roundDownTimestamp(block.timestamp));

            // Prevent someone from assigning tokens to an inaccessible week.
            require(
                block.timestamp > _startTime,
                "Fee distribution has not started yet"
            );
        } else {
            unchecked {
                timeSinceLastCheckpoint = block.timestamp - lastTokenTime;

                if (!force) {
                    // Checkpointing N times within a single week is completely equivalent to checkpointing once at the end.
                    // We then want to get as close as possible to a single checkpoint every Wed 23:59 UTC to save gas.

                    // We then skip checkpointing if we're in the same week as the previous checkpoint.
                    bool alreadyCheckpointedThisWeek = _roundDownTimestamp(
                        block.timestamp
                    ) == _roundDownTimestamp(lastTokenTime);
                    // However we want to ensure that all of this week's fees are assigned to the current week without
                    // overspilling into the next week. To mitigate this, we checkpoint if we're near the end of the week.
                    bool nearingEndOfWeek = _roundUpTimestamp(block.timestamp) -
                        block.timestamp <
                        1 days;

                    // This ensures that we checkpoint once at the beginning of the week and again for each user interaction
                    // towards the end of the week to give an accurate final reading of the balance.
                    if (alreadyCheckpointedThisWeek && !nearingEndOfWeek) {
                        return;
                    }
                }
            }
        }

        tokenState.timeCursor = uint64(block.timestamp);

        uint256 tokenBalance = token.balanceOf(address(this));
        uint256 newTokensToDistribute = tokenBalance - tokenState.cachedBalance;
        if (newTokensToDistribute == 0) return;
        require(
            tokenBalance <= type(uint128).max,
            "Maximum token balance exceeded"
        );
        tokenState.cachedBalance = uint128(tokenBalance);

        uint256 firstIncompleteWeek = _roundDownTimestamp(lastTokenTime);
        uint256 nextWeek = 0;

        // Distribute `newTokensToDistribute` evenly across the time period from `lastTokenTime` to now.
        // These tokens are assigned to weeks proportionally to how much of this period falls into each week.
        mapping(uint256 => uint256) storage tokensPerWeek = _tokensPerWeek[
            token
        ];
        for (uint256 i = 0; i < 20; ) {
            unchecked {
                // This is safe as we're incrementing a timestamp.
                nextWeek = firstIncompleteWeek + 1 weeks;
                if (block.timestamp < nextWeek) {
                    // `firstIncompleteWeek` is now the beginning of the current week, i.e. this is the final iteration.
                    if (
                        timeSinceLastCheckpoint == 0 &&
                        block.timestamp == lastTokenTime
                    ) {
                        tokensPerWeek[
                            firstIncompleteWeek
                        ] += newTokensToDistribute;
                    } else {
                        // block.timestamp >= lastTokenTime by definition.
                        tokensPerWeek[firstIncompleteWeek] +=
                            (newTokensToDistribute *
                                (block.timestamp - lastTokenTime)) /
                            timeSinceLastCheckpoint;
                    }
                    // As we've caught up to the present then we should now break.
                    break;
                } else {
                    // We've gone a full week or more without checkpointing so need to distribute tokens to previous weeks.
                    if (
                        timeSinceLastCheckpoint == 0 &&
                        nextWeek == lastTokenTime
                    ) {
                        // It shouldn't be possible to enter this block
                        tokensPerWeek[
                            firstIncompleteWeek
                        ] += newTokensToDistribute;
                    } else {
                        // nextWeek > lastTokenTime by definition.
                        tokensPerWeek[firstIncompleteWeek] +=
                            (newTokensToDistribute *
                                (nextWeek - lastTokenTime)) /
                            timeSinceLastCheckpoint;
                    }
                }

                // We've now "checkpointed" up to the beginning of next week so must update timestamps appropriately.
                lastTokenTime = nextWeek;
                firstIncompleteWeek = nextWeek;

                ++i;
            }
        }

        emit TokenCheckpointed(token, newTokensToDistribute, lastTokenTime);
    }

    /**
     * @dev Cache the `user`'s balance of `_votingEscrow` at the beginning of each new week
     */
    function _checkpointUserBalance(address user) internal {
        uint256 maxUserEpoch = _votingEscrow.user_point_epoch(user);

        // If user has no epochs then they have never locked vetoken.
        // They clearly will not then receive fees.
        if (maxUserEpoch == 0) return;

        UserState storage userState = _userState[user];

        // `nextWeekToCheckpoint` represents the timestamp of the beginning of the first week
        // which we haven't checkpointed the user's VotingEscrow balance yet.
        uint256 nextWeekToCheckpoint = userState.timeCursor;

        uint256 userEpoch;
        if (nextWeekToCheckpoint == 0) {
            // First checkpoint for user so need to do the initial binary search
            userEpoch = _findTimestampUserEpoch(
                user,
                _startTime,
                0,
                maxUserEpoch
            );
        } else {
            if (nextWeekToCheckpoint >= block.timestamp) {
                // User has checkpointed the current week already so perform early return.
                // This prevents a user from processing epochs created later in this week, however this is not an issue
                // as if a significant number of these builds up then the user will skip past them with a binary search.
                return;
            }

            // Otherwise use the value saved from last time
            userEpoch = userState.lastEpochCheckpointed;

            unchecked {
                // This optimizes a scenario common for power users, which have frequent `VotingEscrow` interactions in
                // the same week. We assume that any such user is also claiming fees every week, and so we only perform
                // a binary search here rather than integrating it into the main search algorithm, effectively skipping
                // most of the week's irrelevant checkpoints.
                // The slight tradeoff is that users who have multiple infrequent `VotingEscrow` interactions and also don't
                // claim frequently will also perform the binary search, despite it not leading to gas savings.
                if (maxUserEpoch - userEpoch > 20) {
                    userEpoch = _findTimestampUserEpoch(
                        user,
                        nextWeekToCheckpoint,
                        userEpoch,
                        maxUserEpoch
                    );
                }
            }
        }

        // Epoch 0 is always empty so bump onto the next one so that we start on a valid epoch.
        if (userEpoch == 0) {
            userEpoch = 1;
        }

        IVotingEscrow.Point memory nextUserPoint = _votingEscrow
            .user_point_history(user, userEpoch);

        // If this is the first checkpoint for the user, calculate the first week they're eligible for.
        // i.e. the timestamp of the first Thursday after they locked.
        // If this is earlier then the first distribution then fast forward to then.
        if (nextWeekToCheckpoint == 0) {
            // Disallow checkpointing before `startTime`.
            require(
                block.timestamp > _startTime,
                "Fee distribution has not started yet"
            );
            nextWeekToCheckpoint = Math.max(
                _startTime,
                _roundUpTimestamp(nextUserPoint.ts)
            );
            userState.startTime = uint64(nextWeekToCheckpoint);
        }

        // It's safe to increment `userEpoch` and `nextWeekToCheckpoint` in this loop as epochs and timestamps
        // are always much smaller than 2^256 and are being incremented by small values.
        IVotingEscrow.Point memory currentUserPoint;
        for (uint256 i = 0; i < 50; ) {
            unchecked {
                if (
                    nextWeekToCheckpoint >= nextUserPoint.ts &&
                    userEpoch <= maxUserEpoch
                ) {
                    // The week being considered is contained in a user epoch after that described by `currentUserPoint`.
                    // We then shift `nextUserPoint` into `currentUserPoint` and query the Point for the next user epoch.
                    // We do this in order to step though epochs until we find the first epoch starting after
                    // `nextWeekToCheckpoint`, making the previous epoch the one that contains `nextWeekToCheckpoint`.
                    userEpoch += 1;
                    currentUserPoint = nextUserPoint;
                    if (userEpoch > maxUserEpoch) {
                        nextUserPoint = IVotingEscrow.Point(0, 0, 0, 0);
                    } else {
                        nextUserPoint = _votingEscrow.user_point_history(
                            user,
                            userEpoch
                        );
                    }
                } else {
                    // The week being considered lies inside the user epoch described by `oldUserPoint`
                    // we can then use it to calculate the user's balance at the beginning of the week.
                    if (nextWeekToCheckpoint >= block.timestamp) {
                        // Break if we're trying to cache the user's balance at a timestamp in the future.
                        // We only perform this check here to ensure that we can still process checkpoints created
                        // in the current week.
                        break;
                    }

                    int128 dt = SafeCast.toInt128(
                        SafeCast.toInt256(
                            nextWeekToCheckpoint - currentUserPoint.ts
                        )
                    );
                    uint256 userBalance = currentUserPoint.bias >
                        currentUserPoint.slope * dt
                        ? uint256(
                            SafeCast.toUint256(
                                currentUserPoint.bias -
                                    currentUserPoint.slope *
                                    dt
                            )
                        )
                        : 0;

                    // User's lock has expired and they haven't relocked yet.
                    if (userBalance == 0 && userEpoch > maxUserEpoch) {
                        nextWeekToCheckpoint = _roundUpTimestamp(
                            block.timestamp
                        );
                        break;
                    }

                    // User had a nonzero lock and so is eligible to collect fees.
                    _userBalanceAtTimestamp[user][
                        nextWeekToCheckpoint
                    ] = userBalance;

                    nextWeekToCheckpoint += 1 weeks;
                }

                ++i;
            }
        }

        // We subtract off 1 from the userEpoch to step back once so that on the next attempt to checkpoint
        // the current `currentUserPoint` will be loaded as `nextUserPoint`. This ensures that we can't skip over the
        // user epoch containing `nextWeekToCheckpoint`.
        unchecked {
            // userEpoch > 0 so this is safe.
            userState.lastEpochCheckpointed = uint64(userEpoch - 1);
        }
        userState.timeCursor = uint64(nextWeekToCheckpoint);
    }

    /**
     * @notice Calculate total voting power at some point in the past
     * @param point The point (bias/slope) to start search from
     * @param t Time to calculate the total voting power at
     * @return Total voting power at that time
     */
    function _supplyAt(
        IVotingEscrow.Point memory point,
        uint256 t
    ) internal view returns (uint256) {
        IVotingEscrow.Point memory lastPoint = point;
        uint tI = _roundDownTimestamp(lastPoint.ts);
        for (uint i = 0; i < 255; ++i) {
            tI += 1 weeks;
            int128 dSlope = 0;
            if (tI > t) {
                tI = t;
            } else {
                dSlope = IVotingEscrow(_votingEscrow).slope_changes(tI);
            }
            lastPoint.bias -=
                lastPoint.slope *
                int128(int(tI) - int(lastPoint.ts));
            if (tI == t) {
                break;
            }
            lastPoint.slope += dSlope;
            lastPoint.ts = tI;
        }

        if (lastPoint.bias < 0) {
            lastPoint.bias = 0;
        }
        return uint(uint128(lastPoint.bias));
    }

    /**
     * @dev Provides a better implementation of VE.totalSupplyAtT
     */
    function _veTotalSupply(uint256 timestamp) internal view returns (uint256) {
        uint256 _epoch = IVotingEscrow(_votingEscrow).epoch();
        IVotingEscrow.Point memory lastPoint = IVotingEscrow(_votingEscrow)
            .point_history(_epoch);
        return _supplyAt(lastPoint, timestamp);
    }

    /**
     * @dev Cache the totalSupply of VotingEscrow token at the beginning of each new week
     */
    function _checkpointTotalSupply() internal {
        uint256 nextWeekToCheckpoint = _timeCursor;
        uint256 weekStart = _roundDownTimestamp(block.timestamp);

        // We expect `timeCursor == weekStart + 1 weeks` when fully up to date.
        if (nextWeekToCheckpoint > weekStart || weekStart == block.timestamp) {
            // We've already checkpointed up to this week so perform early return
            return;
        }

        _votingEscrow.checkpoint();

        // Step through the each week and cache the total supply at beginning of week on this contract
        for (uint256 i = 0; i < 20; ) {
            unchecked {
                if (nextWeekToCheckpoint > weekStart) break;

                _veSupplyCache[nextWeekToCheckpoint] = _veTotalSupply(
                    nextWeekToCheckpoint
                );

                // This is safe as we're incrementing a timestamp
                nextWeekToCheckpoint += 1 weeks;

                ++i;
            }
        }
        // Update state to the end of the current week (`weekStart` + 1 weeks)
        _timeCursor = nextWeekToCheckpoint;
    }

    // Helper functions

    /**
     * @dev Wrapper around `_userTokenTimeCursor` which returns the start timestamp for `token`
     * if `user` has not attempted to interact with it previously.
     */
    function _getUserTokenTimeCursor(
        address user,
        ERC20 token
    ) internal view returns (uint256) {
        uint256 userTimeCursor = _userTokenTimeCursor[user][token];
        if (userTimeCursor > 0) return userTimeCursor;
        // This is the first time that the user has interacted with this token.
        // We then start from the latest out of either when `user` first locked vetoken or `token` was first checkpointed.
        return
            Math.max(_userState[user].startTime, _tokenState[token].startTime);
    }

    /**
     * @dev Return the user epoch number for `user` corresponding to the provided `timestamp`
     */
    function _findTimestampUserEpoch(
        address user,
        uint256 timestamp,
        uint256 minUserEpoch,
        uint256 maxUserEpoch
    ) internal view returns (uint256) {
        uint256 min = minUserEpoch;
        uint256 max = maxUserEpoch;

        // Perform binary search through epochs to find epoch containing `timestamp`
        for (uint256 i = 0; i < 128; ) {
            unchecked {
                if (min >= max) break;

                // Algorithm assumes that inputs are less than 2^128 so this operation is safe.
                // +2 avoids getting stuck in min == mid < max
                uint256 mid = (min + max + 2) / 2;
                IVotingEscrow.Point memory pt = _votingEscrow
                    .user_point_history(user, mid);
                if (pt.ts <= timestamp) {
                    min = mid;
                } else {
                    // max > min so this is safe.
                    max = mid - 1;
                }
            }
        }
        return min;
    }

    /**
     * @dev Rounds the provided timestamp down to the beginning of the previous week (Thurs 00:00 UTC)
     */
    function _roundDownTimestamp(
        uint256 timestamp
    ) private pure returns (uint256) {
        unchecked {
            // Division by zero or overflows are impossible here.
            return (timestamp / 1 weeks) * 1 weeks;
        }
    }

    /**
     * @dev Rounds the provided timestamp up to the beginning of the next week (Thurs 00:00 UTC)
     */
    function _roundUpTimestamp(
        uint256 timestamp
    ) private pure returns (uint256) {
        unchecked {
            // Overflows are impossible here for all realistic inputs.
            return _roundDownTimestamp(timestamp + 1 weeks - 1);
        }
    }
}

File 2 of 19 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 3 of 19 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v2.5._
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v2.5._
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v2.5._
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v2.5._
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 4 of 19 : 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 5 of 19 : 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.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
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), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // 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.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // 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.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        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), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // 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.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // 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.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        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), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // 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.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // 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.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

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

/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 private locked = 1;

    modifier nonReentrant() virtual {
        require(locked == 1, "REENTRANCY");

        locked = 2;

        _;

        locked = 1;
    }
}

File 7 of 19 : OptionalOnlyCaller.sol
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

import "./EOASignaturesValidator.sol";
import "../interfaces/IOptionalOnlyCaller.sol";

abstract contract OptionalOnlyCaller is
    IOptionalOnlyCaller,
    EOASignaturesValidator
{
    error OptionalOnlyCaller__SenderNotAllowed();

    mapping(address => bool) private _isOnlyCallerEnabled;

    uint256 private constant INVALID_SIGNATURE_ERROR = 1;
    bytes32 private constant _SET_ONLY_CALLER_CHECK_TYPEHASH =
        keccak256(
            "SetOnlyCallerCheck(address user,bool enabled,uint256 nonce)"
        );

    /**
     * @dev Reverts if the verification mechanism is enabled and the given address is not the caller.
     * @param user - Address to validate as the only allowed caller, if the verification is enabled.
     */
    modifier optionalOnlyCaller(address user) {
        _verifyCaller(user);
        _;
    }

    function setOnlyCallerCheck(bool enabled) external override {
        _setOnlyCallerCheck(msg.sender, enabled);
    }

    function setOnlyCallerCheckWithSignature(
        address user,
        bool enabled,
        bytes memory signature
    ) external override {
        bytes32 structHash = keccak256(
            abi.encode(
                _SET_ONLY_CALLER_CHECK_TYPEHASH,
                user,
                enabled,
                getNextNonce(user)
            )
        );
        _ensureValidSignature(
            user,
            structHash,
            signature,
            INVALID_SIGNATURE_ERROR
        );
        _setOnlyCallerCheck(user, enabled);
    }

    function _setOnlyCallerCheck(address user, bool enabled) private {
        _isOnlyCallerEnabled[user] = enabled;
        emit OnlyCallerOptIn(user, enabled);
    }

    function isOnlyCallerEnabled(
        address user
    ) external view override returns (bool) {
        return _isOnlyCallerEnabled[user];
    }

    function _verifyCaller(address user) private view {
        if (_isOnlyCallerEnabled[user]) {
            if (msg.sender != user) {
                revert OptionalOnlyCaller__SenderNotAllowed();
            }
        }
    }
}

File 8 of 19 : IVotingEscrow.sol
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity >=0.6.11;

pragma experimental ABIEncoderV2;

// For compatibility, we're keeping the same function names as in the original Curve code, including the mixed-case
// naming convention.
// solhint-disable func-name-mixedcase

interface IVotingEscrow {
    struct Point {
        int128 bias;
        int128 slope; // - dweight / dt
        uint256 ts;
        uint256 blk; // block
    }

    function epoch() external view returns (uint256);

    function user_point_epoch(address user) external view returns (uint256);

    function slope_changes(uint256 i) external view returns (int128);

    function point_history(
        uint256 timestamp
    ) external view returns (Point memory);

    function user_point_history(
        address user,
        uint256 timestamp
    ) external view returns (Point memory);

    function checkpoint() external;
}

File 9 of 19 : IFeeDistributor.sol
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

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

import "./IVotingEscrow.sol";

/**
 * @title Fee Distributor
 * @notice Distributes any tokens transferred to the contract (e.g. Protocol fees and any BAL emissions) among veBAL
 * holders proportionally based on a snapshot of the week at which the tokens are sent to the FeeDistributor contract.
 * @dev Supports distributing arbitrarily many different tokens. In order to start distributing a new token to veBAL
 * holders simply transfer the tokens to the `FeeDistributor` contract and then call `checkpointToken`.
 */
interface IFeeDistributor {
    event TokenCheckpointed(
        ERC20 token,
        uint256 amount,
        uint256 lastCheckpointTimestamp
    );
    event TokensClaimed(
        address user,
        ERC20 token,
        uint256 amount,
        uint256 userTokenTimeCursor
    );

    /**
     * @notice Returns the VotingEscrow (veBAL) token contract
     */
    function getVotingEscrow() external view returns (IVotingEscrow);

    /**
     * @notice Returns the global time cursor representing the most earliest uncheckpointed week.
     */
    function getTimeCursor() external view returns (uint256);

    /**
     * @notice Returns the user-level time cursor representing the most earliest uncheckpointed week.
     * @param user - The address of the user to query.
     */
    function getUserTimeCursor(address user) external view returns (uint256);

    /**
     * @notice Returns the token-level time cursor storing the timestamp at up to which tokens have been distributed.
     * @param token - The ERC20 token address to query.
     */
    function getTokenTimeCursor(ERC20 token) external view returns (uint256);

    /**
     * @notice Returns the user-level time cursor storing the timestamp of the latest token distribution claimed.
     * @param user - The address of the user to query.
     * @param token - The ERC20 token address to query.
     */
    function getUserTokenTimeCursor(
        address user,
        ERC20 token
    ) external view returns (uint256);

    /**
     * @notice Returns the user's cached balance of veBAL as of the provided timestamp.
     * @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
     * This function requires `user` to have been checkpointed past `timestamp` so that their balance is cached.
     * @param user - The address of the user of which to read the cached balance of.
     * @param timestamp - The timestamp at which to read the `user`'s cached balance at.
     */
    function getUserBalanceAtTimestamp(
        address user,
        uint256 timestamp
    ) external view returns (uint256);

    /**
     * @notice Returns the cached total supply of veBAL as of the provided timestamp.
     * @dev Only timestamps which fall on Thursdays 00:00:00 UTC will return correct values.
     * This function requires the contract to have been checkpointed past `timestamp` so that the supply is cached.
     * @param timestamp - The timestamp at which to read the cached total supply at.
     */
    function getTotalSupplyAtTimestamp(
        uint256 timestamp
    ) external view returns (uint256);

    /**
     * @notice Returns the FeeDistributor's cached balance of `token`.
     */
    function getTokenLastBalance(ERC20 token) external view returns (uint256);

    /**
     * @notice Returns the amount of `token` which the FeeDistributor received in the week beginning at `timestamp`.
     * @param token - The ERC20 token address to query.
     * @param timestamp - The timestamp corresponding to the beginning of the week of interest.
     */
    function getTokensDistributedInWeek(
        ERC20 token,
        uint256 timestamp
    ) external view returns (uint256);

    // Depositing

    /**
     * @notice Deposits tokens to be distributed in the current week.
     * @dev Sending tokens directly to the FeeDistributor instead of using `depositTokens` may result in tokens being
     * retroactively distributed to past weeks, or for the distribution to carry over to future weeks.
     *
     * If for some reason `depositTokens` cannot be called, in order to ensure that all tokens are correctly distributed
     * manually call `checkpointToken` before and after the token transfer.
     * @param token - The ERC20 token address to distribute.
     * @param amount - The amount of tokens to deposit.
     */
    function depositToken(ERC20 token, uint256 amount) external;

    /**
     * @notice Deposits tokens to be distributed in the current week.
     * @dev A version of `depositToken` which supports depositing multiple `tokens` at once.
     * See `depositToken` for more details.
     * @param tokens - An array of ERC20 token addresses to distribute.
     * @param amounts - An array of token amounts to deposit.
     */
    function depositTokens(
        ERC20[] calldata tokens,
        uint256[] calldata amounts
    ) external;

    // Checkpointing

    /**
     * @notice Caches the total supply of veBAL at the beginning of each week.
     * This function will be called automatically before claiming tokens to ensure the contract is properly updated.
     */
    function checkpoint() external;

    /**
     * @notice Caches the user's balance of veBAL at the beginning of each week.
     * This function will be called automatically before claiming tokens to ensure the contract is properly updated.
     * @param user - The address of the user to be checkpointed.
     */
    function checkpointUser(address user) external;

    /**
     * @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
     * @dev Any `token` balance held by the FeeDistributor above that which is returned by `getTokenLastBalance`
     * will be distributed evenly across the time period since `token` was last checkpointed.
     *
     * This function will be called automatically before claiming tokens to ensure the contract is properly updated.
     * @param token - The ERC20 token address to be checkpointed.
     */
    function checkpointToken(ERC20 token) external;

    /**
     * @notice Assigns any newly-received tokens held by the FeeDistributor to weekly distributions.
     * @dev A version of `checkpointToken` which supports checkpointing multiple tokens.
     * See `checkpointToken` for more details.
     * @param tokens - An array of ERC20 token addresses to be checkpointed.
     */
    function checkpointTokens(ERC20[] calldata tokens) external;

    // Claiming

    /**
     * @notice Claims all pending distributions of the provided token for a user.
     * @dev It's not necessary to explicitly checkpoint before calling this function, it will ensure the FeeDistributor
     * is up to date before calculating the amount of tokens to be claimed.
     * @param user - The user on behalf of which to claim.
     * @param token - The ERC20 token address to be claimed.
     * @return The amount of `token` sent to `user` as a result of claiming.
     */
    function claimToken(address user, ERC20 token) external returns (uint256);

    /**
     * @notice Claims a number of tokens on behalf of a user.
     * @dev A version of `claimToken` which supports claiming multiple `tokens` on behalf of `user`.
     * See `claimToken` for more details.
     * @param user - The user on behalf of which to claim.
     * @param tokens - An array of ERC20 token addresses to be claimed.
     * @return An array of the amounts of each token in `tokens` sent to `user` as a result of claiming.
     */
    function claimTokens(
        address user,
        ERC20[] calldata tokens
    ) external returns (uint256[] memory);
}

File 10 of 19 : EOASignaturesValidator.sol
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

import "../../lib/openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol";

import "../interfaces/ISignaturesValidator.sol";

/**
 * @dev Utility for signing Solidity function calls.
 */
abstract contract EOASignaturesValidator is ISignaturesValidator, EIP712 {
    error EOASignaturesValidator__ExpiredSignature();
    error EOASignaturesValidator__MalformedSignature();
    error EOASignaturesValidator__RevertWithErrorCode(uint256 errorCode);

    // Replay attack prevention for each account.
    mapping(address => uint256) internal _nextNonce;

    function getDomainSeparator() public view override returns (bytes32) {
        return _domainSeparatorV4();
    }

    function getNextNonce(
        address account
    ) public view override returns (uint256) {
        return _nextNonce[account];
    }

    function _ensureValidSignature(
        address account,
        bytes32 structHash,
        bytes memory signature,
        uint256 errorCode
    ) internal {
        return
            _ensureValidSignature(
                account,
                structHash,
                signature,
                type(uint256).max,
                errorCode
            );
    }

    function _ensureValidSignature(
        address account,
        bytes32 structHash,
        bytes memory signature,
        uint256 deadline,
        uint256 errorCode
    ) internal {
        bytes32 digest = _hashTypedDataV4(structHash);
        if (!_isValidSignature(account, digest, signature)) {
            revert EOASignaturesValidator__RevertWithErrorCode(errorCode);
        }

        // We could check for the deadline before validating the signature, but this leads to saner error processing (as
        // we only care about expired deadlines if the signature is correct) and only affects the gas cost of the revert
        // scenario, which will only occur infrequently, if ever.
        // The deadline is timestamp-based: it should not be relied upon for sub-minute accuracy.
        // solhint-disable-next-line not-rely-on-time
        if (deadline < block.timestamp) {
            revert EOASignaturesValidator__ExpiredSignature();
        }

        // We only advance the nonce after validating the signature. This is irrelevant for this module, but it can be
        // important in derived contracts that override _isValidSignature (e.g. SignaturesValidator), as we want for
        // the observable state to still have the current nonce as the next valid one.
        _nextNonce[account] += 1;
    }

    function _isValidSignature(
        address account,
        bytes32 digest,
        bytes memory signature
    ) internal view virtual returns (bool) {
        if (signature.length != 65) {
            revert EOASignaturesValidator__MalformedSignature();
        }

        bytes32 r;
        bytes32 s;
        uint8 v;

        // ecrecover takes the r, s and v signature parameters, and the only way to get them is to use assembly.
        // solhint-disable-next-line no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }

        address recoveredAddress = ecrecover(digest, v, r, s);

        // ecrecover returns the zero address on recover failure, so we need to handle that explicitly.
        return (recoveredAddress != address(0) && recoveredAddress == account);
    }
}

File 11 of 19 : IOptionalOnlyCaller.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity >=0.6.11;

/**
 * @dev Interface for the OptionalOnlyCaller helper, used to opt in to a caller
 * verification for a given address to methods that are otherwise callable by any address.
 */
interface IOptionalOnlyCaller {
    /**
     * @dev Emitted every time setOnlyCallerCheck is called.
     */
    event OnlyCallerOptIn(address user, bool enabled);

    /**
     * @dev Enables / disables verification mechanism for caller.
     * @param enabled - True if caller verification shall be enabled, false otherwise.
     */
    function setOnlyCallerCheck(bool enabled) external;

    function setOnlyCallerCheckWithSignature(
        address user,
        bool enabled,
        bytes memory signature
    ) external;

    /**
     * @dev Returns true if caller verification is enabled for the given user, false otherwise.
     */
    function isOnlyCallerEnabled(address user) external view returns (bool);
}

File 12 of 19 : EIP712.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.8;

import "./ECDSA.sol";
import "../ShortStrings.sol";
import "../../interfaces/IERC5267.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * _Available since v3.4._
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
 */
abstract contract EIP712 is IERC5267 {
    using ShortStrings for *;

    bytes32 private constant _TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;
    address private immutable _cachedThis;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    ShortString private immutable _name;
    ShortString private immutable _version;
    string private _nameFallback;
    string private _versionFallback;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _name = name.toShortStringWithFallback(_nameFallback);
        _version = version.toShortStringWithFallback(_versionFallback);
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
        _cachedThis = address(this);
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {EIP-5267}.
     *
     * _Available since v4.9._
     */
    function eip712Domain()
        public
        view
        virtual
        override
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        return (
            hex"0f", // 01111
            _name.toStringWithFallback(_nameFallback),
            _version.toStringWithFallback(_versionFallback),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }
}

File 13 of 19 : ISignaturesValidator.sol
// SPDX-License-Identifier: GPL-3.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity >=0.6.11;

/**
 * @dev Interface for the SignatureValidator helper, used to support meta-transactions.
 */
interface ISignaturesValidator {
    /**
     * @dev Returns the EIP712 domain separator.
     */
    function getDomainSeparator() external view returns (bytes32);

    /**
     * @dev Returns the next nonce used by an address to sign messages.
     */
    function getNextNonce(address user) external view returns (uint256);
}

File 14 of 19 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

File 15 of 19 : ShortStrings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)

pragma solidity ^0.8.8;

import "./StorageSlot.sol";

// | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
// | length  | 0x                                                              BB |
type ShortString is bytes32;

/**
 * @dev This library provides functions to convert short memory strings
 * into a `ShortString` type that can be used as an immutable variable.
 *
 * Strings of arbitrary length can be optimized using this library if
 * they are short enough (up to 31 bytes) by packing them with their
 * length (1 byte) in a single EVM word (32 bytes). Additionally, a
 * fallback mechanism can be used for every other case.
 *
 * Usage example:
 *
 * ```solidity
 * contract Named {
 *     using ShortStrings for *;
 *
 *     ShortString private immutable _name;
 *     string private _nameFallback;
 *
 *     constructor(string memory contractName) {
 *         _name = contractName.toShortStringWithFallback(_nameFallback);
 *     }
 *
 *     function name() external view returns (string memory) {
 *         return _name.toStringWithFallback(_nameFallback);
 *     }
 * }
 * ```
 */
library ShortStrings {
    // Used as an identifier for strings longer than 31 bytes.
    bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;

    error StringTooLong(string str);
    error InvalidShortString();

    /**
     * @dev Encode a string of at most 31 chars into a `ShortString`.
     *
     * This will trigger a `StringTooLong` error is the input string is too long.
     */
    function toShortString(string memory str) internal pure returns (ShortString) {
        bytes memory bstr = bytes(str);
        if (bstr.length > 31) {
            revert StringTooLong(str);
        }
        return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
    }

    /**
     * @dev Decode a `ShortString` back to a "normal" string.
     */
    function toString(ShortString sstr) internal pure returns (string memory) {
        uint256 len = byteLength(sstr);
        // using `new string(len)` would work locally but is not memory safe.
        string memory str = new string(32);
        /// @solidity memory-safe-assembly
        assembly {
            mstore(str, len)
            mstore(add(str, 0x20), sstr)
        }
        return str;
    }

    /**
     * @dev Return the length of a `ShortString`.
     */
    function byteLength(ShortString sstr) internal pure returns (uint256) {
        uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
        if (result > 31) {
            revert InvalidShortString();
        }
        return result;
    }

    /**
     * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
     */
    function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
        if (bytes(value).length < 32) {
            return toShortString(value);
        } else {
            StorageSlot.getStringSlot(store).value = value;
            return ShortString.wrap(_FALLBACK_SENTINEL);
        }
    }

    /**
     * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     */
    function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return toString(value);
        } else {
            return store;
        }
    }

    /**
     * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     *
     * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
     * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
     */
    function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return byteLength(value);
        } else {
            return bytes(store).length;
        }
    }
}

File 16 of 19 : IERC5267.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.0;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}

File 17 of 19 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 18 of 19 : StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
 * _Available since v4.9 for `string`, `bytes`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

File 19 of 19 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "solidity-examples/=lib/solidity-examples/contracts/",
    "solmate/=lib/solmate/src/",
    "superallowlist/=lib/superallowlist/",
    "v2-core/=lib/v2-core/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IVotingEscrow","name":"votingEscrow","type":"address"},{"internalType":"uint256","name":"startTime","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EOASignaturesValidator__ExpiredSignature","type":"error"},{"inputs":[],"name":"EOASignaturesValidator__MalformedSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"EOASignaturesValidator__RevertWithErrorCode","type":"error"},{"inputs":[],"name":"FeeDistributor__CannotStartBeforeCurrentWeek","type":"error"},{"inputs":[],"name":"FeeDistributor__InputLengthMismatch","type":"error"},{"inputs":[],"name":"FeeDistributor__VotingEscrowZeroTotalSupply","type":"error"},{"inputs":[],"name":"InvalidShortString","type":"error"},{"inputs":[],"name":"OptionalOnlyCaller__SenderNotAllowed","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"OnlyCallerOptIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lastCheckpointTimestamp","type":"uint256"}],"name":"TokenCheckpointed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"contract ERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"userTokenTimeCursor","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"checkpointToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20[]","name":"tokens","type":"address[]"}],"name":"checkpointTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"checkpointUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"claimToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract ERC20[]","name":"tokens","type":"address[]"}],"name":"claimTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"depositTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDomainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getNextNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"getTokenLastBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"getTokenTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getTokensDistributedInWeek","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getTotalSupplyAtTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getUserBalanceAtTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"contract ERC20","name":"token","type":"address"}],"name":"getUserTokenTimeCursor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVotingEscrow","outputs":[{"internalType":"contract IVotingEscrow","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isOnlyCallerEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setOnlyCallerCheck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"setOnlyCallerCheckWithSignature","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101a060405260016004553480156200001757600080fd5b5060405162002f3c38038062002f3c8339810160408190526200003a9162000490565b604080518082018252600e81526d2332b2a234b9ba3934b13aba37b960911b602080830191909152825180840190935260018352603160f81b908301529062000085826000620001b2565b6101205262000096816001620001b2565b61014052815160208084019190912060e052815190820120610100524660a0526200012460e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60805250503060c0526001600160a01b0382166101805262093a80908190048102904281900402808210156200016d576040516344473c4960e01b815260040160405180910390fd5b808203620001a1576200018081620001eb565b600003620001a157604051636e5832e160e01b815260040160405180910390fd5b506101608190526005555062000877565b6000602083511015620001d257620001ca83620002e6565b9050620001e5565b81620001df848262000571565b5060ff90505b92915050565b600080610180516001600160a01b031663900cf0cf6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000230573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200025691906200063d565b90506000610180516001600160a01b031663d1febfb9836040518263ffffffff1660e01b81526004016200028c91815260200190565b608060405180830381865afa158015620002aa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002d091906200066f565b9050620002de818562000332565b949350505050565b600080829050601f815111156200031d578260405163305a27a960e01b8152600401620003149190620006e8565b60405180910390fd5b80516200032a8262000738565b179392505050565b6040820151600090839062093a809081900402825b60ff81101562000467576200036062093a808362000773565b91506000858311156200037657859250620003ea565b61018051604051631c465d2160e21b8152600481018590526001600160a01b0390911690637119748490602401602060405180830381865afa158015620003c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003e7919062000789565b90505b6040840151620003fb9084620007ae565b84602001516200040c9190620007d8565b845185906200041d908390620007fb565b600f0b90525085830362000432575062000467565b80846020018181516200044691906200082b565b600f0b90525050604083018290526200045f816200085b565b905062000347565b5060008260000151600f0b12156200047e57600082525b50516001600160801b03169392505050565b60008060408385031215620004a457600080fd5b82516001600160a01b0381168114620004bc57600080fd5b6020939093015192949293505050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620004f757607f821691505b6020821081036200051857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200056c57600081815260208120601f850160051c81016020861015620005475750805b601f850160051c820191505b81811015620005685782815560010162000553565b5050505b505050565b81516001600160401b038111156200058d576200058d620004cc565b620005a5816200059e8454620004e2565b846200051e565b602080601f831160018114620005dd5760008415620005c45750858301515b600019600386901b1c1916600185901b17855562000568565b600085815260208120601f198616915b828110156200060e57888601518255948401946001909101908401620005ed565b50858210156200062d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000602082840312156200065057600080fd5b5051919050565b8051600f81900b81146200066a57600080fd5b919050565b6000608082840312156200068257600080fd5b604051608081016001600160401b0381118282101715620006a757620006a7620004cc565b604052620006b58362000657565b8152620006c56020840162000657565b602082015260408301516040820152606083015160608201528091505092915050565b600060208083528351808285015260005b818110156200071757858101830151858201604001528201620006f9565b506000604082860101526040601f19601f8301168501019250505092915050565b80516020808301519190811015620005185760001960209190910360031b1b16919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115620001e557620001e56200075d565b6000602082840312156200079c57600080fd5b620007a78262000657565b9392505050565b8181036000831280158383131683831282161715620007d157620007d16200075d565b5092915050565b600082600f0b82600f0b0280600f0b9150808214620007d157620007d16200075d565b600f82810b9082900b0360016001607f1b0319811260016001607f1b0382131715620001e557620001e56200075d565b600f81810b9083900b0160016001607f1b03811360016001607f1b031982121715620001e557620001e56200075d565b6000600182016200087057620008706200075d565b5060010190565b60805160a05160c05160e0516101005161012051610140516101605161018051612607620009356000396000818161015f015281816109b101528181610b0501528181610cbe015281816113d30152818161184401528181611a7601528181611b1a0152611d24015260008181610a6701528181610b7e01528181610bc00152610e8b01526000610673015260006106490152600061179b01526000611773015260006116ce015260006116f80152600061172201526126076000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c806388720467116100c3578063ca31879d1161007c578063ca31879d1461038b578063d3dc4ca11461039e578063de681faf146103d4578063e811f44b1461040a578063ed24911d1461041d578063fcaa54ee1461042557600080fd5b806388720467146102b257806390193b7c146102d2578063905d10ac146102fb578063a1648aa51461030e578063acbc14281461034a578063c2c4c5c11461038357600080fd5b80634f3c5090116101155780634f3c5090146102105780637b8d6221146102305780638050a7ee1461024357806382aa5ad41461025657806384b0196e1461025e578063876e69a11461027957600080fd5b806308b0308a1461015257806314866e081461018e5780632308805b146101a3578063338b5dea146101ea5780633902b9bc146101fd575b600080fd5b6040516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526020015b60405180910390f35b6101a161019c366004611f3d565b610438565b005b6101dc6101b1366004611f3d565b6001600160a01b0316600090815260076020526040902054600160801b90046001600160801b031690565b604051908152602001610185565b6101a16101f8366004611f61565b610479565b6101a161020b366004611f3d565b6104d4565b6101dc61021e366004611f8d565b60009081526006602052604090205490565b6101a161023e366004611ff1565b610506565b6101dc61025136600461205c565b610626565b6005546101dc565b61026661063b565b6040516101859796959493929190612116565b6101dc610287366004611f3d565b6001600160a01b0316600090815260096020526040902054600160401b90046001600160401b031690565b6102c56102c0366004612186565b6106c3565b60405161018591906121da565b6101dc6102e0366004611f3d565b6001600160a01b031660009081526002602052604090205490565b6101a16103093660046121ed565b6107d3565b61033a61031c366004611f3d565b6001600160a01b031660009081526003602052604090205460ff1690565b6040519015158152602001610185565b6101dc610358366004611f3d565b6001600160a01b0316600090815260076020526040902054600160401b90046001600160401b031690565b6101a161082e565b6101dc61039936600461205c565b610864565b6101dc6103ac366004611f61565b6001600160a01b03919091166000908152600860209081526040808320938352929052205490565b6101dc6103e2366004611f61565b6001600160a01b03919091166000908152600a60209081526040808320938352929052205490565b6101a161041836600461223e565b6108cd565b6101dc6108da565b6101a161043336600461229f565b6108e9565b6004546001146104635760405162461bcd60e51b815260040161045a90612356565b60405180910390fd5b60026004556104718161098f565b506001600455565b60045460011461049b5760405162461bcd60e51b815260040161045a90612356565b60026004556104ab826000610e2d565b6104c06001600160a01b0383163330846111b3565b6104cb826001610e2d565b50506001600455565b6004546001146104f65760405162461bcd60e51b815260040161045a90612356565b6002600455610471816001610e2d565b6004546001146105285760405162461bcd60e51b815260040161045a90612356565b600260045582811461054d5760405163b0208d4960e01b815260040160405180910390fd5b8260005b818110156106195761058a86868381811061056e5761056e61237a565b90506020020160208101906105839190611f3d565b6000610e2d565b6105e033308686858181106105a1576105a161237a565b905060200201358989868181106105ba576105ba61237a565b90506020020160208101906105cf9190611f3d565b6001600160a01b03169291906111b3565b6106118686838181106105f5576105f561237a565b905060200201602081019061060a9190611f3d565b6001610e2d565b600101610551565b5050600160045550505050565b6000610632838361123d565b90505b92915050565b60006060808280808361066e7f0000000000000000000000000000000000000000000000000000000000000000836112ba565b6106997f000000000000000000000000000000000000000000000000000000000000000060016112ba565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004546001146106e75760405162461bcd60e51b815260040161045a90612356565b6002600455836106f681611365565b6106fe6113af565b6107078561098f565b826000816001600160401b0381111561072257610722612259565b60405190808252806020026020018201604052801561074b578160200160208202803683370190505b50905060005b828110156107c35761076e87878381811061056e5761056e61237a565b61079e888888848181106107845761078461237a565b90506020020160208101906107999190611f3d565b611486565b8282815181106107b0576107b061237a565b6020908102919091010152600101610751565b5060016004559695505050505050565b6004546001146107f55760405162461bcd60e51b815260040161045a90612356565b60026004558060005b818110156108235761081b8484838181106105f5576105f561237a565b6001016107fe565b505060016004555050565b6004546001146108505760405162461bcd60e51b815260040161045a90612356565b600260045561085d6113af565b6001600455565b60006004546001146108885760405162461bcd60e51b815260040161045a90612356565b60026004558261089781611365565b61089f6113af565b6108a88461098f565b6108b3836000610e2d565b60006108bf8585611486565b600160045595945050505050565b6108d7338261165e565b50565b60006108e46116c1565b905090565b60007fbd291ffccec065968fe20c5f8debdad73ab50837733f357eeae8814178015a90848461092d876001600160a01b031660009081526002602052604090205490565b6040805160208101959095526001600160a01b039093169284019290925215156060830152608082015260a00160405160208183030381529060405280519060200120905061097f84828460016117ec565b610989848461165e565b50505050565b60405163010ae75760e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063010ae75790602401602060405180830381865afa1580156109fa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1e9190612390565b905080600003610a2c575050565b6001600160a01b038216600090815260096020526040812080549091600160401b9091046001600160401b031690818103610a9557610a8e857f00000000000000000000000000000000000000000000000000000000000000006000876117fb565b9050610ad0565b428210610aa3575050505050565b508154600160801b90046001600160801b031660148185031115610ad057610acd858383876117fb565b90505b80600003610adc575060015b6040516328d09d4760e01b81526001600160a01b038681166004830152602482018390526000917f0000000000000000000000000000000000000000000000000000000000000000909116906328d09d4790604401608060405180830381865afa158015610b4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7291906123bb565b905082600003610c0e577f00000000000000000000000000000000000000000000000000000000000000004211610bbb5760405162461bcd60e51b815260040161045a9061242c565b610bf17f0000000000000000000000000000000000000000000000000000000000000000610bec83604001516118de565b6118f3565b845467ffffffffffffffff19166001600160401b03821617855592505b6040805160808101825260008082526020820181905291810182905260608101829052905b6032811015610de75782604001518510158015610c505750868411155b15610d305760018401935082915086841115610c985760405180608001604052806000600f0b81526020016000600f0b81526020016000815260200160008152509250610ddf565b6040516328d09d4760e01b81526001600160a01b038981166004830152602482018690527f000000000000000000000000000000000000000000000000000000000000000016906328d09d4790604401608060405180830381865afa158015610d05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2991906123bb565b9250610ddf565b42851015610de7576000610d51610d4c84604001518803611909565b611977565b9050600081846020015102600f0b8460000151600f0b13610d73576000610d8b565b610d8b82856020015102856000015103600f0b6119e0565b905080158015610d9a57508886115b15610db157610da8426118de565b96505050610de7565b6001600160a01b038a166000908152600a602090815260408083208a84529091529020555062093a80909401935b600101610c33565b505083546001600160401b03938416600160401b0267ffffffffffffffff60401b19600160801b6000199095018616949094029390931693169290921717909155505050565b6001600160a01b038216600090815260076020526040812080549091600160401b9091046001600160401b031690818103610ecd5742915062093a8080830402835467ffffffffffffffff19166001600160401b03919091161783557f00000000000000000000000000000000000000000000000000000000000000004211610ec85760405162461bcd60e51b815260040161045a9061242c565b610f1d565b814203905083610f1d57600062093a808084040262093a80428190040214905060006201518042610efd426118de565b03109050818015610f0c575080155b15610f1a5750505050505050565b50505b825467ffffffffffffffff60401b1916600160401b426001600160401b0316021783556040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015610f87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fab9190612390565b8454909150600090610fcd90600160801b90046001600160801b031683612486565b905080600003610fe05750505050505050565b6001600160801b038211156110375760405162461bcd60e51b815260206004820152601e60248201527f4d6178696d756d20746f6b656e2062616c616e63652065786365656465640000604482015260640161045a565b84546001600160801b03808416600160801b02911617855560006110618562093a80908190040290565b6001600160a01b038916600090815260086020526040812091925090815b601481101561115c578362093a80019250824210156110f457861580156110a557508742145b156110c357600084815260208390526040902080548601905561115c565b868842038602816110d6576110d6612499565b6000868152602085905260409020805492909104909101905561115c565b8615801561110157508783145b1561111f57600084815260208390526040902080548601905561114c565b8688840386028161113257611132612499565b600086815260208590526040902080549290910490910190555b919650869250829160010161107f565b50604080516001600160a01b038c168152602081018690529081018890527f9b7f1a85a4c9b4e59e1b6527d9969c50cdfb3a1a467d0c4a51fb0ed8bf07f1309060600160405180910390a150505050505050505050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806112365760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b604482015260640161045a565b5050505050565b6001600160a01b038083166000908152600b602090815260408083209385168352929052908120548015611272579050610635565b6001600160a01b03808516600090815260096020908152604080832054938716835260079091529020546112b2916001600160401b0390811691166118f3565b949350505050565b606060ff83146112d4576112cd83611a32565b9050610635565b8180546112e0906124af565b80601f016020809104026020016040519081016040528092919081815260200182805461130c906124af565b80156113595780601f1061132e57610100808354040283529160200191611359565b820191906000526020600020905b81548152906001019060200180831161133c57829003601f168201915b50505050509050610635565b6001600160a01b03811660009081526003602052604090205460ff16156108d757336001600160a01b038216146108d75760405163f93014bb60e01b815260040160405180910390fd5b60055462093a804281900402808211806113c857504281145b156113d1575050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561142c57600080fd5b505af1158015611440573d6000803e3d6000fd5b5050505060005b601481101561147f5781831161147f5761146083611a71565b60008481526006602052604090205562093a8090920191600101611447565b5050600555565b6001600160a01b0381166000908152600760205260408120816114a9858561123d565b6005546001600160a01b03871660009081526009602052604081205492935091611517916114f0916114eb9190600160401b90046001600160401b0316611b91565b6118de565b845461151290600160401b90046001600160401b031662093a80908190040290565b611b91565b6001600160a01b038087166000908152600860209081526040808320938b168352600a9091528120929350909190805b601481101561159d578486101561159d576000868152600660209081526040808320548683528184205492889052922054028161158657611586612499565b62093a809790970196049190910190600101611547565b506001600160a01b03808a166000908152600b60209081526040808320938c1683529290522085905580156116525785546001600160801b03600160801b808304821684900382160291161786556115ff6001600160a01b0389168a83611ba0565b604080516001600160a01b03808c1682528a166020820152908101829052606081018690527fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de69060800160405180910390a15b98975050505050505050565b6001600160a01b038216600081815260036020908152604091829020805460ff19168515159081179091558251938452908301527fac9874a7a931a3f5c9f202c6d9cf40de5d21506993c9f9c38ca8265add89584c910160405180910390a15050565b6000306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801561171a57507f000000000000000000000000000000000000000000000000000000000000000046145b1561174457507f000000000000000000000000000000000000000000000000000000000000000090565b6108e4604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b61098984848460001985611c18565b60008282825b60808110156118d257818310156118d2576040516328d09d4760e01b81526001600160a01b038981166004830152600284860181010460248301819052916000917f000000000000000000000000000000000000000000000000000000000000000016906328d09d4790604401608060405180830381865afa15801561188b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118af91906123bb565b9050888160400151116118c4578194506118cb565b6001820393505b5050611801565b50909695505050505050565b600062093a8062093a7f830181900402610635565b60008183116119025781610632565b5090919050565b60006001600160ff1b038211156119735760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b606482015260840161045a565b5090565b80600f81900b81146119db5760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b606482015260840161045a565b919050565b6000808212156119735760405162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f736974697665604482015260640161045a565b60606000611a3f83611ca7565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663900cf0cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af69190612390565b60405163d1febfb960e01b8152600481018290529091506000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d1febfb990602401608060405180830381865afa158015611b61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8591906123bb565b90506112b28185611ccf565b60008183106119025781610632565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806109895760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015260640161045a565b6000611c2385611e32565b9050611c30868286611e5f565b611c505760405163e9a06b8b60e01b81526004810183905260240161045a565b42831015611c7157604051634917986360e11b815260040160405180910390fd5b6001600160a01b0386166000908152600260205260408120805460019290611c9a9084906124e9565b9091555050505050505050565b600060ff8216601f81111561063557604051632cd44ac360e21b815260040160405180910390fd5b6040820151600090839062093a809081900402825b60ff811015611e0a57611cfa62093a80836124e9565b9150600085831115611d0e57859250611d9a565b604051631c465d2160e21b8152600481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690637119748490602401602060405180830381865afa158015611d73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9791906124fc565b90505b6040840151611da99084612517565b8460200151611db8919061253e565b84518590611dc790839061255e565b600f0b905250858303611dda5750611e0a565b8084602001818151611dec919061258b565b600f0b9052505060408301829052611e03816125b8565b9050611ce4565b5060008260000151600f0b1215611e2057600082525b50516001600160801b03169392505050565b6000610635611e3f6116c1565b8360405161190160f01b8152600281019290925260228201526042902090565b60008151604114611e8357604051634c57c87b60e11b815260040160405180910390fd5b602082810151604080850151606080870151835160008082529681018086528a905290861a938101849052908101849052608081018290529293909260019060a0016020604051602081039080840390855afa158015611ee7573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906116525750876001600160a01b0316816001600160a01b03161498975050505050505050565b6001600160a01b03811681146108d757600080fd5b600060208284031215611f4f57600080fd5b8135611f5a81611f28565b9392505050565b60008060408385031215611f7457600080fd5b8235611f7f81611f28565b946020939093013593505050565b600060208284031215611f9f57600080fd5b5035919050565b60008083601f840112611fb857600080fd5b5081356001600160401b03811115611fcf57600080fd5b6020830191508360208260051b8501011115611fea57600080fd5b9250929050565b6000806000806040858703121561200757600080fd5b84356001600160401b038082111561201e57600080fd5b61202a88838901611fa6565b9096509450602087013591508082111561204357600080fd5b5061205087828801611fa6565b95989497509550505050565b6000806040838503121561206f57600080fd5b823561207a81611f28565b9150602083013561208a81611f28565b809150509250929050565b6000815180845260005b818110156120bb5760208185018101518683018201520161209f565b506000602082860101526020601f19601f83011685010191505092915050565b600081518084526020808501945080840160005b8381101561210b578151875295820195908201906001016120ef565b509495945050505050565b60ff60f81b8816815260e06020820152600061213560e0830189612095565b82810360408401526121478189612095565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152905061217881856120db565b9a9950505050505050505050565b60008060006040848603121561219b57600080fd5b83356121a681611f28565b925060208401356001600160401b038111156121c157600080fd5b6121cd86828701611fa6565b9497909650939450505050565b60208152600061063260208301846120db565b6000806020838503121561220057600080fd5b82356001600160401b0381111561221657600080fd5b61222285828601611fa6565b90969095509350505050565b803580151581146119db57600080fd5b60006020828403121561225057600080fd5b6106328261222e565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561229757612297612259565b604052919050565b6000806000606084860312156122b457600080fd5b83356122bf81611f28565b925060206122ce85820161222e565b925060408501356001600160401b03808211156122ea57600080fd5b818701915087601f8301126122fe57600080fd5b81358181111561231057612310612259565b612322601f8201601f1916850161226f565b9150808252888482850101111561233857600080fd5b80848401858401376000848284010152508093505050509250925092565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156123a257600080fd5b5051919050565b8051600f81900b81146119db57600080fd5b6000608082840312156123cd57600080fd5b604051608081018181106001600160401b03821117156123ef576123ef612259565b6040526123fb836123a9565b8152612409602084016123a9565b602082015260408301516040820152606083015160608201528091505092915050565b60208082526024908201527f46656520646973747269627574696f6e20686173206e6f742073746172746564604082015263081e595d60e21b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8181038181111561063557610635612470565b634e487b7160e01b600052601260045260246000fd5b600181811c908216806124c357607f821691505b6020821081036124e357634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561063557610635612470565b60006020828403121561250e57600080fd5b610632826123a9565b818103600083128015838313168383128216171561253757612537612470565b5092915050565b600082600f0b82600f0b0280600f0b915080821461253757612537612470565b600f82810b9082900b0360016001607f1b0319811260016001607f1b038213171561063557610635612470565b600f81810b9083900b0160016001607f1b03811360016001607f1b03198212171561063557610635612470565b6000600182016125ca576125ca612470565b506001019056fea26469706673582212209e53aef3029d82e8f564f18db73ffe53732896b692bbae51844017b611945d9864736f6c6343000813003300000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd87000000000000000000000000000000000000000000000000000000006486e6b7

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061014d5760003560e01c806388720467116100c3578063ca31879d1161007c578063ca31879d1461038b578063d3dc4ca11461039e578063de681faf146103d4578063e811f44b1461040a578063ed24911d1461041d578063fcaa54ee1461042557600080fd5b806388720467146102b257806390193b7c146102d2578063905d10ac146102fb578063a1648aa51461030e578063acbc14281461034a578063c2c4c5c11461038357600080fd5b80634f3c5090116101155780634f3c5090146102105780637b8d6221146102305780638050a7ee1461024357806382aa5ad41461025657806384b0196e1461025e578063876e69a11461027957600080fd5b806308b0308a1461015257806314866e081461018e5780632308805b146101a3578063338b5dea146101ea5780633902b9bc146101fd575b600080fd5b6040516001600160a01b037f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd871681526020015b60405180910390f35b6101a161019c366004611f3d565b610438565b005b6101dc6101b1366004611f3d565b6001600160a01b0316600090815260076020526040902054600160801b90046001600160801b031690565b604051908152602001610185565b6101a16101f8366004611f61565b610479565b6101a161020b366004611f3d565b6104d4565b6101dc61021e366004611f8d565b60009081526006602052604090205490565b6101a161023e366004611ff1565b610506565b6101dc61025136600461205c565b610626565b6005546101dc565b61026661063b565b6040516101859796959493929190612116565b6101dc610287366004611f3d565b6001600160a01b0316600090815260096020526040902054600160401b90046001600160401b031690565b6102c56102c0366004612186565b6106c3565b60405161018591906121da565b6101dc6102e0366004611f3d565b6001600160a01b031660009081526002602052604090205490565b6101a16103093660046121ed565b6107d3565b61033a61031c366004611f3d565b6001600160a01b031660009081526003602052604090205460ff1690565b6040519015158152602001610185565b6101dc610358366004611f3d565b6001600160a01b0316600090815260076020526040902054600160401b90046001600160401b031690565b6101a161082e565b6101dc61039936600461205c565b610864565b6101dc6103ac366004611f61565b6001600160a01b03919091166000908152600860209081526040808320938352929052205490565b6101dc6103e2366004611f61565b6001600160a01b03919091166000908152600a60209081526040808320938352929052205490565b6101a161041836600461223e565b6108cd565b6101dc6108da565b6101a161043336600461229f565b6108e9565b6004546001146104635760405162461bcd60e51b815260040161045a90612356565b60405180910390fd5b60026004556104718161098f565b506001600455565b60045460011461049b5760405162461bcd60e51b815260040161045a90612356565b60026004556104ab826000610e2d565b6104c06001600160a01b0383163330846111b3565b6104cb826001610e2d565b50506001600455565b6004546001146104f65760405162461bcd60e51b815260040161045a90612356565b6002600455610471816001610e2d565b6004546001146105285760405162461bcd60e51b815260040161045a90612356565b600260045582811461054d5760405163b0208d4960e01b815260040160405180910390fd5b8260005b818110156106195761058a86868381811061056e5761056e61237a565b90506020020160208101906105839190611f3d565b6000610e2d565b6105e033308686858181106105a1576105a161237a565b905060200201358989868181106105ba576105ba61237a565b90506020020160208101906105cf9190611f3d565b6001600160a01b03169291906111b3565b6106118686838181106105f5576105f561237a565b905060200201602081019061060a9190611f3d565b6001610e2d565b600101610551565b5050600160045550505050565b6000610632838361123d565b90505b92915050565b60006060808280808361066e7f4665654469737472696275746f7200000000000000000000000000000000000e836112ba565b6106997f310000000000000000000000000000000000000000000000000000000000000160016112ba565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b60606004546001146106e75760405162461bcd60e51b815260040161045a90612356565b6002600455836106f681611365565b6106fe6113af565b6107078561098f565b826000816001600160401b0381111561072257610722612259565b60405190808252806020026020018201604052801561074b578160200160208202803683370190505b50905060005b828110156107c35761076e87878381811061056e5761056e61237a565b61079e888888848181106107845761078461237a565b90506020020160208101906107999190611f3d565b611486565b8282815181106107b0576107b061237a565b6020908102919091010152600101610751565b5060016004559695505050505050565b6004546001146107f55760405162461bcd60e51b815260040161045a90612356565b60026004558060005b818110156108235761081b8484838181106105f5576105f561237a565b6001016107fe565b505060016004555050565b6004546001146108505760405162461bcd60e51b815260040161045a90612356565b600260045561085d6113af565b6001600455565b60006004546001146108885760405162461bcd60e51b815260040161045a90612356565b60026004558261089781611365565b61089f6113af565b6108a88461098f565b6108b3836000610e2d565b60006108bf8585611486565b600160045595945050505050565b6108d7338261165e565b50565b60006108e46116c1565b905090565b60007fbd291ffccec065968fe20c5f8debdad73ab50837733f357eeae8814178015a90848461092d876001600160a01b031660009081526002602052604090205490565b6040805160208101959095526001600160a01b039093169284019290925215156060830152608082015260a00160405160208183030381529060405280519060200120905061097f84828460016117ec565b610989848461165e565b50505050565b60405163010ae75760e01b81526001600160a01b0382811660048301526000917f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd879091169063010ae75790602401602060405180830381865afa1580156109fa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1e9190612390565b905080600003610a2c575050565b6001600160a01b038216600090815260096020526040812080549091600160401b9091046001600160401b031690818103610a9557610a8e857f0000000000000000000000000000000000000000000000000000000064811a006000876117fb565b9050610ad0565b428210610aa3575050505050565b508154600160801b90046001600160801b031660148185031115610ad057610acd858383876117fb565b90505b80600003610adc575060015b6040516328d09d4760e01b81526001600160a01b038681166004830152602482018390526000917f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd87909116906328d09d4790604401608060405180830381865afa158015610b4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7291906123bb565b905082600003610c0e577f0000000000000000000000000000000000000000000000000000000064811a004211610bbb5760405162461bcd60e51b815260040161045a9061242c565b610bf17f0000000000000000000000000000000000000000000000000000000064811a00610bec83604001516118de565b6118f3565b845467ffffffffffffffff19166001600160401b03821617855592505b6040805160808101825260008082526020820181905291810182905260608101829052905b6032811015610de75782604001518510158015610c505750868411155b15610d305760018401935082915086841115610c985760405180608001604052806000600f0b81526020016000600f0b81526020016000815260200160008152509250610ddf565b6040516328d09d4760e01b81526001600160a01b038981166004830152602482018690527f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd8716906328d09d4790604401608060405180830381865afa158015610d05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2991906123bb565b9250610ddf565b42851015610de7576000610d51610d4c84604001518803611909565b611977565b9050600081846020015102600f0b8460000151600f0b13610d73576000610d8b565b610d8b82856020015102856000015103600f0b6119e0565b905080158015610d9a57508886115b15610db157610da8426118de565b96505050610de7565b6001600160a01b038a166000908152600a602090815260408083208a84529091529020555062093a80909401935b600101610c33565b505083546001600160401b03938416600160401b0267ffffffffffffffff60401b19600160801b6000199095018616949094029390931693169290921717909155505050565b6001600160a01b038216600090815260076020526040812080549091600160401b9091046001600160401b031690818103610ecd5742915062093a8080830402835467ffffffffffffffff19166001600160401b03919091161783557f0000000000000000000000000000000000000000000000000000000064811a004211610ec85760405162461bcd60e51b815260040161045a9061242c565b610f1d565b814203905083610f1d57600062093a808084040262093a80428190040214905060006201518042610efd426118de565b03109050818015610f0c575080155b15610f1a5750505050505050565b50505b825467ffffffffffffffff60401b1916600160401b426001600160401b0316021783556040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015610f87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fab9190612390565b8454909150600090610fcd90600160801b90046001600160801b031683612486565b905080600003610fe05750505050505050565b6001600160801b038211156110375760405162461bcd60e51b815260206004820152601e60248201527f4d6178696d756d20746f6b656e2062616c616e63652065786365656465640000604482015260640161045a565b84546001600160801b03808416600160801b02911617855560006110618562093a80908190040290565b6001600160a01b038916600090815260086020526040812091925090815b601481101561115c578362093a80019250824210156110f457861580156110a557508742145b156110c357600084815260208390526040902080548601905561115c565b868842038602816110d6576110d6612499565b6000868152602085905260409020805492909104909101905561115c565b8615801561110157508783145b1561111f57600084815260208390526040902080548601905561114c565b8688840386028161113257611132612499565b600086815260208590526040902080549290910490910190555b919650869250829160010161107f565b50604080516001600160a01b038c168152602081018690529081018890527f9b7f1a85a4c9b4e59e1b6527d9969c50cdfb3a1a467d0c4a51fb0ed8bf07f1309060600160405180910390a150505050505050505050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806112365760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b604482015260640161045a565b5050505050565b6001600160a01b038083166000908152600b602090815260408083209385168352929052908120548015611272579050610635565b6001600160a01b03808516600090815260096020908152604080832054938716835260079091529020546112b2916001600160401b0390811691166118f3565b949350505050565b606060ff83146112d4576112cd83611a32565b9050610635565b8180546112e0906124af565b80601f016020809104026020016040519081016040528092919081815260200182805461130c906124af565b80156113595780601f1061132e57610100808354040283529160200191611359565b820191906000526020600020905b81548152906001019060200180831161133c57829003601f168201915b50505050509050610635565b6001600160a01b03811660009081526003602052604090205460ff16156108d757336001600160a01b038216146108d75760405163f93014bb60e01b815260040160405180910390fd5b60055462093a804281900402808211806113c857504281145b156113d1575050565b7f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd876001600160a01b031663c2c4c5c16040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561142c57600080fd5b505af1158015611440573d6000803e3d6000fd5b5050505060005b601481101561147f5781831161147f5761146083611a71565b60008481526006602052604090205562093a8090920191600101611447565b5050600555565b6001600160a01b0381166000908152600760205260408120816114a9858561123d565b6005546001600160a01b03871660009081526009602052604081205492935091611517916114f0916114eb9190600160401b90046001600160401b0316611b91565b6118de565b845461151290600160401b90046001600160401b031662093a80908190040290565b611b91565b6001600160a01b038087166000908152600860209081526040808320938b168352600a9091528120929350909190805b601481101561159d578486101561159d576000868152600660209081526040808320548683528184205492889052922054028161158657611586612499565b62093a809790970196049190910190600101611547565b506001600160a01b03808a166000908152600b60209081526040808320938c1683529290522085905580156116525785546001600160801b03600160801b808304821684900382160291161786556115ff6001600160a01b0389168a83611ba0565b604080516001600160a01b03808c1682528a166020820152908101829052606081018690527fff097c7d8b1957a4ff09ef1361b5fb54dcede3941ba836d0beb9d10bec725de69060800160405180910390a15b98975050505050505050565b6001600160a01b038216600081815260036020908152604091829020805460ff19168515159081179091558251938452908301527fac9874a7a931a3f5c9f202c6d9cf40de5d21506993c9f9c38ca8265add89584c910160405180910390a15050565b6000306001600160a01b037f0000000000000000000000008787ca6a0afffd8f634d09d7c2ee8d15b326e3f61614801561171a57507f000000000000000000000000000000000000000000000000000000000000000146145b1561174457507f6619b75de76f7331a7d6a541214e0324c0b12c551ab82224c197ae2c1d28b20c90565b6108e4604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0618c188edbe06a8ffa15e11b4f74493cfd6f23aba7fab610364d908072aac99918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b61098984848460001985611c18565b60008282825b60808110156118d257818310156118d2576040516328d09d4760e01b81526001600160a01b038981166004830152600284860181010460248301819052916000917f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd8716906328d09d4790604401608060405180830381865afa15801561188b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118af91906123bb565b9050888160400151116118c4578194506118cb565b6001820393505b5050611801565b50909695505050505050565b600062093a8062093a7f830181900402610635565b60008183116119025781610632565b5090919050565b60006001600160ff1b038211156119735760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b606482015260840161045a565b5090565b80600f81900b81146119db5760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b606482015260840161045a565b919050565b6000808212156119735760405162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f736974697665604482015260640161045a565b60606000611a3f83611ca7565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000807f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd876001600160a01b031663900cf0cf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af69190612390565b60405163d1febfb960e01b8152600481018290529091506000906001600160a01b037f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd87169063d1febfb990602401608060405180830381865afa158015611b61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8591906123bb565b90506112b28185611ccf565b60008183106119025781610632565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806109895760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015260640161045a565b6000611c2385611e32565b9050611c30868286611e5f565b611c505760405163e9a06b8b60e01b81526004810183905260240161045a565b42831015611c7157604051634917986360e11b815260040160405180910390fd5b6001600160a01b0386166000908152600260205260408120805460019290611c9a9084906124e9565b9091555050505050505050565b600060ff8216601f81111561063557604051632cd44ac360e21b815260040160405180910390fd5b6040820151600090839062093a809081900402825b60ff811015611e0a57611cfa62093a80836124e9565b9150600085831115611d0e57859250611d9a565b604051631c465d2160e21b8152600481018490527f00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd876001600160a01b031690637119748490602401602060405180830381865afa158015611d73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9791906124fc565b90505b6040840151611da99084612517565b8460200151611db8919061253e565b84518590611dc790839061255e565b600f0b905250858303611dda5750611e0a565b8084602001818151611dec919061258b565b600f0b9052505060408301829052611e03816125b8565b9050611ce4565b5060008260000151600f0b1215611e2057600082525b50516001600160801b03169392505050565b6000610635611e3f6116c1565b8360405161190160f01b8152600281019290925260228201526042902090565b60008151604114611e8357604051634c57c87b60e11b815260040160405180910390fd5b602082810151604080850151606080870151835160008082529681018086528a905290861a938101849052908101849052608081018290529293909260019060a0016020604051602081039080840390855afa158015611ee7573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906116525750876001600160a01b0316816001600160a01b03161498975050505050505050565b6001600160a01b03811681146108d757600080fd5b600060208284031215611f4f57600080fd5b8135611f5a81611f28565b9392505050565b60008060408385031215611f7457600080fd5b8235611f7f81611f28565b946020939093013593505050565b600060208284031215611f9f57600080fd5b5035919050565b60008083601f840112611fb857600080fd5b5081356001600160401b03811115611fcf57600080fd5b6020830191508360208260051b8501011115611fea57600080fd5b9250929050565b6000806000806040858703121561200757600080fd5b84356001600160401b038082111561201e57600080fd5b61202a88838901611fa6565b9096509450602087013591508082111561204357600080fd5b5061205087828801611fa6565b95989497509550505050565b6000806040838503121561206f57600080fd5b823561207a81611f28565b9150602083013561208a81611f28565b809150509250929050565b6000815180845260005b818110156120bb5760208185018101518683018201520161209f565b506000602082860101526020601f19601f83011685010191505092915050565b600081518084526020808501945080840160005b8381101561210b578151875295820195908201906001016120ef565b509495945050505050565b60ff60f81b8816815260e06020820152600061213560e0830189612095565b82810360408401526121478189612095565b606084018890526001600160a01b038716608085015260a0840186905283810360c0850152905061217881856120db565b9a9950505050505050505050565b60008060006040848603121561219b57600080fd5b83356121a681611f28565b925060208401356001600160401b038111156121c157600080fd5b6121cd86828701611fa6565b9497909650939450505050565b60208152600061063260208301846120db565b6000806020838503121561220057600080fd5b82356001600160401b0381111561221657600080fd5b61222285828601611fa6565b90969095509350505050565b803580151581146119db57600080fd5b60006020828403121561225057600080fd5b6106328261222e565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561229757612297612259565b604052919050565b6000806000606084860312156122b457600080fd5b83356122bf81611f28565b925060206122ce85820161222e565b925060408501356001600160401b03808211156122ea57600080fd5b818701915087601f8301126122fe57600080fd5b81358181111561231057612310612259565b612322601f8201601f1916850161226f565b9150808252888482850101111561233857600080fd5b80848401858401376000848284010152508093505050509250925092565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156123a257600080fd5b5051919050565b8051600f81900b81146119db57600080fd5b6000608082840312156123cd57600080fd5b604051608081018181106001600160401b03821117156123ef576123ef612259565b6040526123fb836123a9565b8152612409602084016123a9565b602082015260408301516040820152606083015160608201528091505092915050565b60208082526024908201527f46656520646973747269627574696f6e20686173206e6f742073746172746564604082015263081e595d60e21b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8181038181111561063557610635612470565b634e487b7160e01b600052601260045260246000fd5b600181811c908216806124c357607f821691505b6020821081036124e357634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561063557610635612470565b60006020828403121561250e57600080fd5b610632826123a9565b818103600083128015838313168383128216171561253757612537612470565b5092915050565b600082600f0b82600f0b0280600f0b915080821461253757612537612470565b600f82810b9082900b0360016001607f1b0319811260016001607f1b038213171561063557610635612470565b600f81810b9083900b0160016001607f1b03811360016001607f1b03198212171561063557610635612470565b6000600182016125ca576125ca612470565b506001019056fea26469706673582212209e53aef3029d82e8f564f18db73ffe53732896b692bbae51844017b611945d9864736f6c63430008130033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd87000000000000000000000000000000000000000000000000000000006486e6b7

-----Decoded View---------------
Arg [0] : votingEscrow (address): 0x28901CF869D94C9d892fBd86c8e57b801E8FDd87
Arg [1] : startTime (uint256): 1686562487

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000028901cf869d94c9d892fbd86c8e57b801e8fdd87
Arg [1] : 000000000000000000000000000000000000000000000000000000006486e6b7


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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