ETH Price: $3,299.69 (-1.64%)

Contract

0x6B83c4f3a6729Fb7D5e19b720092162DF439f567
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Delegate201207842024-06-18 19:39:35187 days ago1718739575IN
0x6B83c4f3...DF439f567
0 ETH0.001313656.44843154
Delegate195889232024-04-05 10:23:59261 days ago1712312639IN
0x6B83c4f3...DF439f567
0 ETH0.0046801615.9251437
Delegate195745382024-04-03 10:05:35263 days ago1712138735IN
0x6B83c4f3...DF439f567
0 ETH0.0047941920.1
Delegate191456662024-02-03 5:10:47324 days ago1706937047IN
0x6B83c4f3...DF439f567
0 ETH0.0040688818.90867947
Delegate191075502024-01-28 20:55:23329 days ago1706475323IN
0x6B83c4f3...DF439f567
0 ETH0.0010277210.47633361
Delegate191075492024-01-28 20:55:11329 days ago1706475311IN
0x6B83c4f3...DF439f567
0 ETH0.002205510.33692933
Delegate191075262024-01-28 20:50:35329 days ago1706475035IN
0x6B83c4f3...DF439f567
0 ETH0.0029305511.45003566
Delegate183486912023-10-14 12:42:47435 days ago1697287367IN
0x6B83c4f3...DF439f567
0 ETH0.001243565.60880478
Delegate181938662023-09-22 20:50:23457 days ago1695415823IN
0x6B83c4f3...DF439f567
0 ETH0.002025549.1357143
Delegate181856202023-09-21 17:04:59458 days ago1695315899IN
0x6B83c4f3...DF439f567
0 ETH0.0025145912.0683381
Delegate181856172023-09-21 17:04:23458 days ago1695315863IN
0x6B83c4f3...DF439f567
0 ETH0.0028021312.6383475
Delegate178785402023-08-09 16:28:47501 days ago1691598527IN
0x6B83c4f3...DF439f567
0 ETH0.0068408430.85395251

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
VeFxsVotingDelegation

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
default evmVersion
File 1 of 15 : VeFxsVotingDelegation.sol
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.19;

// ====================================================================
// |     ______                   _______                             |
// |    / _____________ __  __   / ____(_____  ____ _____  ________   |
// |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
// |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
// | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
// |                                                                  |
// ====================================================================
// ======================= VeFxsVotingDelegation ======================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance

// Primary Author(s)
// Jon Walch: https://github.com/jonwalch

// Contributors
// Dennis: https://github.com/denett
// Drake Evans: https://github.com/DrakeEvans
// Jamie Turley: https://github.com/jyturley

// Reviewers
// Sam Kazemian: https://github.com/samkazemian

// ====================================================================

import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { IERC5805 } from "@openzeppelin/contracts/interfaces/IERC5805.sol";
import { IVeFxs } from "./interfaces/IVeFxs.sol";
import { IVeFxsVotingDelegation } from "./interfaces/IVeFxsVotingDelegation.sol";

/// @title VeFxsVotingDelegation
/// @author Jon Walch (Frax Finance) https://github.com/jonwalch
/// @notice Contract that keeps track of voting weights and delegations, leveraging veFXS
contract VeFxsVotingDelegation is EIP712, IERC5805 {
    using SafeCast for uint256;

    /// @notice A week
    uint256 public constant WEEK = 7 days;

    /// @notice Max veFXS lock duration
    uint256 public constant MAX_LOCK_DURATION = 365 days * 4;

    /// @notice vote weight multiplier taken from veFXS
    uint256 public constant VOTE_WEIGHT_MULTIPLIER = 3;

    /// @notice Typehash needed for delegations by signature
    /// @dev keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)")
    bytes32 public constant DELEGATION_TYPEHASH = 0xe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf;

    /// @notice veFXS contract
    IVeFxs public immutable VE_FXS;

    /// @notice Nonces needed for delegations by signature
    mapping(address signer => uint256 nonce) public $nonces;

    /// @notice Mapping from delegator to delegate including additional parameters for our weight calculations.
    mapping(address delegator => IVeFxsVotingDelegation.Delegation delegate) public $delegations;

    /// @notice Mapping from delegate to their checkpoints. Checkpoints correspond to daily rounded dates of delegation.
    mapping(address delegate => IVeFxsVotingDelegation.DelegateCheckpoint[]) public $delegateCheckpoints;

    /// @notice Mapping from delegate to weekly rounded time of expiry to the aggregated values at time of expiration. Mirrors veFXS expiration.
    mapping(address delegate => mapping(uint256 week => IVeFxsVotingDelegation.Expiration)) public $expiredDelegations;

    /// @notice The ```constructor``` function is called on deployment
    /// @param veFxs Address of veFXS contract
    constructor(address veFxs, string memory name, string memory version) EIP712(name, version) {
        VE_FXS = IVeFxs(veFxs);
    }

    /// @notice The ```getCheckpoint``` function is called to retrieve a specific delegation checkpoint
    /// @dev Get the checkpoint for a delegate at a given index.
    /// @param delegateAddress Address of delegate
    /// @param index Integer index of the checkpoint
    /// @return delegateCheckpoint DelegateCheckpoint of ```delegate``` at ```index```
    function getCheckpoint(
        address delegateAddress,
        uint32 index
    ) external view returns (IVeFxsVotingDelegation.DelegateCheckpoint memory) {
        return $delegateCheckpoints[delegateAddress][index];
    }

    /// @notice The ```_calculateDelegatedWeight``` function calculates weight delegated to ```account``` accounting for any weight that expired since the nearest checkpoint
    /// @dev May include own weight if account previously delegated to someone else and then back to themselves
    /// @param voter Address of voter
    /// @param timestamp A block.timestamp, typically corresponding to a proposal snapshot
    /// @return delegatedWeight Voting weight corresponding to all ```delegateAccount```'s received delegations
    function _calculateDelegatedWeight(
        address voter,
        uint256 timestamp
    ) internal view returns (uint256 delegatedWeight) {
        // Check if delegate account has any delegations
        IVeFxsVotingDelegation.DelegateCheckpoint memory checkpoint = _checkpointBinarySearch({
            _$checkpoints: $delegateCheckpoints[voter],
            timestamp: timestamp
        });

        // If checkpoint is empty, short circuit and return 0 delegated weight
        if (checkpoint.timestamp == 0) {
            return 0;
        }

        // It's possible that some delegated veFXS has expired.
        // Add up all expirations during this time period, week by week.
        (uint256 totalExpiredBias, uint256 totalExpiredSlope, uint256 totalExpiredFxs) = _calculateExpirations({
            account: voter,
            start: checkpoint.timestamp,
            end: timestamp,
            checkpoint: checkpoint
        });

        uint256 expirationAdjustedBias = checkpoint.normalizedBias - totalExpiredBias;
        uint256 expirationAdjustedSlope = checkpoint.normalizedSlope - totalExpiredSlope;
        uint256 expirationAdjustedFxs = checkpoint.totalFxs - totalExpiredFxs;

        uint256 voteDecay = expirationAdjustedSlope * timestamp;
        uint256 biasAtTimestamp = (expirationAdjustedBias > voteDecay) ? expirationAdjustedBias - voteDecay : 0;

        // If all delegations are expired they have no voting weight. This differs from veFXS, which returns the locked FXS amount if it has not yet been withdrawn.
        delegatedWeight = expirationAdjustedFxs + (VOTE_WEIGHT_MULTIPLIER * biasAtTimestamp);
    }

    /// @notice The ```_calculateVotingWeight``` function calculates ```account```'s voting weight. Is 0 if they ever delegated and the delegation is in effect.
    /// @param voter Address of voter
    /// @param timestamp A block.timestamp, typically corresponding to a proposal snapshot
    /// @return votingWeight Voting weight corresponding to ```account```'s veFXS balance
    function _calculateVotingWeight(address voter, uint256 timestamp) internal view returns (uint256) {
        // If lock is expired they have no voting weight. This differs from veFXS, which returns the locked FXS amount if it has not yet been withdrawn.
        if (VE_FXS.locked(voter).end <= timestamp) return 0;

        uint256 firstDelegationTimestamp = $delegations[voter].firstDelegationTimestamp;
        // Never delegated OR this timestamp is before the first delegation by account
        if (firstDelegationTimestamp == 0 || timestamp < firstDelegationTimestamp) {
            try VE_FXS.balanceOf({ addr: voter, _t: timestamp }) returns (uint256 _balance) {
                return _balance;
            } catch {}
        }
        return 0;
    }

    /// @notice The ```calculateExpirations``` function calculates all expired delegations for an account since the last checkpoint.
    /// @dev Can be used in tandem with writeNewCheckpointForExpirations() to write a new checkpoint
    /// @dev Long time periods between checkpoints can increase gas costs for delegate() and castVote()
    /// @dev See _calculateExpirations
    /// @param delegateAddress Address of delegate
    /// @return calculatedCheckpoint A new DelegateCheckpoint to write based on expirations since previous checkpoint
    function calculateExpiredDelegations(
        address delegateAddress
    ) public view returns (IVeFxsVotingDelegation.DelegateCheckpoint memory calculatedCheckpoint) {
        IVeFxsVotingDelegation.DelegateCheckpoint[] storage $userDelegationCheckpoints = $delegateCheckpoints[
            delegateAddress
        ];

        // This ensures that checkpoints take effect at the next epoch
        uint256 checkpointTimestamp = ((block.timestamp / 1 days) * 1 days) + 1 days;
        uint256 checkpointsLength = $userDelegationCheckpoints.length;

        // Nothing to expire if no one delegated to you
        if (checkpointsLength == 0) return calculatedCheckpoint;

        IVeFxsVotingDelegation.DelegateCheckpoint memory lastCheckpoint = $userDelegationCheckpoints[
            checkpointsLength - 1
        ];

        // Nothing expired because the most recent checkpoint is already written
        if (lastCheckpoint.timestamp == checkpointTimestamp) {
            return calculatedCheckpoint;
        }

        (uint256 totalExpiredBias, uint256 totalExpiredSlope, uint256 totalExpiredFxs) = _calculateExpirations({
            account: delegateAddress,
            start: lastCheckpoint.timestamp,
            end: checkpointTimestamp,
            checkpoint: lastCheckpoint
        });

        // All will be 0 if no expirations, only need to check one of them
        if (totalExpiredFxs == 0) return calculatedCheckpoint;

        /// NOTE: Checkpoint values will always be larger than or equal to expired values
        unchecked {
            calculatedCheckpoint = IVeFxsVotingDelegation.DelegateCheckpoint({
                timestamp: uint128(checkpointTimestamp),
                normalizedBias: uint128(lastCheckpoint.normalizedBias - totalExpiredBias),
                normalizedSlope: uint128(lastCheckpoint.normalizedSlope - totalExpiredSlope),
                totalFxs: uint128(lastCheckpoint.totalFxs - totalExpiredFxs)
            });
        }
    }

    /// @notice The ```writeNewCheckpointForExpirations``` function writes a new checkpoint if any weight has expired since the previous checkpoint
    /// @dev Long time periods between checkpoints can increase gas costs for delegate() and castVote()
    /// @dev See _calculateExpirations
    /// @param delegateAddress Address of delegate
    function writeNewCheckpointForExpiredDelegations(address delegateAddress) external {
        IVeFxsVotingDelegation.DelegateCheckpoint memory newCheckpoint = calculateExpiredDelegations(delegateAddress);

        if (newCheckpoint.timestamp == 0) revert IVeFxsVotingDelegation.NoExpirations();

        $delegateCheckpoints[delegateAddress].push(newCheckpoint);
    }

    function CLOCK_MODE() public pure override returns (string memory) {
        return "mode=timestamp";
    }

    function clock() public view override returns (uint48) {
        return uint48(block.timestamp);
    }

    /// @notice The ```_getVotingWeight``` function calculates a voter's voting weight at ```timestamp```
    /// @param voter Address of voter
    /// @param timestamp A block.timestamp, typically corresponding to a proposal snapshot
    /// @return totalVotingWeight Voting weight of ```voter``` at ```timestamp```
    function _getVotingWeight(address voter, uint256 timestamp) internal view returns (uint256 totalVotingWeight) {
        totalVotingWeight =
            _calculateVotingWeight({ voter: voter, timestamp: timestamp }) +
            _calculateDelegatedWeight({ voter: voter, timestamp: timestamp });
    }

    /// @notice The ```getVotes``` function calculates a voter's weight at ```timestamp```
    /// @param voter Address of voter
    /// @param timepoint A block.timestamp, typically corresponding to a proposal snapshot
    /// @return votingWeight Voting weight of ```voterAddress``` at ```timepoint```
    function getVotes(address voter, uint256 timepoint) external view returns (uint256) {
        return _getVotingWeight({ voter: voter, timestamp: timepoint });
    }

    /// @notice The ```getVotes``` function calculates a voter's weight at the current block.timestamp
    /// @param voter Address of voter
    /// @return votingWeight Voting weight of ```voterAddress``` at ```block.timestamp```
    function getVotes(address voter) external view returns (uint256 votingWeight) {
        votingWeight = _getVotingWeight({ voter: voter, timestamp: block.timestamp });
    }

    /// @notice The ```getPastVotes``` function calculates a voter's weight at ```timepoint```
    /// @param voter Address of voter
    /// @param timepoint A block.timestamp, typically corresponding to a proposal snapshot, must be in past
    /// @return pastVotingWeight Voting weight of ```account``` at ```timepoint``` in past
    function getPastVotes(address voter, uint256 timepoint) external view returns (uint256 pastVotingWeight) {
        if (timepoint >= block.timestamp) revert IVeFxsVotingDelegation.TimestampInFuture();

        pastVotingWeight = _getVotingWeight({ voter: voter, timestamp: timepoint });
    }

    /// @notice The ```getPastTotalSupply``` function retrieves the total supply of veFXS at ```blockNumber```
    /// @dev Must use block.number instead of timestamp because VE_FXS.totalSupply(timestamp) doesn't work for historical values.
    /// @param blockNumber block.number of total supply, must be in past
    /// @return pastTotalSupply veFXS supply at ```blockNumber```
    function getPastTotalSupply(uint256 blockNumber) external view returns (uint256 pastTotalSupply) {
        // Future blocks are not valid
        if (blockNumber >= block.number) revert IVeFxsVotingDelegation.BlockNumberInFuture();

        // Our total voting weight isn't the same as VE_FXS.totalSupplyAt(blockNumber) because
        // we expire all voting weight when the lock ends, which also may not be accounted for yet.
        // This is close enough.
        pastTotalSupply = VE_FXS.totalSupplyAt(blockNumber);
    }

    /// @notice The ```delegates``` function returns who the address of the delegate, that delegatorAddress has chosen
    /// @param delegator Address of delegator
    /// @return delegateAddress Address of the delegate
    function delegates(address delegator) external view returns (address delegateAddress) {
        delegateAddress = $delegations[delegator].delegate;
    }

    /// @notice The ```delegate``` function delegates votes from signer to ```delegatee``` at the next epoch
    /// @param delegatee Address to delegate to
    function delegate(address delegatee) external {
        _delegate({ delegator: msg.sender, delegatee: delegatee });
    }

    /// @notice The ```delegate``` function delegates votes from signer to ```delegatee```
    /// @param delegatee Address to delegate to
    /// @param nonce Nonce of signed message
    /// @param expiry Expiry time of message
    /// @param v Recovery ID
    /// @param r Output of an ECDSA signature
    /// @param s Output of an ECDSA signature
    function delegateBySig(
        address delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual override {
        // Revert if signature is expired
        if (block.timestamp > expiry) revert IVeFxsVotingDelegation.SignatureExpired();

        address signer = ECDSA.recover(
            _hashTypedDataV4(keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry))),
            v,
            r,
            s
        );

        // Increment nonce and check against incremented value
        if (nonce != $nonces[signer]++) revert IVeFxsVotingDelegation.InvalidSignatureNonce();
        _delegate(signer, delegatee);
    }

    /// @notice The ```_delegate``` function delegates votes from signer to ```delegatee``` at the next epoch
    /// @param delegator Caller delegating their weight
    /// @param delegatee Address to delegate to
    /// @dev An account can only delegate to one account at a time. The previous delegation will be overwritten.
    /// @dev To undelegate, ```delegatee``` should be ```address(delegator)```
    function _delegate(address delegator, address delegatee) internal {
        // Revert if delegating to self with address(0), should be address(delegator)
        if (delegatee == address(0)) revert IVeFxsVotingDelegation.IncorrectSelfDelegation();

        IVeFxsVotingDelegation.Delegation memory previousDelegation = $delegations[delegator];

        // This ensures that checkpoints take effect at the next epoch
        uint256 checkpointTimestamp = ((block.timestamp / 1 days) * 1 days) + 1 days;

        IVeFxsVotingDelegation.NormalizedVeFxsLockInfo
            memory normalizedDelegatorVeFxsLockInfo = _getNormalizedVeFxsLockInfo({
                delegator: delegator,
                checkpointTimestamp: checkpointTimestamp
            });

        _moveVotingPowerFromPreviousDelegate({
            previousDelegation: previousDelegation,
            checkpointTimestamp: checkpointTimestamp
        });

        _moveVotingPowerToNewDelegate({
            newDelegate: delegatee,
            delegatorVeFxsLockInfo: normalizedDelegatorVeFxsLockInfo,
            checkpointTimestamp: checkpointTimestamp
        });

        // NOTE: Unsafe casting because these values will never exceed the size of their respective types
        $delegations[delegator] = IVeFxsVotingDelegation.Delegation({
            delegate: delegatee,
            firstDelegationTimestamp: previousDelegation.firstDelegationTimestamp == 0
                ? uint48(checkpointTimestamp)
                : previousDelegation.firstDelegationTimestamp,
            expiry: uint48(normalizedDelegatorVeFxsLockInfo.expiry),
            bias: uint96(normalizedDelegatorVeFxsLockInfo.bias),
            fxs: uint96(normalizedDelegatorVeFxsLockInfo.fxs),
            slope: uint64(normalizedDelegatorVeFxsLockInfo.slope)
        });

        emit DelegateChanged({
            delegator: delegator,
            fromDelegate: previousDelegation.delegate,
            toDelegate: delegatee
        });
    }

    /// @notice The ```_getNormalizedVeFxsLockInfo``` function retrieves lock information from veFXS. We normalize and store this information to calculate voting weights
    /// @param delegator Address of the delegator
    /// @param checkpointTimestamp block.timestamp of the next checkpoint epoch
    /// @return normalizedVeFxsLockInfo Information about delegator's lock from veFXS contract, normalized
    function _getNormalizedVeFxsLockInfo(
        address delegator,
        uint256 checkpointTimestamp
    ) private view returns (IVeFxsVotingDelegation.NormalizedVeFxsLockInfo memory normalizedVeFxsLockInfo) {
        // Check expiry in case we need to revert
        uint256 expiry = VE_FXS.locked(delegator).end;
        if (expiry <= checkpointTimestamp) revert IVeFxsVotingDelegation.CantDelegateLockExpired();

        // Most recent epoch
        uint256 epoch = VE_FXS.user_point_epoch(delegator);
        // Values for delegator at the most recent epoch
        (int128 userBias, int128 userSlope, , , uint256 userFxs) = VE_FXS.user_point_history({
            _addr: delegator,
            _idx: epoch
        });
        // Get the timestamp of the last update in veFXS user history
        uint256 lastUpdate = VE_FXS.user_point_history__ts({ _addr: delegator, _idx: epoch });

        // Set return values
        normalizedVeFxsLockInfo.slope = SafeCast.toUint256(userSlope);
        normalizedVeFxsLockInfo.fxs = userFxs;
        normalizedVeFxsLockInfo.expiry = expiry;
        // Normalize bias to unix epoch, so all biases can be added and subtracted directly
        normalizedVeFxsLockInfo.bias = SafeCast.toUint256(userBias) + normalizedVeFxsLockInfo.slope * lastUpdate;
    }

    /// @notice The ```_checkpointBinarySearch``` function does a binary search for the closest checkpoint before ```timestamp```
    /// @param _$checkpoints Storage pointer to the account's DelegateCheckpoints
    /// @param timestamp block.timestamp to get voting power at, frequently proposal snapshot
    /// @return closestCheckpoint The closest DelegateCheckpoint before timestamp
    function _checkpointBinarySearch(
        IVeFxsVotingDelegation.DelegateCheckpoint[] storage _$checkpoints,
        uint256 timestamp
    ) private view returns (IVeFxsVotingDelegation.DelegateCheckpoint memory closestCheckpoint) {
        uint256 checkpointsLength = _$checkpoints.length;

        // What the newest checkpoint could be for timestamp (rounded to whole days). It will be earlier when checkpoints are sparse.
        uint256 roundedDownTimestamp = (timestamp / 1 days) * 1 days;
        // Newest checkpoint's timestamp (already rounded to whole days)
        uint256 lastCheckpointTimestamp = checkpointsLength > 0 ? _$checkpoints[checkpointsLength - 1].timestamp : 0;
        // The furthest back a checkpoint will ever be is the number of days delta between timestamp and the last
        // checkpoints timestamp. This happens when there was a checkpoint written every single day over that period.
        // If roundedDownTimestamp > lastCheckpointTimestamp that means that we can just use the last index as
        // the checkpoint.
        uint256 delta = lastCheckpointTimestamp > roundedDownTimestamp
            ? (lastCheckpointTimestamp - roundedDownTimestamp) / 1 days
            : 0;
        // low index is equal to the last checkpoints index minus the index delta
        uint256 low = (checkpointsLength > 0 && checkpointsLength - 1 > delta) ? checkpointsLength - 1 - delta : 0;

        uint256 high = checkpointsLength;
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_$checkpoints[mid].timestamp > timestamp) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        closestCheckpoint = high == 0 ? closestCheckpoint : _$checkpoints[high - 1];
    }

    /// @notice The ```_moveVotingPowerFromPreviousDelegate``` function removes voting power from the previous delegate, handling expirations
    /// @notice and writing a new DelegateCheckpoint
    /// @param previousDelegation The delegator's previous delegation
    /// @param checkpointTimestamp block.timestamp of the next DelegateCheckpoint's epoch
    function _moveVotingPowerFromPreviousDelegate(
        IVeFxsVotingDelegation.Delegation memory previousDelegation,
        uint256 checkpointTimestamp
    ) private {
        // Remove voting power from previous delegate, if they exist
        if (previousDelegation.delegate != address(0)) {
            // Get the last Checkpoint for previous delegate
            IVeFxsVotingDelegation.DelegateCheckpoint[] storage $previousDelegationCheckpoints = $delegateCheckpoints[
                previousDelegation.delegate
            ];
            uint256 accountCheckpointsLength = $previousDelegationCheckpoints.length;
            // NOTE: we know that _accountsCheckpointLength > 0 because we have already checked that the previous delegation exists
            IVeFxsVotingDelegation.DelegateCheckpoint memory lastCheckpoint = $previousDelegationCheckpoints[
                accountCheckpointsLength - 1
            ];
            uint256 oldWeightOldDelegate = _getVotingWeight(previousDelegation.delegate, checkpointTimestamp);

            // Handle Expirations

            // Only subtract when the delegator's expiration is in the future. This way, the former delegates voting power
            // will still properly expire for proposals that happen between previousDelegation.expiry and a new delegation.
            // See testExpiredLockRedelegateNoVotingWeight().
            if (previousDelegation.expiry > checkpointTimestamp) {
                // Calculations
                IVeFxsVotingDelegation.Expiration memory expiration = $expiredDelegations[previousDelegation.delegate][
                    previousDelegation.expiry
                ];
                // All expiration fields will never exceed their size so subtraction doesnt need to be checked
                // and they can be unsafely cast
                unchecked {
                    expiration.bias -= uint96(previousDelegation.bias);
                    expiration.slope -= uint64(previousDelegation.slope);
                    expiration.fxs -= uint96(previousDelegation.fxs);
                }

                // Effects
                $expiredDelegations[previousDelegation.delegate][previousDelegation.expiry] = expiration;
            }

            {
                // Calculate new checkpoint
                IVeFxsVotingDelegation.DelegateCheckpoint memory newCheckpoint = _calculateCheckpoint({
                    previousCheckpoint: lastCheckpoint,
                    account: previousDelegation.delegate,
                    isDeltaPositive: false,
                    deltaBias: previousDelegation.bias,
                    deltaSlope: previousDelegation.slope,
                    deltaFxs: previousDelegation.fxs,
                    checkpointTimestamp: checkpointTimestamp,
                    previousDelegationExpiry: previousDelegation.expiry
                });

                // Write new checkpoint
                _writeCheckpoint({
                    $userDelegationCheckpoints: $previousDelegationCheckpoints,
                    accountCheckpointsLength: accountCheckpointsLength,
                    newCheckpoint: newCheckpoint,
                    lastCheckpoint: lastCheckpoint
                });
            }

            // NOTE: oldWeightOldDelegate has had voting power decay since the previous event, meaning that
            // this event shouldn't be relied on. We did our best to conform to the standard.
            emit DelegateVotesChanged({
                delegate: previousDelegation.delegate,
                previousBalance: oldWeightOldDelegate,
                newBalance: _getVotingWeight({ voter: previousDelegation.delegate, timestamp: checkpointTimestamp })
            });
        }
    }

    /// @notice The ```_moveVotingPowerToNewDelegate``` function adds voting power to the new delegate, handling expirations
    /// @notice and writing a new DelegateCheckpoint
    /// @param newDelegate The new delegate that is being delegated to
    /// @param delegatorVeFxsLockInfo Information about the delegator's veFXS lock
    /// @param checkpointTimestamp block.timestamp of the next DelegateCheckpoint's epoch
    function _moveVotingPowerToNewDelegate(
        address newDelegate,
        IVeFxsVotingDelegation.NormalizedVeFxsLockInfo memory delegatorVeFxsLockInfo,
        uint256 checkpointTimestamp
    ) private {
        // Get the last checkpoint for the new delegate
        IVeFxsVotingDelegation.DelegateCheckpoint[] storage $newDelegateCheckpoints = $delegateCheckpoints[newDelegate];
        uint256 accountCheckpointsLength = $newDelegateCheckpoints.length;
        IVeFxsVotingDelegation.DelegateCheckpoint memory lastCheckpoint = accountCheckpointsLength == 0
            ? IVeFxsVotingDelegation.DelegateCheckpoint(0, 0, 0, 0)
            : $newDelegateCheckpoints[accountCheckpointsLength - 1];
        uint256 oldWeightNewDelegate = _getVotingWeight(newDelegate, checkpointTimestamp);

        // Handle expiration
        // Calculations
        IVeFxsVotingDelegation.Expiration memory expiration = $expiredDelegations[newDelegate][
            delegatorVeFxsLockInfo.expiry
        ];

        // NOTE: All expiration fields will never exceed their size so addition doesnt need to be checked
        // and can be unsafely cast
        unchecked {
            expiration.bias += uint96(delegatorVeFxsLockInfo.bias);
            expiration.slope += uint64(delegatorVeFxsLockInfo.slope);
            expiration.fxs += uint96(delegatorVeFxsLockInfo.fxs);
        }
        // Effects
        $expiredDelegations[newDelegate][delegatorVeFxsLockInfo.expiry] = expiration;

        // Calculate new checkpoint
        IVeFxsVotingDelegation.DelegateCheckpoint memory newCheckpoint = _calculateCheckpoint({
            previousCheckpoint: lastCheckpoint,
            isDeltaPositive: true,
            account: newDelegate,
            deltaBias: delegatorVeFxsLockInfo.bias,
            deltaSlope: delegatorVeFxsLockInfo.slope,
            deltaFxs: delegatorVeFxsLockInfo.fxs,
            checkpointTimestamp: checkpointTimestamp,
            previousDelegationExpiry: 0 // not used
        });

        // Write new checkpoint
        _writeCheckpoint({
            $userDelegationCheckpoints: $newDelegateCheckpoints,
            accountCheckpointsLength: accountCheckpointsLength,
            newCheckpoint: newCheckpoint,
            lastCheckpoint: lastCheckpoint
        });

        // NOTE: oldWeightNewDelegate has had voting power decay since the previous event, meaning that
        // this event shouldn't be relied on. We did our best to conform to the standard.
        emit DelegateVotesChanged({
            delegate: newDelegate,
            previousBalance: oldWeightNewDelegate,
            newBalance: _getVotingWeight({ voter: newDelegate, timestamp: checkpointTimestamp })
        });
    }

    /// @notice The ```_calculateCheckpoint``` function calculates the values to be written for the new DelegateCheckpoint
    /// @param previousCheckpoint The previous checkpoint for account
    /// @param account The account to calculate the expirations for
    /// @param isDeltaPositive Whether adding or subtracting from the previous checkpoint
    /// @param deltaBias Amount of bias to add or subtract
    /// @param deltaSlope Amount of slope to add or subtract
    /// @param deltaFxs Amount of FXS to add or subtract
    /// @param checkpointTimestamp block.timestamp of the next DelegateCheckpoint's epoch
    /// @param previousDelegationExpiry When the previous delegation expires
    /// @return newCheckpoint The new DelegateCheckpoint to be stored
    function _calculateCheckpoint(
        IVeFxsVotingDelegation.DelegateCheckpoint memory previousCheckpoint,
        address account,
        bool isDeltaPositive,
        uint256 deltaBias,
        uint256 deltaSlope,
        uint256 deltaFxs,
        uint256 checkpointTimestamp,
        uint256 previousDelegationExpiry
    ) private view returns (IVeFxsVotingDelegation.DelegateCheckpoint memory newCheckpoint) {
        // If this is the first checkpoint, create a new one and early return
        if (previousCheckpoint.timestamp == 0) {
            return
                IVeFxsVotingDelegation.DelegateCheckpoint({
                    // can be unsafely cast because values will never exceed uint128 max
                    timestamp: uint128(checkpointTimestamp),
                    normalizedBias: uint128(deltaBias),
                    normalizedSlope: uint128(deltaSlope),
                    totalFxs: uint128(deltaFxs)
                });
        }

        newCheckpoint.timestamp = previousCheckpoint.timestamp;
        newCheckpoint.normalizedBias = previousCheckpoint.normalizedBias;
        newCheckpoint.normalizedSlope = previousCheckpoint.normalizedSlope;
        newCheckpoint.totalFxs = previousCheckpoint.totalFxs;

        // All checkpoint fields will never exceed their size so addition and subtraction doesnt need to be checked
        unchecked {
            // Add or subtract the delta to the previous checkpoint
            if (isDeltaPositive) {
                newCheckpoint.normalizedBias += uint128(deltaBias);
                newCheckpoint.normalizedSlope += uint128(deltaSlope);
                newCheckpoint.totalFxs += uint128(deltaFxs);
            } else {
                // only subtract the weight from this account if it has not already expired in a previous checkpoint
                if (previousDelegationExpiry > previousCheckpoint.timestamp) {
                    newCheckpoint.normalizedBias -= uint128(deltaBias);
                    newCheckpoint.normalizedSlope -= uint128(deltaSlope);
                    newCheckpoint.totalFxs -= uint128(deltaFxs);
                }
            }

            // If there have been expirations, incorporate the adjustments by subtracting them from the checkpoint
            if (newCheckpoint.timestamp != checkpointTimestamp) {
                (uint256 totalExpiredBias, uint256 totalExpiredSlope, uint256 totalExpiredFxs) = _calculateExpirations({
                    account: account,
                    start: newCheckpoint.timestamp,
                    end: checkpointTimestamp,
                    checkpoint: previousCheckpoint
                });

                newCheckpoint.timestamp = uint128(checkpointTimestamp);
                newCheckpoint.normalizedBias -= uint128(totalExpiredBias);
                newCheckpoint.normalizedSlope -= uint128(totalExpiredSlope);
                newCheckpoint.totalFxs -= uint128(totalExpiredFxs);
            }
        }
    }

    /// @notice The ```_writeCheckpoint``` function pushes a new checkpoint to the array or updates the most recent one if it already exists for the current epoch
    /// @param $userDelegationCheckpoints Pointer to the user's delegation checkpoints
    /// @param accountCheckpointsLength The length of the user's delegation checkpoints
    /// @param newCheckpoint The new checkpoint returned from _calculateCheckpoint()
    /// @param lastCheckpoint The most recent delegate checkpoint
    function _writeCheckpoint(
        IVeFxsVotingDelegation.DelegateCheckpoint[] storage $userDelegationCheckpoints,
        uint256 accountCheckpointsLength,
        IVeFxsVotingDelegation.DelegateCheckpoint memory newCheckpoint,
        IVeFxsVotingDelegation.DelegateCheckpoint memory lastCheckpoint
    ) internal {
        // If the newCheckpoint has the same timestamp as the last checkpoint, overwrite it
        if (accountCheckpointsLength > 0 && lastCheckpoint.timestamp == newCheckpoint.timestamp) {
            $userDelegationCheckpoints[accountCheckpointsLength - 1] = newCheckpoint;
        } else {
            // Otherwise, push a new checkpoint
            $userDelegationCheckpoints.push(newCheckpoint);
        }
    }

    /// @notice The ```_calculateExpirations``` function generates a summation of all bias, slope, and fxs for all delegations that expire during the specified time window for ```account```
    /// @param account Delegate account to generate summations for
    /// @param start Timestamp to start the summations from. The start is not included
    /// @param end Timestamp to end the summations. The end is included
    /// @param checkpoint Checkpoint to start expirations at
    /// @return totalExpiredBias Total bias that expired for delegate ```account``` during timeframe
    /// @return totalExpiredSlope Total slope that expired for delegate ```account``` during timeframe
    /// @return totalExpiredFxs Total FXS that expired for delegate ```account``` during timeframe
    function _calculateExpirations(
        address account,
        uint256 start,
        uint256 end,
        IVeFxsVotingDelegation.DelegateCheckpoint memory checkpoint
    ) private view returns (uint256 totalExpiredBias, uint256 totalExpiredSlope, uint256 totalExpiredFxs) {
        unchecked {
            // Maximum lock time for veFXS is 4 years, it will all be expired
            if (end > start + MAX_LOCK_DURATION) {
                totalExpiredBias = checkpoint.normalizedBias;
                totalExpiredSlope = checkpoint.normalizedSlope;
                totalExpiredFxs = checkpoint.totalFxs;
            } else {
                // Total values will always be less than or equal to a checkpoint's values
                uint256 currentWeek = WEEK + (start / WEEK) * WEEK;
                mapping(uint256 => IVeFxsVotingDelegation.Expiration)
                    storage $delegateExpirations = $expiredDelegations[account];
                // Sum values from currentWeek until end
                while (currentWeek <= end) {
                    IVeFxsVotingDelegation.Expiration memory expiration = $delegateExpirations[currentWeek];
                    totalExpiredBias += expiration.bias;
                    totalExpiredSlope += expiration.slope;
                    totalExpiredFxs += expiration.fxs;
                    currentWeek += WEEK;
                }
            }
        }
    }
}

File 2 of 15 : 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 3 of 15 : 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 4 of 15 : 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 5 of 15 : 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 6 of 15 : IERC5805.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5805.sol)

pragma solidity ^0.8.0;

import "../governance/utils/IVotes.sol";
import "./IERC6372.sol";

interface IERC5805 is IERC6372, IVotes {}

File 7 of 15 : IVeFxs.sol
// SPDX-License-Identifier: ISC
pragma solidity >=0.6.11;
pragma abicoder v2;

interface IVeFxs {
    struct LockedBalance {
        int128 amount;
        uint256 end;
    }

    function commit_transfer_ownership(address addr) external;

    function apply_transfer_ownership() external;

    function commit_smart_wallet_checker(address addr) external;

    function apply_smart_wallet_checker() external;

    function toggleEmergencyUnlock() external;

    function recoverERC20(address token_addr, uint256 amount) external;

    function get_last_user_slope(address addr) external view returns (int128);

    function user_point_history__ts(address _addr, uint256 _idx) external view returns (uint256);

    function locked__end(address _addr) external view returns (uint256);

    function checkpoint() external;

    function deposit_for(address _addr, uint256 _value) external;

    function create_lock(uint256 _value, uint256 _unlock_time) external;

    function increase_amount(uint256 _value) external;

    function increase_unlock_time(uint256 _unlock_time) external;

    function withdraw() external;

    function balanceOf(address addr) external view returns (uint256);

    function balanceOf(address addr, uint256 _t) external view returns (uint256);

    function balanceOfAt(address addr, uint256 _block) external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function totalSupply(uint256 t) external view returns (uint256);

    function totalSupplyAt(uint256 _block) external view returns (uint256);

    function totalFXSSupply() external view returns (uint256);

    function totalFXSSupplyAt(uint256 _block) external view returns (uint256);

    function changeController(address _newController) external;

    function token() external view returns (address);

    function supply() external view returns (uint256);

    function locked(address addr) external view returns (LockedBalance memory);

    function epoch() external view returns (uint256);

    function point_history(
        uint256 arg0
    ) external view returns (int128 bias, int128 slope, uint256 ts, uint256 blk, uint256 fxs_amt);

    function user_point_history(
        address _addr,
        uint256 _idx
    ) external view returns (int128 bias, int128 slope, uint256 ts, uint256 blk, uint256 fxs_amt);

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

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

    function controller() external view returns (address);

    function transfersEnabled() external view returns (bool);

    function emergencyUnlockActive() external view returns (bool);

    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function version() external view returns (string memory);

    function decimals() external view returns (uint256);

    function future_smart_wallet_checker() external view returns (address);

    function smart_wallet_checker() external view returns (address);

    function admin() external view returns (address);

    function future_admin() external view returns (address);
}

File 8 of 15 : IVeFxsVotingDelegation.sol
// SPDX-License-Identifier: ISC
pragma solidity >=0.8.19;

interface IVeFxsVotingDelegation {
    /// Represents the values of a single delegation at the time `delegate()` is called,
    /// to be subtracted when removing delegation
    struct Delegation {
        address delegate;
        uint48 firstDelegationTimestamp;
        uint48 expiry;
        // __________
        uint96 bias;
        uint96 fxs;
        uint64 slope;
    }

    /// A representation of a delegate and all its delegators at a particular timestamp
    struct DelegateCheckpoint {
        uint128 normalizedBias;
        uint128 totalFxs;
        // _________
        uint128 normalizedSlope;
        uint128 timestamp; // Rounded up to the nearest day
    }

    /// Represents the total bias, slope, and FXS amount of all accounts that expire for a specific delegate
    /// in a particular week
    struct Expiration {
        uint96 bias;
        uint96 fxs;
        uint64 slope;
    }

    // Only used in memory
    struct NormalizedVeFxsLockInfo {
        uint256 bias;
        uint256 slope;
        uint256 fxs;
        uint256 expiry;
    }

    function $delegateCheckpoints(
        address,
        uint256
    ) external view returns (uint128 normalizedBias, uint128 totalFxs, uint128 normalizedSlope, uint128 timestamp);

    function $delegations(
        address
    )
        external
        view
        returns (
            address delegate,
            uint48 firstDelegationTimestamp,
            uint48 expiry,
            uint96 bias,
            uint96 fxs,
            uint64 slope
        );

    function $expiredDelegations(address, uint256) external view returns (uint96 bias, uint96 fxs, uint64 slope);

    function $nonces(address) external view returns (uint256);

    function CLOCK_MODE() external pure returns (string memory);

    function DELEGATION_TYPEHASH() external view returns (bytes32);

    function MAX_LOCK_DURATION() external view returns (uint256);

    function VE_FXS() external view returns (address);

    function VOTE_WEIGHT_MULTIPLIER() external view returns (uint256);

    function WEEK() external view returns (uint256);

    function calculateExpiredDelegations(
        address delegateAddress
    ) external view returns (DelegateCheckpoint memory calculatedCheckpoint);

    function clock() external view returns (uint48);

    function delegate(address delegatee) external;

    function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;

    function delegates(address delegator) external view returns (address delegateAddress);

    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );

    function getCheckpoint(address delegateAddress, uint32 index) external view returns (DelegateCheckpoint memory);

    function getPastTotalSupply(uint256 blockNumber) external view returns (uint256 pastTotalSupply);

    function getPastVotes(address voter, uint256 timepoint) external view returns (uint256 pastVotingWeight);

    function getVotes(address voter) external view returns (uint256 votingWeight);

    function getVotes(address voter, uint256 timepoint) external view returns (uint256);

    function writeNewCheckpointForExpiredDelegations(address delegateAddress) external;

    error BlockNumberInFuture();
    error CantDelegateLockExpired();
    error IncorrectSelfDelegation();
    error InvalidSignatureNonce();
    error NoExpirations();
    error SignatureExpired();
    error TimestampInFuture();
}

File 9 of 15 : 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 10 of 15 : 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 11 of 15 : 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 12 of 15 : IVotes.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (governance/utils/IVotes.sol)
pragma solidity ^0.8.0;

/**
 * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
 *
 * _Available since v4.5._
 */
interface IVotes {
    /**
     * @dev Emitted when an account changes their delegate.
     */
    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);

    /**
     * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
     */
    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);

    /**
     * @dev Returns the current amount of votes that `account` has.
     */
    function getVotes(address account) external view returns (uint256);

    /**
     * @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is
     * configured to use block numbers, this will return the value at the end of the corresponding block.
     */
    function getPastVotes(address account, uint256 timepoint) external view returns (uint256);

    /**
     * @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is
     * configured to use block numbers, this will return the value at the end of the corresponding block.
     *
     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
     * Votes that have not been delegated are still part of total supply, even though they would not participate in a
     * vote.
     */
    function getPastTotalSupply(uint256 timepoint) external view returns (uint256);

    /**
     * @dev Returns the delegate that `account` has chosen.
     */
    function delegates(address account) external view returns (address);

    /**
     * @dev Delegates votes from the sender to `delegatee`.
     */
    function delegate(address delegatee) external;

    /**
     * @dev Delegates votes from signer to `delegatee`.
     */
    function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;
}

File 13 of 15 : IERC6372.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC6372.sol)

pragma solidity ^0.8.0;

interface IERC6372 {
    /**
     * @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting).
     */
    function clock() external view returns (uint48);

    /**
     * @dev Description of the clock
     */
    // solhint-disable-next-line func-name-mixedcase
    function CLOCK_MODE() external view returns (string memory);
}

File 14 of 15 : 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);
        }
    }
}

File 15 of 15 : 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
        }
    }
}

Settings
{
  "remappings": [
    "@gnosis.pm/=node_modules/@gnosis.pm/safe-contracts/",
    "@openzeppelin/=node_modules/openzeppelin-solidity/",
    "ds-test/=node_modules/ds-test/src/",
    "forge-std/=node_modules/forge-std/src/",
    "frax-std/=node_modules/frax-standard-solidity/src/",
    "safe-contracts/=node_modules/@gnosis.pm/safe-contracts/contracts/",
    "safe-tools/=node_modules/safe-tools/src/",
    "solady/=node_modules/solady/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"veFxs","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BlockNumberInFuture","type":"error"},{"inputs":[],"name":"CantDelegateLockExpired","type":"error"},{"inputs":[],"name":"IncorrectSelfDelegation","type":"error"},{"inputs":[],"name":"InvalidShortString","type":"error"},{"inputs":[],"name":"InvalidSignatureNonce","type":"error"},{"inputs":[],"name":"NoExpirations","type":"error"},{"inputs":[],"name":"SignatureExpired","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"inputs":[],"name":"TimestampInFuture","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"inputs":[{"internalType":"address","name":"delegate","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"$delegateCheckpoints","outputs":[{"internalType":"uint128","name":"normalizedBias","type":"uint128"},{"internalType":"uint128","name":"totalFxs","type":"uint128"},{"internalType":"uint128","name":"normalizedSlope","type":"uint128"},{"internalType":"uint128","name":"timestamp","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"}],"name":"$delegations","outputs":[{"internalType":"address","name":"delegate","type":"address"},{"internalType":"uint48","name":"firstDelegationTimestamp","type":"uint48"},{"internalType":"uint48","name":"expiry","type":"uint48"},{"internalType":"uint96","name":"bias","type":"uint96"},{"internalType":"uint96","name":"fxs","type":"uint96"},{"internalType":"uint64","name":"slope","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegate","type":"address"},{"internalType":"uint256","name":"week","type":"uint256"}],"name":"$expiredDelegations","outputs":[{"internalType":"uint96","name":"bias","type":"uint96"},{"internalType":"uint96","name":"fxs","type":"uint96"},{"internalType":"uint64","name":"slope","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"name":"$nonces","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CLOCK_MODE","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_LOCK_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VE_FXS","outputs":[{"internalType":"contract IVeFxs","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VOTE_WEIGHT_MULTIPLIER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WEEK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegateAddress","type":"address"}],"name":"calculateExpiredDelegations","outputs":[{"components":[{"internalType":"uint128","name":"normalizedBias","type":"uint128"},{"internalType":"uint128","name":"totalFxs","type":"uint128"},{"internalType":"uint128","name":"normalizedSlope","type":"uint128"},{"internalType":"uint128","name":"timestamp","type":"uint128"}],"internalType":"struct IVeFxsVotingDelegation.DelegateCheckpoint","name":"calculatedCheckpoint","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clock","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"delegateAddress","type":"address"}],"stateMutability":"view","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":[{"internalType":"address","name":"delegateAddress","type":"address"},{"internalType":"uint32","name":"index","type":"uint32"}],"name":"getCheckpoint","outputs":[{"components":[{"internalType":"uint128","name":"normalizedBias","type":"uint128"},{"internalType":"uint128","name":"totalFxs","type":"uint128"},{"internalType":"uint128","name":"normalizedSlope","type":"uint128"},{"internalType":"uint128","name":"timestamp","type":"uint128"}],"internalType":"struct IVeFxsVotingDelegation.DelegateCheckpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"pastTotalSupply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"voter","type":"address"},{"internalType":"uint256","name":"timepoint","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"pastVotingWeight","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"voter","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"votingWeight","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"voter","type":"address"},{"internalType":"uint256","name":"timepoint","type":"uint256"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegateAddress","type":"address"}],"name":"writeNewCheckpointForExpiredDelegations","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101808060405234620001c057620034d38038038091620000218285620001c5565b8339810190606081830312620001c05780516001600160a01b03811690819003620001c05760208201516001600160401b039290838111620001c057846200006b9183016200020e565b936040820151848111620001c0576200008592016200020e565b92620000918162000269565b610120908152620000a2856200041d565b9161014092835260208151910120948560e0526020815191012094610100958087524660a0526040519060208201927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8452604083015260608201524660808201523060a082015260a0815260c081019581871090871117620001aa57856040525190206080523060c052610160928352612f6994856200056a863960805185611ce5015260a05185611da0015260c05185611cb6015260e05185611d3401525184611d5a015251836107060152518261073001525181818161062c01528181610a8c015281816118d601528181611f0a01528181611fa90152818161204501526120e80152f35b634e487b7160e01b600052604160045260246000fd5b600080fd5b601f909101601f19168101906001600160401b03821190821017620001aa57604052565b60005b838110620001fd5750506000910152565b8181015183820152602001620001ec565b81601f82011215620001c05780516001600160401b038111620001aa576040519262000245601f8301601f191660200185620001c5565b81845260208284010111620001c057620002669160208085019101620001e9565b90565b80516020919082811015620002e9575090601f825111620002a757808251920151908083106200029857501790565b82600019910360031b1b161790565b604490620002db9260405193849263305a27a960e01b845280600485015282519283918260248701528686019101620001e9565b601f01601f19168101030190fd5b6001600160401b038111620001aa576000928354926001938481811c9116801562000412575b83821014620003fe57601f8111620003c8575b5081601f8411600114620003615750928293918392869462000355575b50501b916000199060031b1c191617905560ff90565b0151925038806200033f565b919083601f1981168780528488209488905b88838310620003ad575050501062000393575b505050811b01905560ff90565b015160001960f88460031b161c1916905538808062000386565b85870151885590960195948501948793509081019062000373565b85805284601f848820920160051c820191601f860160051c015b828110620003f257505062000322565b878155018590620003e2565b634e487b7160e01b86526022600452602486fd5b90607f16906200030f565b8051602090818110156200044a5750601f825111620002a757808251920151908083106200029857501790565b9192916001600160401b038111620001aa5760019182548381811c911680156200055e575b828210146200054857601f81116200050f575b5080601f8311600114620004c2575081929394600092620004b6575b5050600019600383901b1c191690821b17905560ff90565b0151905038806200049e565b90601f198316958460005282600020926000905b888210620004f757505083859697106200039357505050811b01905560ff90565b808785968294968601518155019501930190620004d6565b8360005283601f83600020920160051c820191601f850160051c015b8281106200053b57505062000482565b600081550184906200052b565b634e487b7160e01b600052602260045260246000fd5b90607f16906200046f56fe608060408181526004918236101561001657600080fd5b600092833560e01c918263101f46d514610d96575081631c94541214610d0857816334940fa814610c7c5781633a46b1a814610bfa5781633b3eb8a614610b6c5781634bf5d7e914610aed5781634f1bfc9e14610ab0578163506d1f8114610a41578163587cde1e146109dc5781635c19a95c1461099b5781636e22c4af146108ed57816375f9e3961461084157816384b0196e146106d05781638e539e8c146105aa57816391ddadf414610569578163944c39301461052f5781639ab24eb0146104db578163b06128211461042b578163c3cda5201461020d57508063e7a324dc146101b5578063eb9019d4146101555763f4359ce51461011757600080fd5b3461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020905162093a808152f35b5080fd5b503461015157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020906101ae610192610df5565b6101a86024356101a281846118bc565b92611522565b9061129d565b9051908152f35b503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015157602090517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf8152f35b8383346101515760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015157610246610df5565b9060243560443560643560ff81168103610427578142116103ff57835160208101907fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf825273ffffffffffffffffffffffffffffffffffffffff938488168783015285606083015260808201526080815260a0810181811067ffffffffffffffff8211176103d357865251902061032f94939291610327916102e6611c9f565b908651917f190100000000000000000000000000000000000000000000000000000000000083526002830152602282015260a4359160426084359220611c03565b949094611a6a565b831685526002602052818520908154917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83146103a7576001830190550361037f57509061037c91611dc6565b80f35b8490517fc4689a54000000000000000000000000000000000000000000000000000000008152fd5b60248760118a7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b60248960418c7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8684517f0819bdcd000000000000000000000000000000000000000000000000000000008152fd5b8580fd5b8383346101515760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015157610464610df5565b9061046e826112b7565b916fffffffffffffffffffffffffffffffff606084015116156104b3579373ffffffffffffffffffffffffffffffffffffffff61037c949516855260205283206114f4565b8482517fc1ccb3d1000000000000000000000000000000000000000000000000000000008152fd5b5050346101515760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020906101ae61051a610df5565b6101a861052742836118bc565b914290611522565b50503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020905160038152f35b50503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020905165ffffffffffff42168152f35b83833461015157602092837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106cc578035438110156106a4578251917f981b24d0000000000000000000000000000000000000000000000000000000008352820152838160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa928315610699578093610664575b505051908152f35b909192508382813d8311610692575b61067d8183610f65565b8101031261068f57505190838061065c565b80fd5b503d610673565b8251903d90823e3d90fd5b5090517f089b2f6f000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b919050346106cc57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106cc5761072a7f0000000000000000000000000000000000000000000000000000000000000000610fa6565b926107547f000000000000000000000000000000000000000000000000000000000000000061111c565b908251926020928385019585871067ffffffffffffffff881117610815575092806107cb8388966107be998b9996528686528151998a997f0f000000000000000000000000000000000000000000000000000000000000008b5260e0868c015260e08b0190610e1d565b91898303908a0152610e1d565b924660608801523060808801528460a088015286840360c088015251928381520193925b8281106107fe57505050500390f35b8351855286955093810193928101926001016107ef565b8360416024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b82843461068f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261068f57610879610df5565b9273ffffffffffffffffffffffffffffffffffffffff6024359416825260205281812090815484101561068f57506080926108b391610e7b565b509060018254920154908051926fffffffffffffffffffffffffffffffff908181168552851c6020850152821690830152821c6060820152f35b5050346101515760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015157908160c09273ffffffffffffffffffffffffffffffffffffffff9283610942610df5565b1681526003602052209060018254920154918151938116845265ffffffffffff8160a01c16602085015260d01c908301526bffffffffffffffffffffffff80821660608401528160601c166080830152821c60a0820152f35b833461068f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261068f5761037c6109d6610df5565b33611dc6565b5050346101515760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020918173ffffffffffffffffffffffffffffffffffffffff9182610a30610df5565b168152600385522054169051908152f35b50503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101515760209051630784ce008152f35b50503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151578051610b6891610b2c82610f49565b600e82527f6d6f64653d74696d657374616d70000000000000000000000000000000000000602083015251918291602083526020830190610e1d565b0390f35b5050346101515760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015157610b6890610bb1610bac610df5565b6112b7565b90519182918291909160606080820193816fffffffffffffffffffffffffffffffff91828151168552826020820151166020860152826040820151166040860152015116910152565b82843461068f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261068f5750610c33610df5565b916024359042821015610c555750826101a8826101a26101ae946020976118bc565b82517f47860b97000000000000000000000000000000000000000000000000000000008152fd5b9050346106cc57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106cc57610cb4610df5565b906024359163ffffffff83168303610d04579183610b6895610cfe9373ffffffffffffffffffffffffffffffffffffffff610bb196610cf16111ef565b5016825260205220610e7b565b50611214565b8480fd5b50503461015157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151578060609273ffffffffffffffffffffffffffffffffffffffff610d59610df5565b168152600560205281812060243582526020522054908051916bffffffffffffffffffffffff808216845281851c16602084015260c01c90820152f35b849084346106cc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106cc5760209273ffffffffffffffffffffffffffffffffffffffff610de8610df5565b1681526002845220548152f35b6004359073ffffffffffffffffffffffffffffffffffffffff82168203610e1857565b600080fd5b919082519283825260005b848110610e675750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b602081830181015184830182015201610e28565b8054821015610e975760005260206000209060011b0190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6080810190811067ffffffffffffffff821117610ee257604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60c0810190811067ffffffffffffffff821117610ee257604052565b6060810190811067ffffffffffffffff821117610ee257604052565b6040810190811067ffffffffffffffff821117610ee257604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610ee257604052565b60ff8114610ffc5760ff811690601f8211610fd25760405191610fc883610f49565b8252602082015290565b60046040517fb3512b0c000000000000000000000000000000000000000000000000000000008152fd5b506040516000805490600182811c90808416938415611112575b60209485841081146110e557838752869493929181156110a6575060011461104a575b505061104792500382610f65565b90565b60008080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56395935091905b81831061108e57505061104793508201013880611039565b85548784018501529485019486945091830191611076565b90506110479593507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b8201013880611039565b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526022600452fd5b91607f1691611016565b60ff811461113e5760ff811690601f8211610fd25760405191610fc883610f49565b50604051600060019081549182811c908084169384156111e5575b60209485841081146110e557838752869493929181156110a6575060011461118957505061104792500382610f65565b60008181527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf695935091905b8183106111cd57505061104793508201013880611039565b855487840185015294850194869450918301916111b5565b91607f1691611159565b604051906111fc82610ec6565b60006060838281528260208201528260408201520152565b9060405161122181610ec6565b6060819360018154916fffffffffffffffffffffffffffffffff92838116865260801c60208601520154908116604084015260801c910152565b8181029291811591840414171561126e57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9190820180921161126e57565b9190820391821161126e57565b6112bf6111ef565b90600073ffffffffffffffffffffffffffffffffffffffff82168152600460205260408120906201518091824204928084029380850482149015171561142b5783018093116113fe5780549182156113c9577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83019283116113d1575061134991610cfe91610e7b565b6fffffffffffffffffffffffffffffffff92836060830151168381146113c95783839161137593612e6b565b91909582156113bf5750908460208180809581885116031698816040880151160316940151160316604051946113aa86610ec6565b85526020850152604084015216606082015290565b9550505050505090565b505050505090565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526011600452fd5b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b906114c557815160208301516fffffffffffffffffffffffffffffffff9182167fffffffffffffffffffffffffffffffff00000000000000000000000000000000608092831b8116919091178455604085015160609095015190911b16921691909117600190910155565b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b9081549168010000000000000000831015610ee2578261151c9160016114c395018155610e7b565b90611458565b91909160009073ffffffffffffffffffffffffffffffffffffffff8116825260049384602052604083206115546111ef565b81546201518092838504938085029480860482149015171561179e57821580159190611865577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8401848111611839576115b060019185610e7b565b50015460801c955b6fffffffffffffffffffffffffffffffff968716908082111561182f576115de916112aa565b04905b806117fd575b156117f6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83018381116117ca5790611620916112aa565b82811061175b5750816116f0575050925b816060850151169081156116e65761167661168593602061165b8888611669999761167f97612e6b565b9491848b9a929a51166112aa565b978360408b0151166112aa565b970151166112aa565b9361125b565b808211156116de57611696916112aa565b915b826003029260038404036116b2575061104792935061129d565b806011867f4e487b71000000000000000000000000000000000000000000000000000000006024945252fd5b505081611698565b5093955050505050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201925090821161172f5761172991610cfe91610e7b565b92611631565b60248660118a7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b9160019061176f818518831c82861661129d565b91878161177c8587610e7b565b50015460801c111561179057505091611620565b909350810180911115611620575b60248860118c7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b60248960118d7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5086611620565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83018381116117ca5781106115e7565b50505087906115e1565b60248a60118e7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b88956115b8565b519081600f0b8203610e1857565b90816040910312610e185760405190604082019082821067ffffffffffffffff831117610ee2576020916040526118b08161186c565b83520151602082015290565b9073ffffffffffffffffffffffffffffffffffffffff90817f000000000000000000000000000000000000000000000000000000000000000016906040918251937fcbf9fe5f00000000000000000000000000000000000000000000000000000000855285168060048601528385602481855afa948515611a5f57600095611a30575b50826020809601511115611a25576000526003845265ffffffffffff836000205460a01c168015908115611a1b575b5061197e575b5050505050600090565b91517efdd58e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff94909416600485015260248401528190839060449082905afa9182916000936119ea575b50506110475780808080611974565b8181949293943d8311611a14575b611a028183610f65565b8101031261068f5750519038806119db565b503d6119f8565b905082103861196e565b505050505050600090565b611a51919550843d8611611a58575b611a498183610f65565b81019061187a565b933861193f565b503d611a3f565b84513d6000823e3d90fd5b6005811015611bd45780611a7b5750565b60018103611ae15760646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152fd5b60028103611b475760646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152fd5b600314611b5057565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9291907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311611c935791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa15611c8657815173ffffffffffffffffffffffffffffffffffffffff811615611c80579190565b50600190565b50604051903d90823e3d90fd5b50505050600090600390565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016301480611d9d575b15611d07577f000000000000000000000000000000000000000000000000000000000000000090565b60405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f000000000000000000000000000000000000000000000000000000000000000060408201527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260a08152611d9781610f11565b51902090565b507f00000000000000000000000000000000000000000000000000000000000000004614611cde565b73ffffffffffffffffffffffffffffffffffffffff821615612ac45773ffffffffffffffffffffffffffffffffffffffff81166000526003602052604060002091600160405193611e1685610f11565b805473ffffffffffffffffffffffffffffffffffffffff8116865265ffffffffffff8160a01c16602087015260d01c604086015201546bffffffffffffffffffffffff811660608501526bffffffffffffffffffffffff8160601c16608085015260c01c60a08401526201518090814204918083029280840482149015171561126e57808301831161126e57611eaa6111ef565b906040517fcbf9fe5f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8616600482015260408160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa80156129e657602091600091612aa5575b500151818501811115612a7b576040517f010ae75700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9081156129e657600091612a49575b506040517f28d09d4700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff881660048201526024810182905260a0818060448101038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa80156129e6576000916000906000926129f2575b506040517fda020a1800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b166004820152602481019490945260208480604481015b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9384156129e6576000946129aa575b509161215494612149926121326101a895600f0b612aee565b60208a015260408901526060880152600f0b612aee565b91602086015161125b565b825273ffffffffffffffffffffffffffffffffffffffff86511680612689575b5073ffffffffffffffffffffffffffffffffffffffff83166000526004602052604060002080549081156000146125fb579267ffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff968796946125c6946123c289986040516121dd81610ec6565b60008152600060208201526000604082015260006060820152905b6122136122078688018c6118bc565b6101a88789018d611522565b938b8b166000526005602052604060002060608a01516000526020526123a060406000206bffffffffffffffffffffffff8b8180604080519561225587610f2d565b54938285168752828560601c1660208801528460c01c828801528280825116818716011687528f806020830151168660c01c0116828801520151169160601c16011660208201528d8d166000526005602052604060002060608c015160005260205289604080600020926bffffffffffffffffffffffff8151167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008554161784556123566bffffffffffffffffffffffff60208301511685907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b0151825477ffffffffffffffffffffffffffffffffffffffffffffffff16911660c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016179055565b6123bc898c815160208301519060408b8d019401519288612cff565b91612def565b6123dd6123d1838501896118bc565b6101a88486018a611522565b60405191825260208201527fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724604089891692a260208b015165ffffffffffff1691826125f35765ffffffffffff92500116915b61257d6bffffffffffffffffffffffff65ffffffffffff606084015116928181511685602084604085015116930151169465ffffffffffff604051986124758a610f11565b8c8c168a52166020890152604088015260608701526080860190815260a08601938452888c166000526003602052816060600160406000208c8a511679ffffffffffff000000000000000000000000000000000000000060208c015160a01b16907fffffffffffff000000000000000000000000000000000000000000000000000060408d015160d01b16911717815501970151167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000875416178655511684907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b51825477ffffffffffffffffffffffffffffffffffffffffffffffff16911660c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016179055565b1693511691167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f600080a4565b505091612430565b94939192817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81011161126e5767ffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff9695879695846123c28998612683610cfe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6125c69a0183610e7b565b906121f8565b60005260046020526040600020805490817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81011161126e576127ab906126f5610cfe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff850183610e7b565b9061272a73ffffffffffffffffffffffffffffffffffffffff8b51166101a8612720888c01836118bc565b91888c0190611522565b9385890165ffffffffffff60408d01511611612813575b6123bc8973ffffffffffffffffffffffffffffffffffffffff8d51168d6bffffffffffffffffffffffff60608201511667ffffffffffffffff60a083015116908b65ffffffffffff60406bffffffffffffffffffffffff6080870151169501511695019389612b57565b7fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724604073ffffffffffffffffffffffffffffffffffffffff895116926128026127f6868a01866118bc565b6101a8878b0187611522565b82519182526020820152a238612174565b73ffffffffffffffffffffffffffffffffffffffff8b51166000526005602052604060002065ffffffffffff60408d0151166000526020526129a560406000206bffffffffffffffffffffffff8d818060806040519561287287610f2d565b54938285168752828560601c1660208801528460c01c604088015282806060830151168187160316875267ffffffffffffffff8060a0830151168660c01c031660408801520151169160601c160316602082015273ffffffffffffffffffffffffffffffffffffffff8d511660005260056020528c65ffffffffffff6040806000209201511660005260205267ffffffffffffffff604080600020926bffffffffffffffffffffffff8151167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008554161784556123566bffffffffffffffffffffffff60208301511685907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b612741565b91929093506020823d6020116129de575b816129c860209383610f65565b81010312610e1857905192909190612154612119565b3d91506129bb565b6040513d6000823e3d90fd5b9250505060a0813d60a011612a41575b81612a0f60a09383610f65565b81010312610e18576120cf91612a248261186c565b60206080612a3382860161186c565b94015191939192945061207b565b3d9150612a02565b90506020813d602011612a73575b81612a6460209383610f65565b81010312610e18575138611fda565b3d9150612a57565b60046040517f14271110000000000000000000000000000000000000000000000000000000008152fd5b612abe915060403d604011611a5857611a498183610f65565b38611f3d565b60046040517f3252b88f000000000000000000000000000000000000000000000000000000008152fd5b60008112612af95790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f53616665436173743a2076616c7565206d75737420626520706f7369746976656044820152fd5b9196959492612b646111ef565b976fffffffffffffffffffffffffffffffff60608501511615612cbc57886fffffffffffffffffffffffffffffffff606086015116968760608301526fffffffffffffffffffffffffffffffff865116928383526fffffffffffffffffffffffffffffffff6040880151168060408501526fffffffffffffffffffffffffffffffff60208901511694602085019b868d526fffffffffffffffffffffffffffffffff60608b01511610612c81575b50505050505050828403612c28575b5050505050565b6fffffffffffffffffffffffffffffffff838180612c4d819896829883988498612e6b565b9790941660608d015216818b5116031689521681604089015116031660408701521681835116031690523880808080612c21565b6fffffffffffffffffffffffffffffffff92831690038216909352938416909103831660408b01528216900316855238808881808080612c12565b9697509150506fffffffffffffffffffffffffffffffff8094508092938160405197612ce789610ec6565b16875216602086015216604084015216606082015290565b90959495929192612d0e6111ef565b9660608301946fffffffffffffffffffffffffffffffff958681511615612dd45784939291878b968180809551169960608a01998b8b528280855116808352818060208160408a0151169889604088015201511698160116905216011660408c01528160208c01991601168752808503612d8c575b50505050505050565b858080612d9f8198968583988498612e6b565b989192909516905216818b51160316895216816040890151160316604087015216818351160316905238808080808080612d83565b509697509394858094935080925060405197612ce789610ec6565b90928315159081612e42575b5015612e38577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830192831161126e576114c39261151c91610e7b565b6114c392506114f4565b606091506fffffffffffffffffffffffffffffffff918291015116906060840151161438612dfb565b9091600092600092838093630784ce0084018111600014612eb457505050505050506fffffffffffffffffffffffffffffffff9182825116926020816040850151169301511690565b9193959794965073ffffffffffffffffffffffffffffffffffffffff62093a808080950402840194168152602090600582526040908181205b84871115612efe5750505050505050565b90919293948698999a8197845282865284808520815190612f1e82610f2d565b54926bffffffffffffffffffffffff91828516928382528560601c1694858b83015260c01c9384910152019c019a0198019493929190959495612eed56fea164736f6c6343000813000a000000000000000000000000c8418af6358ffdda74e09ca9cc3fe03ca6adc5b0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000155665467873566f74696e6744656c65676174696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000013100000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060408181526004918236101561001657600080fd5b600092833560e01c918263101f46d514610d96575081631c94541214610d0857816334940fa814610c7c5781633a46b1a814610bfa5781633b3eb8a614610b6c5781634bf5d7e914610aed5781634f1bfc9e14610ab0578163506d1f8114610a41578163587cde1e146109dc5781635c19a95c1461099b5781636e22c4af146108ed57816375f9e3961461084157816384b0196e146106d05781638e539e8c146105aa57816391ddadf414610569578163944c39301461052f5781639ab24eb0146104db578163b06128211461042b578163c3cda5201461020d57508063e7a324dc146101b5578063eb9019d4146101555763f4359ce51461011757600080fd5b3461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020905162093a808152f35b5080fd5b503461015157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020906101ae610192610df5565b6101a86024356101a281846118bc565b92611522565b9061129d565b9051908152f35b503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015157602090517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf8152f35b8383346101515760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015157610246610df5565b9060243560443560643560ff81168103610427578142116103ff57835160208101907fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf825273ffffffffffffffffffffffffffffffffffffffff938488168783015285606083015260808201526080815260a0810181811067ffffffffffffffff8211176103d357865251902061032f94939291610327916102e6611c9f565b908651917f190100000000000000000000000000000000000000000000000000000000000083526002830152602282015260a4359160426084359220611c03565b949094611a6a565b831685526002602052818520908154917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83146103a7576001830190550361037f57509061037c91611dc6565b80f35b8490517fc4689a54000000000000000000000000000000000000000000000000000000008152fd5b60248760118a7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b60248960418c7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8684517f0819bdcd000000000000000000000000000000000000000000000000000000008152fd5b8580fd5b8383346101515760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015157610464610df5565b9061046e826112b7565b916fffffffffffffffffffffffffffffffff606084015116156104b3579373ffffffffffffffffffffffffffffffffffffffff61037c949516855260205283206114f4565b8482517fc1ccb3d1000000000000000000000000000000000000000000000000000000008152fd5b5050346101515760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020906101ae61051a610df5565b6101a861052742836118bc565b914290611522565b50503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020905160038152f35b50503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020905165ffffffffffff42168152f35b83833461015157602092837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106cc578035438110156106a4578251917f981b24d0000000000000000000000000000000000000000000000000000000008352820152838160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c8418af6358ffdda74e09ca9cc3fe03ca6adc5b0165afa928315610699578093610664575b505051908152f35b909192508382813d8311610692575b61067d8183610f65565b8101031261068f57505190838061065c565b80fd5b503d610673565b8251903d90823e3d90fd5b5090517f089b2f6f000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b919050346106cc57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106cc5761072a7f5665467873566f74696e6744656c65676174696f6e0000000000000000000015610fa6565b926107547f310000000000000000000000000000000000000000000000000000000000000161111c565b908251926020928385019585871067ffffffffffffffff881117610815575092806107cb8388966107be998b9996528686528151998a997f0f000000000000000000000000000000000000000000000000000000000000008b5260e0868c015260e08b0190610e1d565b91898303908a0152610e1d565b924660608801523060808801528460a088015286840360c088015251928381520193925b8281106107fe57505050500390f35b8351855286955093810193928101926001016107ef565b8360416024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b82843461068f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261068f57610879610df5565b9273ffffffffffffffffffffffffffffffffffffffff6024359416825260205281812090815484101561068f57506080926108b391610e7b565b509060018254920154908051926fffffffffffffffffffffffffffffffff908181168552851c6020850152821690830152821c6060820152f35b5050346101515760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015157908160c09273ffffffffffffffffffffffffffffffffffffffff9283610942610df5565b1681526003602052209060018254920154918151938116845265ffffffffffff8160a01c16602085015260d01c908301526bffffffffffffffffffffffff80821660608401528160601c166080830152821c60a0820152f35b833461068f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261068f5761037c6109d6610df5565b33611dc6565b5050346101515760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020918173ffffffffffffffffffffffffffffffffffffffff9182610a30610df5565b168152600385522054169051908152f35b50503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c8418af6358ffdda74e09ca9cc3fe03ca6adc5b0168152f35b50503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101515760209051630784ce008152f35b50503461015157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151578051610b6891610b2c82610f49565b600e82527f6d6f64653d74696d657374616d70000000000000000000000000000000000000602083015251918291602083526020830190610e1d565b0390f35b5050346101515760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015157610b6890610bb1610bac610df5565b6112b7565b90519182918291909160606080820193816fffffffffffffffffffffffffffffffff91828151168552826020820151166020860152826040820151166040860152015116910152565b82843461068f57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261068f5750610c33610df5565b916024359042821015610c555750826101a8826101a26101ae946020976118bc565b82517f47860b97000000000000000000000000000000000000000000000000000000008152fd5b9050346106cc57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106cc57610cb4610df5565b906024359163ffffffff83168303610d04579183610b6895610cfe9373ffffffffffffffffffffffffffffffffffffffff610bb196610cf16111ef565b5016825260205220610e7b565b50611214565b8480fd5b50503461015157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610151578060609273ffffffffffffffffffffffffffffffffffffffff610d59610df5565b168152600560205281812060243582526020522054908051916bffffffffffffffffffffffff808216845281851c16602084015260c01c90820152f35b849084346106cc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126106cc5760209273ffffffffffffffffffffffffffffffffffffffff610de8610df5565b1681526002845220548152f35b6004359073ffffffffffffffffffffffffffffffffffffffff82168203610e1857565b600080fd5b919082519283825260005b848110610e675750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b602081830181015184830182015201610e28565b8054821015610e975760005260206000209060011b0190600090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6080810190811067ffffffffffffffff821117610ee257604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60c0810190811067ffffffffffffffff821117610ee257604052565b6060810190811067ffffffffffffffff821117610ee257604052565b6040810190811067ffffffffffffffff821117610ee257604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610ee257604052565b60ff8114610ffc5760ff811690601f8211610fd25760405191610fc883610f49565b8252602082015290565b60046040517fb3512b0c000000000000000000000000000000000000000000000000000000008152fd5b506040516000805490600182811c90808416938415611112575b60209485841081146110e557838752869493929181156110a6575060011461104a575b505061104792500382610f65565b90565b60008080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56395935091905b81831061108e57505061104793508201013880611039565b85548784018501529485019486945091830191611076565b90506110479593507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b8201013880611039565b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526022600452fd5b91607f1691611016565b60ff811461113e5760ff811690601f8211610fd25760405191610fc883610f49565b50604051600060019081549182811c908084169384156111e5575b60209485841081146110e557838752869493929181156110a6575060011461118957505061104792500382610f65565b60008181527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf695935091905b8183106111cd57505061104793508201013880611039565b855487840185015294850194869450918301916111b5565b91607f1691611159565b604051906111fc82610ec6565b60006060838281528260208201528260408201520152565b9060405161122181610ec6565b6060819360018154916fffffffffffffffffffffffffffffffff92838116865260801c60208601520154908116604084015260801c910152565b8181029291811591840414171561126e57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9190820180921161126e57565b9190820391821161126e57565b6112bf6111ef565b90600073ffffffffffffffffffffffffffffffffffffffff82168152600460205260408120906201518091824204928084029380850482149015171561142b5783018093116113fe5780549182156113c9577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83019283116113d1575061134991610cfe91610e7b565b6fffffffffffffffffffffffffffffffff92836060830151168381146113c95783839161137593612e6b565b91909582156113bf5750908460208180809581885116031698816040880151160316940151160316604051946113aa86610ec6565b85526020850152604084015216606082015290565b9550505050505090565b505050505090565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526011600452fd5b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b906114c557815160208301516fffffffffffffffffffffffffffffffff9182167fffffffffffffffffffffffffffffffff00000000000000000000000000000000608092831b8116919091178455604085015160609095015190911b16921691909117600190910155565b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b9081549168010000000000000000831015610ee2578261151c9160016114c395018155610e7b565b90611458565b91909160009073ffffffffffffffffffffffffffffffffffffffff8116825260049384602052604083206115546111ef565b81546201518092838504938085029480860482149015171561179e57821580159190611865577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8401848111611839576115b060019185610e7b565b50015460801c955b6fffffffffffffffffffffffffffffffff968716908082111561182f576115de916112aa565b04905b806117fd575b156117f6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83018381116117ca5790611620916112aa565b82811061175b5750816116f0575050925b816060850151169081156116e65761167661168593602061165b8888611669999761167f97612e6b565b9491848b9a929a51166112aa565b978360408b0151166112aa565b970151166112aa565b9361125b565b808211156116de57611696916112aa565b915b826003029260038404036116b2575061104792935061129d565b806011867f4e487b71000000000000000000000000000000000000000000000000000000006024945252fd5b505081611698565b5093955050505050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201925090821161172f5761172991610cfe91610e7b565b92611631565b60248660118a7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b9160019061176f818518831c82861661129d565b91878161177c8587610e7b565b50015460801c111561179057505091611620565b909350810180911115611620575b60248860118c7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b60248960118d7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5086611620565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83018381116117ca5781106115e7565b50505087906115e1565b60248a60118e7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b88956115b8565b519081600f0b8203610e1857565b90816040910312610e185760405190604082019082821067ffffffffffffffff831117610ee2576020916040526118b08161186c565b83520151602082015290565b9073ffffffffffffffffffffffffffffffffffffffff90817f000000000000000000000000c8418af6358ffdda74e09ca9cc3fe03ca6adc5b016906040918251937fcbf9fe5f00000000000000000000000000000000000000000000000000000000855285168060048601528385602481855afa948515611a5f57600095611a30575b50826020809601511115611a25576000526003845265ffffffffffff836000205460a01c168015908115611a1b575b5061197e575b5050505050600090565b91517efdd58e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff94909416600485015260248401528190839060449082905afa9182916000936119ea575b50506110475780808080611974565b8181949293943d8311611a14575b611a028183610f65565b8101031261068f5750519038806119db565b503d6119f8565b905082103861196e565b505050505050600090565b611a51919550843d8611611a58575b611a498183610f65565b81019061187a565b933861193f565b503d611a3f565b84513d6000823e3d90fd5b6005811015611bd45780611a7b5750565b60018103611ae15760646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152fd5b60028103611b475760646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152fd5b600314611b5057565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9291907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311611c935791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa15611c8657815173ffffffffffffffffffffffffffffffffffffffff811615611c80579190565b50600190565b50604051903d90823e3d90fd5b50505050600090600390565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006b83c4f3a6729fb7d5e19b720092162df439f56716301480611d9d575b15611d07577f7fe24e004d050b0dec799c65dff98542560cc11245f76821cd43f8c89886cab990565b60405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527fa07003d5f6686ee8309a11d6a6d07ebad10ea48ba10f95597658cd093105acf560408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a08152611d9781610f11565b51902090565b507f00000000000000000000000000000000000000000000000000000000000000014614611cde565b73ffffffffffffffffffffffffffffffffffffffff821615612ac45773ffffffffffffffffffffffffffffffffffffffff81166000526003602052604060002091600160405193611e1685610f11565b805473ffffffffffffffffffffffffffffffffffffffff8116865265ffffffffffff8160a01c16602087015260d01c604086015201546bffffffffffffffffffffffff811660608501526bffffffffffffffffffffffff8160601c16608085015260c01c60a08401526201518090814204918083029280840482149015171561126e57808301831161126e57611eaa6111ef565b906040517fcbf9fe5f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8616600482015260408160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c8418af6358ffdda74e09ca9cc3fe03ca6adc5b0165afa80156129e657602091600091612aa5575b500151818501811115612a7b576040517f010ae75700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c8418af6358ffdda74e09ca9cc3fe03ca6adc5b0165afa9081156129e657600091612a49575b506040517f28d09d4700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff881660048201526024810182905260a0818060448101038173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c8418af6358ffdda74e09ca9cc3fe03ca6adc5b0165afa80156129e6576000916000906000926129f2575b506040517fda020a1800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b166004820152602481019490945260208480604481015b038173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c8418af6358ffdda74e09ca9cc3fe03ca6adc5b0165afa9384156129e6576000946129aa575b509161215494612149926121326101a895600f0b612aee565b60208a015260408901526060880152600f0b612aee565b91602086015161125b565b825273ffffffffffffffffffffffffffffffffffffffff86511680612689575b5073ffffffffffffffffffffffffffffffffffffffff83166000526004602052604060002080549081156000146125fb579267ffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff968796946125c6946123c289986040516121dd81610ec6565b60008152600060208201526000604082015260006060820152905b6122136122078688018c6118bc565b6101a88789018d611522565b938b8b166000526005602052604060002060608a01516000526020526123a060406000206bffffffffffffffffffffffff8b8180604080519561225587610f2d565b54938285168752828560601c1660208801528460c01c828801528280825116818716011687528f806020830151168660c01c0116828801520151169160601c16011660208201528d8d166000526005602052604060002060608c015160005260205289604080600020926bffffffffffffffffffffffff8151167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008554161784556123566bffffffffffffffffffffffff60208301511685907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b0151825477ffffffffffffffffffffffffffffffffffffffffffffffff16911660c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016179055565b6123bc898c815160208301519060408b8d019401519288612cff565b91612def565b6123dd6123d1838501896118bc565b6101a88486018a611522565b60405191825260208201527fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724604089891692a260208b015165ffffffffffff1691826125f35765ffffffffffff92500116915b61257d6bffffffffffffffffffffffff65ffffffffffff606084015116928181511685602084604085015116930151169465ffffffffffff604051986124758a610f11565b8c8c168a52166020890152604088015260608701526080860190815260a08601938452888c166000526003602052816060600160406000208c8a511679ffffffffffff000000000000000000000000000000000000000060208c015160a01b16907fffffffffffff000000000000000000000000000000000000000000000000000060408d015160d01b16911717815501970151167fffffffffffffffffffffffffffffffffffffffff000000000000000000000000875416178655511684907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b51825477ffffffffffffffffffffffffffffffffffffffffffffffff16911660c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016179055565b1693511691167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f600080a4565b505091612430565b94939192817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81011161126e5767ffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff9695879695846123c28998612683610cfe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6125c69a0183610e7b565b906121f8565b60005260046020526040600020805490817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81011161126e576127ab906126f5610cfe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff850183610e7b565b9061272a73ffffffffffffffffffffffffffffffffffffffff8b51166101a8612720888c01836118bc565b91888c0190611522565b9385890165ffffffffffff60408d01511611612813575b6123bc8973ffffffffffffffffffffffffffffffffffffffff8d51168d6bffffffffffffffffffffffff60608201511667ffffffffffffffff60a083015116908b65ffffffffffff60406bffffffffffffffffffffffff6080870151169501511695019389612b57565b7fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724604073ffffffffffffffffffffffffffffffffffffffff895116926128026127f6868a01866118bc565b6101a8878b0187611522565b82519182526020820152a238612174565b73ffffffffffffffffffffffffffffffffffffffff8b51166000526005602052604060002065ffffffffffff60408d0151166000526020526129a560406000206bffffffffffffffffffffffff8d818060806040519561287287610f2d565b54938285168752828560601c1660208801528460c01c604088015282806060830151168187160316875267ffffffffffffffff8060a0830151168660c01c031660408801520151169160601c160316602082015273ffffffffffffffffffffffffffffffffffffffff8d511660005260056020528c65ffffffffffff6040806000209201511660005260205267ffffffffffffffff604080600020926bffffffffffffffffffffffff8151167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008554161784556123566bffffffffffffffffffffffff60208301511685907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b612741565b91929093506020823d6020116129de575b816129c860209383610f65565b81010312610e1857905192909190612154612119565b3d91506129bb565b6040513d6000823e3d90fd5b9250505060a0813d60a011612a41575b81612a0f60a09383610f65565b81010312610e18576120cf91612a248261186c565b60206080612a3382860161186c565b94015191939192945061207b565b3d9150612a02565b90506020813d602011612a73575b81612a6460209383610f65565b81010312610e18575138611fda565b3d9150612a57565b60046040517f14271110000000000000000000000000000000000000000000000000000000008152fd5b612abe915060403d604011611a5857611a498183610f65565b38611f3d565b60046040517f3252b88f000000000000000000000000000000000000000000000000000000008152fd5b60008112612af95790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f53616665436173743a2076616c7565206d75737420626520706f7369746976656044820152fd5b9196959492612b646111ef565b976fffffffffffffffffffffffffffffffff60608501511615612cbc57886fffffffffffffffffffffffffffffffff606086015116968760608301526fffffffffffffffffffffffffffffffff865116928383526fffffffffffffffffffffffffffffffff6040880151168060408501526fffffffffffffffffffffffffffffffff60208901511694602085019b868d526fffffffffffffffffffffffffffffffff60608b01511610612c81575b50505050505050828403612c28575b5050505050565b6fffffffffffffffffffffffffffffffff838180612c4d819896829883988498612e6b565b9790941660608d015216818b5116031689521681604089015116031660408701521681835116031690523880808080612c21565b6fffffffffffffffffffffffffffffffff92831690038216909352938416909103831660408b01528216900316855238808881808080612c12565b9697509150506fffffffffffffffffffffffffffffffff8094508092938160405197612ce789610ec6565b16875216602086015216604084015216606082015290565b90959495929192612d0e6111ef565b9660608301946fffffffffffffffffffffffffffffffff958681511615612dd45784939291878b968180809551169960608a01998b8b528280855116808352818060208160408a0151169889604088015201511698160116905216011660408c01528160208c01991601168752808503612d8c575b50505050505050565b858080612d9f8198968583988498612e6b565b989192909516905216818b51160316895216816040890151160316604087015216818351160316905238808080808080612d83565b509697509394858094935080925060405197612ce789610ec6565b90928315159081612e42575b5015612e38577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830192831161126e576114c39261151c91610e7b565b6114c392506114f4565b606091506fffffffffffffffffffffffffffffffff918291015116906060840151161438612dfb565b9091600092600092838093630784ce0084018111600014612eb457505050505050506fffffffffffffffffffffffffffffffff9182825116926020816040850151169301511690565b9193959794965073ffffffffffffffffffffffffffffffffffffffff62093a808080950402840194168152602090600582526040908181205b84871115612efe5750505050505050565b90919293948698999a8197845282865284808520815190612f1e82610f2d565b54926bffffffffffffffffffffffff91828516928382528560601c1694858b83015260c01c9384910152019c019a0198019493929190959495612eed56fea164736f6c6343000813000a

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

000000000000000000000000c8418af6358ffdda74e09ca9cc3fe03ca6adc5b0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000155665467873566f74696e6744656c65676174696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000013100000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : veFxs (address): 0xc8418aF6358FFddA74e09Ca9CC3Fe03Ca6aDC5b0
Arg [1] : name (string): VeFxsVotingDelegation
Arg [2] : version (string): 1

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 000000000000000000000000c8418af6358ffdda74e09ca9cc3fe03ca6adc5b0
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000015
Arg [4] : 5665467873566f74696e6744656c65676174696f6e0000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [6] : 3100000000000000000000000000000000000000000000000000000000000000


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.