ETH Price: $1,813.85 (+10.36%)
Gas: 0.17 GWei

Contract

0xa615388bD2f920eE2FeC7606F26908B454F0C249

Overview

ETH Balance

Linea Mainnet LogoLinea Mainnet LogoLinea Mainnet Logo0 ETH

ETH Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions found.

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
128712132024-12-04 1:15:21140 days ago1733274921
0xa615388b...454F0C249
0 ETH
128711842024-12-04 1:14:19140 days ago1733274859
0xa615388b...454F0C249
0 ETH
128711842024-12-04 1:14:19140 days ago1733274859
0xa615388b...454F0C249
0 ETH
128711842024-12-04 1:14:19140 days ago1733274859
0xa615388b...454F0C249
0 ETH
128709662024-12-04 1:06:23140 days ago1733274383
0xa615388b...454F0C249
0 ETH
128709092024-12-04 1:04:06140 days ago1733274246
0xa615388b...454F0C249
0 ETH
128709072024-12-04 1:04:02140 days ago1733274242
0xa615388b...454F0C249
0 ETH
128702922024-12-04 0:41:09140 days ago1733272869
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
128699452024-12-04 0:27:35140 days ago1733272055
0xa615388b...454F0C249
0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EscrowDelegateCheckpoints

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
File 1 of 5 : EscrowDelegateCheckpoints.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import {SafeCastLibrary} from "../libraries/SafeCastLibrary.sol";
import {Checkpoints} from "../libraries/Checkpoints.sol";
import {Time} from "../libraries/Time.sol";

/**
 * @title CheckPointSystem
 * @dev This contract is used to manage checkpoints in the system.
 */
library EscrowDelegateCheckpoints {
    using Checkpoints for Checkpoints.Trace;
    using Checkpoints for Checkpoints.TraceAddress;
    using SafeCastLibrary for int128;
    using SafeCastLibrary for uint256;

    /// @notice Maximum time for a checkpoint
    int128 public constant MAX_TIME = 2 * 365 * 86400;
    /// @notice Unit of time for the clock
    uint48 public constant CLOCK_UNIT = 7 days;

    struct EscrowDelegateStore {
        /// @notice Global checkpoints
        Checkpoints.Trace _globalCheckpoints;
        /// @notice Mapping of global slope changes
        /// @dev Intended to be exposed with a getter
        mapping(uint256 => int128) globalSlopeChanges;
        /// @notice escrow lock checkpoints
        mapping(uint256 => Checkpoints.Trace) _escrowCheckpoints;
        /// @notice Delegate checkpoints
        mapping(address => Checkpoints.Trace) _delegateCheckpoints;
        /// @notice escrow lock to delegatee mapping
        mapping(uint256 => Checkpoints.TraceAddress) _escrowDelegateeAddress;
        /// @notice Delegatee slope changes
        /// @dev Intended to be exposed with a getter
        mapping(address => mapping(uint256 => int128)) delegateeSlopeChanges;
    }

    event CheckpointGlobal(uint48 timestamp, int128 slope, int128 bias, int128 permanent);
    event CheckpointDelegate(address delegatee, uint48 timestamp, int128 slope, int128 bias, int128 permanent);
    event CheckpointEscrow(uint256 escrowId, uint48 timestamp, int128 slope, int128 bias, int128 permanent);

    /**
     * @notice Clock used for flagging checkpoints.
     * @return Current timestamp
     */
    function clock() public view returns (uint48) {
        return Time.timestamp();
    }

    /**
     * @notice Clock used for flagging global checkpoints.
     * @return Current timestamp rounded to the nearest clock unit
     */
    function globalClock() public view returns (uint48) {
        return toGlobalClock(Time.timestamp());
    }

    /**
     * @notice Converts a timestamp to a global clock value.
     * @param timestamp The timestamp to convert
     * @return The converted global clock value
     */
    function toGlobalClock(uint256 timestamp) internal pure returns (uint48) {
        return uint48((timestamp / CLOCK_UNIT) * CLOCK_UNIT);
    }

    /**
     * @dev Record global and per-escrow data to checkpoints. Used by VotingEscrow system.
     * @param store_ The EscrowDelegateStore struct containing all the storage mappings.
     * @param escrowId NFT escrow lock ID. No escrow checkpoint if 0
     * @param uOldAmount Locked amount from last checkpoint
     * @param uNewAmount Locked amount from current checkpoint
     * @param uOldEndTime Last checkpoint time
     * @param uNewEndTime Current checkpoint time
     */
    function checkpoint(
        EscrowDelegateStore storage store_,
        uint256 escrowId,
        int128 uOldAmount,
        int128 uNewAmount,
        uint256 uOldEndTime,
        uint256 uNewEndTime
    ) external {
        int128 oldDslope = 0;
        int128 newDslope = 0;
        Checkpoints.Point memory uOldPoint = Checkpoints.blankPoint();
        Checkpoints.Point memory uNewPoint = Checkpoints.blankPoint();
        /// @notice if this is not rounded to CLOCK_UNIT
        /// the system will not be able to go too long without checkpoints
        uNewEndTime = toGlobalClock(uNewEndTime);
        if (escrowId != 0) {
            // Calculate slopes and biases
            // Kept at zero when they have to
            uNewPoint.permanent = uNewEndTime == 0 ? uNewAmount : int128(0);
            uOldPoint.permanent = uOldEndTime == 0 ? uOldAmount : int128(0);
            if (uOldEndTime > block.timestamp && uOldAmount > 0) {
                /// @dev  Calculate the slope based on the older checkpoint amount
                uOldPoint.slope = (uOldAmount) / MAX_TIME;
                uOldPoint.bias = (uOldPoint.slope * (uOldEndTime - block.timestamp).toInt128());
            }
            if (uNewEndTime > block.timestamp && uNewAmount > 0) {
                uNewPoint.slope = (uNewAmount) / MAX_TIME;
                uNewPoint.bias = (uNewPoint.slope * (uNewEndTime - block.timestamp).toInt128());
            }
            oldDslope = store_.globalSlopeChanges[uOldEndTime];
            if (uNewEndTime != 0) {
                if (uNewEndTime == uOldEndTime) {
                    newDslope = oldDslope;
                } else {
                    newDslope = store_.globalSlopeChanges[uNewEndTime];
                }
            }

            // Schedule the slope changes (slope is going down)
            // We subtract new escrow slope from [_newLocked.endTime]
            // and add old_escrow_slope to [_oldLocked.end]
            if (uOldEndTime > block.timestamp) {
                // oldDslope was <something> - uOld.slope, so we cancel that
                oldDslope += uOldPoint.slope;
                if (uOldEndTime == uNewEndTime) {
                    oldDslope -= uNewPoint.slope; // It was a new deposit, not extension
                }
                store_.globalSlopeChanges[uOldEndTime] = oldDslope;
            }

            if (uNewEndTime > block.timestamp) {
                // update slope if new lock is greater than old lock and is not permanent or if old lock is permanent
                if ((uNewEndTime > uOldEndTime)) {
                    newDslope -= uNewPoint.slope; // old slope disappeared at this point
                    store_.globalSlopeChanges[uNewEndTime] = newDslope;
                }
                // else: we recorded it already in oldDslope
            }
            /// @dev Add the new point to the escrowId Checkpoints.Trace
            _pushPointAtClock(store_._escrowCheckpoints[escrowId], uNewPoint);
            emit CheckpointEscrow(escrowId, clock(), uNewPoint.slope, uNewPoint.bias, uNewPoint.permanent);

            (, uint48 delegateTs, address delegateeAddress) = store_
                ._escrowDelegateeAddress[escrowId]
                .latestCheckpoint();

            if (delegateTs != 0) {
                /// @notice this can likely be handled more efficiently
                _checkpointDelegatee(store_, delegateeAddress, uOldPoint, uOldEndTime, false);
                _checkpointDelegatee(store_, delegateeAddress, uNewPoint, uNewEndTime, true);
            }
        }
        /// @dev If escrowId is 0,  this  will still create a global checkpoint
        globalCheckpoint(store_, escrowId, uOldPoint, uNewPoint);
    }

    /**
     * @dev Function to update global checkpoint
     */
    function globalCheckpoint(EscrowDelegateStore storage store_) external {
        globalCheckpoint(store_, 0, Checkpoints.blankPoint(), Checkpoints.blankPoint());
    }

    /**
     * @dev Function to update global checkpoint with new points
     * @param escrowId The ID of the escrow lock
     * - If
     * @param uOldPoint The old point to be updated
     * @param uNewPoint The new point to be updated
     */
    function globalCheckpoint(
        EscrowDelegateStore storage store_,
        uint256 escrowId,
        Checkpoints.Point memory uOldPoint,
        Checkpoints.Point memory uNewPoint
    ) public {
        (, uint48 lastPoint, Checkpoints.Point memory lastGlobal) = store_._globalCheckpoints.latestCheckpoint();
        uint48 lastCheckpoint = lastPoint != 0 ? lastPoint : uint48(block.timestamp);

        {
            // Go over weeks to fill history and calculate what the current point is
            uint48 testTime = toGlobalClock(lastCheckpoint); /// @dev  lastCheckpoint > tesTime
            uint256 maxTime = testTime + MAX_TIME.toUint256();

            while (testTime < block.timestamp) {
                testTime += CLOCK_UNIT;
                int128 dSlope = 0;
                if (testTime > block.timestamp) {
                    testTime = block.timestamp.toUint48();
                } else {
                    dSlope = store_.globalSlopeChanges[testTime];
                }
                if (dSlope != 0) {
                    lastGlobal.bias -= lastGlobal.slope * uint256(testTime - lastCheckpoint).toInt128();
                    lastGlobal.slope += dSlope;
                    if (lastGlobal.bias < 0) {
                        lastGlobal.bias = 0;
                    }
                    if (lastGlobal.slope < 0) {
                        lastGlobal.slope = 0;
                    }

                    lastCheckpoint = testTime;
                    store_._globalCheckpoints.push(lastCheckpoint, lastGlobal);
                }
                if (testTime > maxTime) break;
            }
        }

        if (escrowId != 0) {
            lastGlobal.bias = lastGlobal.bias - ((lastGlobal.slope * (block.timestamp - lastCheckpoint).toInt128()));

            lastGlobal.slope += uNewPoint.slope - uOldPoint.slope;
            lastGlobal.bias += uNewPoint.bias - uOldPoint.bias;
            lastGlobal.permanent += uNewPoint.permanent - uOldPoint.permanent;
        } else {
            // Initial value of testTime is always larger than the ts of the last point
            uint256 testTime = block.timestamp;
            lastGlobal.bias -= (lastGlobal.slope * (testTime - lastCheckpoint).toInt128());
        }

        _pushPointAtClock(store_._globalCheckpoints, lastGlobal);
        emit CheckpointGlobal(clock(), lastGlobal.slope, lastGlobal.bias, lastGlobal.permanent);
    }

    /**
     * @dev Function to calculate total voting power at some point in the past
     * @param _delegateeAddress The address of the delegatee
     * @param timestamp Time to calculate the total voting power at
     * @return Total voting power at that time
     */
    function getAdjustedVotes(
        EscrowDelegateStore storage store_,
        address _delegateeAddress,
        uint48 timestamp
    ) external view returns (uint256) {
        Checkpoints.Point memory lastPoint = _getAdjustedVotesCheckpoint(store_, _delegateeAddress, timestamp);
        return (lastPoint.bias + lastPoint.permanent).toUint256();
    }

    /**
     * @dev Function to get delegated votes checkpoint at some point in the past
     * @param _delegateeAddress The address of the delegatee
     * @param timestamp Time to calculate the total voting power at
     * @return Total voting power at that time
     */
    function _getAdjustedVotesCheckpoint(
        EscrowDelegateStore storage store_,
        address _delegateeAddress,
        uint48 timestamp
    ) internal view returns (Checkpoints.Point memory) {
        (bool exists, uint48 lastCheckpointTs, Checkpoints.Point memory lastPoint) = store_
            ._delegateCheckpoints[_delegateeAddress]
            .upperLookupRecent(timestamp);
        if (!exists) return lastPoint;
        uint48 testTime = toGlobalClock(lastCheckpointTs); /// @dev  lastCheckpointTs > tesTime
        uint256 maxTime = testTime + MAX_TIME.toUint256();
        while (testTime < timestamp) {
            testTime += CLOCK_UNIT;
            int128 dSlope = 0;
            if (testTime > timestamp) {
                testTime = timestamp;
            } else {
                dSlope = store_.delegateeSlopeChanges[_delegateeAddress][testTime];
            }
            if (dSlope != 0) {
                lastPoint.bias -= lastPoint.slope * uint256(testTime - lastCheckpointTs).toInt128();
                lastPoint.slope += dSlope;
                if (lastPoint.bias < 0) {
                    lastPoint.bias = 0;
                }
                if (lastPoint.slope < 0) {
                    lastPoint.slope = 0;
                }
                lastCheckpointTs = uint48(testTime);
            }
            if (testTime > maxTime) break;
        }
        int128 change = lastPoint.slope * uint256(timestamp - lastCheckpointTs).toInt128();
        lastPoint.bias = lastPoint.bias < change ? int128(0) : lastPoint.bias - change;

        return lastPoint;
    }

    /**
     * @notice Public function to get the delegatee of an escrow lock
     * @param escrowId The ID of the escrow
     * @return The address of the delegate
     */
    function getEscrowDelegatee(EscrowDelegateStore storage store_, uint256 escrowId) external view returns (address) {
        return getEscrowDelegateeAtTime(store_, escrowId, block.timestamp.toUint48());
    }

    /**
     * @notice Public function to get the delegatee of an escrow lock
     * @param escrowId The ID of the escrow lock
     * @param timestamp The timestamp to get the delegate at
     * @return The address of the delegate
     */
    function getEscrowDelegateeAtTime(
        EscrowDelegateStore storage store_,
        uint256 escrowId,
        uint48 timestamp
    ) public view returns (address) {
        return store_._escrowDelegateeAddress[escrowId].upperLookupRecent(timestamp);
    }

    /**
     * @dev Function to record escrow delegation checkpoints. Used by voting system.
     * @param escrowId The ID of the escrow lock
     * @param delegatee The address of the delegatee
     * @param endTime The end time of the delegation
     */
    function delegate(
        EscrowDelegateStore storage store_,
        uint256 escrowId,
        address delegatee,
        uint256 endTime
    ) external returns (address oldDelegatee, address newDelegatee) {
        oldDelegatee = store_._escrowDelegateeAddress[escrowId].latest();
        if (oldDelegatee == delegatee) return (oldDelegatee, delegatee);

        (, uint48 ts, Checkpoints.Point memory lastPoint) = store_._escrowCheckpoints[escrowId].latestCheckpoint();
        lastPoint.bias -= ((lastPoint.slope * (block.timestamp - ts).toInt128()));
        if (lastPoint.bias < 0) {
            lastPoint.bias = 0;
        }

        if (oldDelegatee != delegatee && oldDelegatee != address(0)) {
            _checkpointDelegatee(store_, oldDelegatee, lastPoint, endTime, false);
        }
        // Delegate to new delegator
        _checkpointDelegatee(store_, delegatee, lastPoint, endTime, true);
        _pushAddressAtClock(store_._escrowDelegateeAddress[escrowId], delegatee);
        return (oldDelegatee, delegatee);
    }

    /**
     * @dev Function to update delegatee's `delegatedBalance` by `balance`.
     *      Only updates if delegating to a new delegatee.
     * @param delegateeAddress The address of the delegatee
     * @param escrowPoint The point of the escrow
     * @param endTime The end time of the delegation
     * @param increase Whether to increase or decrease the balance
     */
    function _checkpointDelegatee(
        EscrowDelegateStore storage store_,
        address delegateeAddress,
        Checkpoints.Point memory escrowPoint,
        uint256 endTime,
        bool increase
    ) internal {
        (Checkpoints.Point memory lastPoint, uint48 lastCheckpoint) = baseCheckpointDelegatee(store_, delegateeAddress);

        int128 baseBias = lastPoint.bias - (lastPoint.slope * (block.timestamp - lastCheckpoint).toInt128());

        if (!increase) {
            if (endTime > block.timestamp) {
                store_.delegateeSlopeChanges[delegateeAddress][endTime] += escrowPoint.slope;
                lastPoint.slope = escrowPoint.slope < lastPoint.slope ? lastPoint.slope - escrowPoint.slope : int128(0);
            }
            lastPoint.bias = escrowPoint.bias < baseBias ? baseBias - escrowPoint.bias : int128(0);
            lastPoint.permanent = escrowPoint.permanent < lastPoint.permanent
                ? lastPoint.permanent - escrowPoint.permanent
                : int128(0);
        } else {
            if (endTime > block.timestamp) {
                store_.delegateeSlopeChanges[delegateeAddress][endTime] -= escrowPoint.slope;
                lastPoint.slope = lastPoint.slope + escrowPoint.slope;
            }
            lastPoint.bias = baseBias + escrowPoint.bias;
            lastPoint.permanent = lastPoint.permanent + escrowPoint.permanent;
        }
        /// @dev bias can be rounded up by lack of precision. If slope is 0 we are out
        if (lastPoint.slope == 0) {
            lastPoint.bias = 0;
        }
        _pushPointAtClock(store_._delegateCheckpoints[delegateeAddress], lastPoint);
        emit CheckpointDelegate(delegateeAddress, clock(), lastPoint.slope, lastPoint.bias, lastPoint.permanent);
    }

    /**
     * @dev Function to update delegatee's checkpoint
     * @param delegateeAddress The address of the delegatee
     * @return lastPoint The last point of the delegatee
     * @return lastCheckpoint The last checkpoint time of the delegatee
     */
    function baseCheckpointDelegatee(
        EscrowDelegateStore storage store_,
        address delegateeAddress
    ) public returns (Checkpoints.Point memory lastPoint, uint48 lastCheckpoint) {
        (bool exists, uint48 ts, Checkpoints.Point memory point) = store_
            ._delegateCheckpoints[delegateeAddress]
            .latestCheckpoint();
        lastPoint = point;
        lastCheckpoint = ts;
        if (exists) {
            // Go over days to fill history and calculate what the current point is
            uint48 testTime = toGlobalClock(lastCheckpoint); /// @dev  lastCheckpoint > tesTime

            uint256 maxTime = testTime + MAX_TIME.toUint256();

            // Iterate over time until current block timestamp or maxtime
            while (testTime < block.timestamp) {
                testTime += CLOCK_UNIT;
                int128 dSlope = 0;
                if (testTime > block.timestamp) {
                    testTime = uint48(block.timestamp);
                } else {
                    dSlope = store_.delegateeSlopeChanges[delegateeAddress][testTime];
                }
                if (dSlope != 0) {
                    lastPoint.bias -= lastPoint.slope * uint256(testTime - lastCheckpoint).toInt128();
                    lastPoint.slope += dSlope;
                    if (lastPoint.bias < 0) {
                        lastPoint.bias = 0;
                    }
                    if (lastPoint.slope < 0) {
                        lastPoint.slope = 0;
                    }
                    lastCheckpoint = uint48(testTime);
                    store_._delegateCheckpoints[delegateeAddress].push(lastCheckpoint, lastPoint);
                }
                if (testTime > maxTime) break;
            }
        }
        emit CheckpointDelegate(delegateeAddress, clock(), lastPoint.slope, lastPoint.bias, lastPoint.permanent);
    }

    /**
     * @dev Function to calculate total voting power at some point in the past
     * @param timestamp Time to calculate the total voting power at
     * @return Total voting power at that time
     */
    function getAdjustedGlobalVotes(
        EscrowDelegateStore storage store_,
        uint48 timestamp
    ) external view returns (uint256) {
        Checkpoints.Point memory lastPoint = _getAdjustedCheckpoint(store_, timestamp);
        return (lastPoint.bias + lastPoint.permanent).toUint256();
    }

    /**
     * @dev Function to get latest checkpoint of some point in the past
     * @param timestamp Time to calculate the total voting power at
     * @return Total voting power at that time
     */
    function _getAdjustedCheckpoint(
        EscrowDelegateStore storage store_,
        uint48 timestamp
    ) internal view returns (Checkpoints.Point memory) {
        uint48 clockTime = timestamp;
        (bool exists, uint48 lastCheckpointTs, Checkpoints.Point memory lastGlobal) = store_
            ._globalCheckpoints
            .upperLookupRecent(clockTime);
        if (!exists) return lastGlobal;
        uint48 testTime = toGlobalClock(lastCheckpointTs); /// @dev  lastCheckpointTs > tesTime
        uint256 maxTime = testTime + MAX_TIME.toUint256();

        // Iterate over time until the specified timestamp or maxtime is reached
        while (testTime < timestamp) {
            testTime += CLOCK_UNIT;
            int128 dSlope = 0;
            if (testTime > timestamp) {
                testTime = timestamp;
            } else {
                dSlope = store_.globalSlopeChanges[testTime];
            }
            if (dSlope != 0) {
                lastGlobal.bias -= lastGlobal.slope * uint256(testTime - lastCheckpointTs).toInt128();
                lastGlobal.slope += dSlope;
                if (lastGlobal.bias < 0) {
                    lastGlobal.bias = 0;
                }
                if (lastGlobal.slope < 0) {
                    lastGlobal.slope = 0;
                }
                lastCheckpointTs = uint48(testTime);
            }
            if (testTime > maxTime) break;
        }

        int128 change = lastGlobal.slope * uint256(clockTime - lastCheckpointTs).toInt128();
        lastGlobal.bias = lastGlobal.bias < change ? int128(0) : lastGlobal.bias - change;

        return lastGlobal;
    }

    /**
     * @notice Get the current bias for `escrowId` at `timestamp`
     * @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility
     * @dev Fetches last escrow point prior to a certain timestamp, then walks forward to timestamp.
     * @param escrowId NFT for lock
     * @param timestamp Epoch time to return bias power at
     * @return NFT bias
     */
    function getAdjustedEscrowBias(
        EscrowDelegateStore storage store_,
        uint256 escrowId,
        uint256 timestamp
    ) external view returns (uint256) {
        uint48 clockTime = timestamp.toUint48();
        (Checkpoints.Point memory lastPoint,) = getAdjustedEscrow(store_, escrowId, clockTime);
        if (lastPoint.permanent != 0) return lastPoint.permanent.toUint256();
        return lastPoint.bias.toUint256();
    }

    /**
     * @notice Get the current bias for `escrowId` at `timestamp`
     * @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility
     * @dev Fetches last escrow point prior to a certain timestamp, then walks forward to timestamp.
     * @param escrowId NFT for lock
     * @param timestamp Epoch time to return bias power at
     * @return NFT bias
     */
    function getAdjustedEscrow(
        EscrowDelegateStore storage store_,
        uint256 escrowId,
        uint256 timestamp
    ) public view returns (Checkpoints.Point memory, uint48) {
        uint48 clockTime = timestamp.toUint48();
        (bool exists, uint48 ts, Checkpoints.Point memory lastPoint) = store_
            ._escrowCheckpoints[escrowId]
            .upperLookupRecent(clockTime);
        if (!exists) return (lastPoint, ts);
        int128 change = ((lastPoint.slope * uint256(clockTime - ts).toInt128()));
        lastPoint.bias = lastPoint.bias < change ? int128(0) : lastPoint.bias - change;
        return (lastPoint, ts);
    }

    function getFirstEscrowPoint(
        EscrowDelegateStore storage store_,
        uint256 escrowId
    ) internal view returns (Checkpoints.Point memory, uint48) {
        (, uint48 ts, Checkpoints.Point memory point) =  store_._escrowCheckpoints[escrowId].firstCheckpoint();
        return (point, ts);
    }

    /// -----------------------------------------------------------------------
    /// Private functions
    /// -----------------------------------------------------------------------

    /**
     * @dev Function to push an address to the checkpoint
     * @param store The storage to push the address to
     * @param value The address to be pushed
     * @return The old and new address
     */
    function _pushAddressAtClock(
        Checkpoints.TraceAddress storage store,
        address value
    ) private returns (address, address) {
        return store.push(clock(), value);
    }

    /**
     * @dev Function to push a struct to the checkpoint
     * @param store The storage to push the struct to
     * @param value The struct to be pushed
     * @return The old and new struct
     */
    function _pushPointAtClock(
        Checkpoints.Trace storage store,
        Checkpoints.Point memory value
    ) private returns (Checkpoints.Point memory, Checkpoints.Point memory) {
        return store.push(clock(), value);
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 3 of 5 : Checkpoints.sol
// SPDX-License-Identifier: MIT
// This file was derived from OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/Checkpoints.sol)

pragma solidity 0.8.13;

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

/**
 * @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in
 * time, and later looking up past values by block number. See {Votes} as an example.
 *
 * To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new
 * checkpoint for the current transaction block using the {push} function.
 */
library Checkpoints {
    struct Trace {
        Checkpoint[] _checkpoints;
    }

    /**
     * @dev Struct to keep track of the voting power over time.
     */
    struct Point {
        /// @dev The voting power at a specific time
        /// - MUST never be negative.
        int128 bias;
        /// @dev The rate at which the voting power decreases over time.
        int128 slope;
        /// @dev The value of tokens which do not decrease over time, representing permanent voting power
        /// - MUST never be negative.
        int128 permanent;
    }

    struct Checkpoint {
        uint48 _key;
        Point _value;
    }

    /**
     * @dev A value was attempted to be inserted on a past checkpoint.
     */
    error CheckpointUnorderedInsertions();

    /**
     * @dev Pushes a (`key`, `value`) pair into a Trace so that it is stored as the checkpoint.
     *
     * Returns previous value and new value.
     *
     * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
     * library.
     */
    function push(Trace storage self, uint48 key, Point memory value) internal returns (Point memory, Point memory) {
        return _insert(self._checkpoints, key, value);
    }

    /**
     * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
     * there is none.
     */
    function lowerLookup(Trace storage self, uint48 key) internal view returns (Point memory) {
        uint256 len = self._checkpoints.length;
        uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
        return pos == len ? blankPoint() : _unsafeAccess(self._checkpoints, pos)._value;
    }

    /**
     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
     * if there is none.
     */
    function upperLookup(
        Trace storage self,
        uint48 key
    ) internal view returns (bool exists, uint48 _key, Point memory _value) {
        uint256 len = self._checkpoints.length;
        uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);

        exists = pos != 0;
        _value = exists ? _unsafeAccess(self._checkpoints, pos - 1)._value : blankPoint();
        _key = exists ? _unsafeAccess(self._checkpoints, pos - 1)._key : 0;
    }

    /**
     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
     * if there is none.
     *
     * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
     * keys).
     */
    function upperLookupRecent(
        Trace storage self,
        uint48 key
    ) internal view returns (bool exists, uint48 _key, Point memory _value) {
        uint256 len = self._checkpoints.length;

        uint256 low = 0;
        uint256 high = len;

        if (len > 5) {
            uint256 mid = len - Math.sqrt(len);
            if (key < _unsafeAccess(self._checkpoints, mid)._key) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);

        exists = pos != 0;
        _value = exists ? _unsafeAccess(self._checkpoints, pos - 1)._value : blankPoint();
        _key = exists ? _unsafeAccess(self._checkpoints, pos - 1)._key : 0;
    }

    /**
     * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
     */
    function latest(Trace storage self) internal view returns (Point memory) {
        uint256 pos = self._checkpoints.length;
        return pos == 0 ? blankPoint() : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
     * in the most recent checkpoint.
     */
    function latestCheckpoint(
        Trace storage self
    ) internal view returns (bool exists, uint48 _key, Point memory _value) {
        uint256 pos = self._checkpoints.length;
        if (pos == 0) {
            return (false, 0, blankPoint());
        } else {
            Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
            return (true, ckpt._key, ckpt._value);
        }
    }

    /**
     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
     * in the most recent checkpoint.
     */
    function firstCheckpoint(
        Trace storage self
    ) internal view returns (bool exists, uint48 _key, Point memory _value) {
        uint256 pos = self._checkpoints.length;
        if (pos == 0) {
            return (false, 0, blankPoint());
        } else {
            Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, 0);
            return (true, ckpt._key, ckpt._value);
        }
    }

    /**
     * @dev Returns the number of checkpoint.
     */
    function length(Trace storage self) internal view returns (uint256) {
        return self._checkpoints.length;
    }

    /**
     * @dev Returns checkpoint at given position.
     */
    function at(Trace storage self, uint48 pos) internal view returns (Checkpoint memory) {
        return self._checkpoints[pos];
    }

    /**
     * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
     * or by updating the last one.
     */
    function _insert(
        Checkpoint[] storage self,
        uint48 key,
        Point memory value
    ) private returns (Point memory, Point memory) {
        uint256 pos = self.length;

        if (pos > 0) {
            // Copying to memory is important here.
            Checkpoint memory last = _unsafeAccess(self, pos - 1);

            // Checkpoint keys must be non-decreasing.
            if (last._key > key) {
                revert CheckpointUnorderedInsertions();
            }

            // Update or push new checkpoint
            if (last._key == key) {
                _unsafeAccess(self, pos - 1)._value = value;
            } else {
                self.push(Checkpoint({_key: key, _value: value}));
            }
            return (last._value, value);
        } else {
            self.push(Checkpoint({_key: key, _value: value}));
            return (blankPoint(), value);
        }
    }

    /**
     * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
     * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
     * `high`.
     *
     * WARNING: `high` should not be greater than the array's length.
     */
    function _upperBinaryLookup(
        Checkpoint[] storage self,
        uint48 key,
        uint256 low,
        uint256 high
    ) private view returns (uint256) {
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self, mid)._key > key) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return high;
    }

    /**
     * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
     * `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
     * exclusive `high`.
     *
     * WARNING: `high` should not be greater than the array's length.
     */
    function _lowerBinaryLookup(
        Checkpoint[] storage self,
        uint48 key,
        uint256 low,
        uint256 high
    ) private view returns (uint256) {
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self, mid)._key < key) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        return high;
    }

    /**
     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
     */
    function _unsafeAccess(Checkpoint[] storage self, uint256 pos) private view returns (Checkpoint storage result) {
        return self[pos];
    }

    /**
     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
     */
    function _realUnsafeAccess(
        Checkpoint[] storage self,
        uint256 pos
    ) private pure returns (Checkpoint storage result) {
        assembly {
            mstore(0, self.slot)
            result.slot := add(keccak256(0, 0x20), pos)
        }
    }

    function blankPoint() internal pure returns (Point memory) {
        return Point({bias: 0, slope: 0, permanent: 0});
    }

    struct TraceAddress {
        CheckpointAddress[] _checkpoints;
    }

    struct CheckpointAddress {
        uint48 _key;
        address _value;
    }

    /**
     * @dev Pushes a (`key`, `value`) pair into a TraceAddress so that it is stored as the checkpoint.
     *
     * Returns previous value and new value.
     *
     * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
     * library.
     */
    function push(TraceAddress storage self, uint48 key, address value) internal returns (address, address) {
        return _insert(self._checkpoints, key, value);
    }

    /**
     * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
     * there is none.
     */
    function lowerLookup(TraceAddress storage self, uint48 key) internal view returns (address) {
        uint256 len = self._checkpoints.length;
        uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
        return pos == len ? address(0) : _unsafeAccess(self._checkpoints, pos)._value;
    }

    /**
     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
     * if there is none.
     */
    function upperLookup(TraceAddress storage self, uint48 key) internal view returns (address) {
        uint256 len = self._checkpoints.length;
        uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
        return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
     * if there is none.
     *
     * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high
     * keys).
     */
    function upperLookupRecent(TraceAddress storage self, uint48 key) internal view returns (address) {
        uint256 len = self._checkpoints.length;

        uint256 low = 0;
        uint256 high = len;

        if (len > 5) {
            uint256 mid = len - Math.sqrt(len);
            if (key < _unsafeAccess(self._checkpoints, mid)._key) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);

        return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
     */
    function latest(TraceAddress storage self) internal view returns (address) {
        uint256 pos = self._checkpoints.length;
        return pos == 0 ? address(0) : _unsafeAccess(self._checkpoints, pos - 1)._value;
    }

    /**
     * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
     * in the most recent checkpoint.
     */
    function latestCheckpoint(
        TraceAddress storage self
    ) internal view returns (bool exists, uint48 _key, address _value) {
        uint256 pos = self._checkpoints.length;
        if (pos == 0) {
            return (false, 0, address(0));
        } else {
            CheckpointAddress memory ckpt = _unsafeAccess(self._checkpoints, pos - 1);
            return (true, ckpt._key, ckpt._value);
        }
    }

    /**
     * @dev Returns the number of checkpoint.
     */
    function length(TraceAddress storage self) internal view returns (uint256) {
        return self._checkpoints.length;
    }

    /**
     * @dev Returns checkpoint at given position.
     */
    function at(TraceAddress storage self, uint48 pos) internal view returns (CheckpointAddress memory) {
        return self._checkpoints[pos];
    }

    /**
     * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
     * or by updating the last one.
     */
    function _insert(CheckpointAddress[] storage self, uint48 key, address value) private returns (address, address) {
        uint256 pos = self.length;

        if (pos > 0) {
            // Copying to memory is important here.
            CheckpointAddress memory last = _unsafeAccess(self, pos - 1);

            // Checkpoint keys must be non-decreasing.
            if (last._key > key) {
                revert CheckpointUnorderedInsertions();
            }

            // Update or push new checkpoint
            if (last._key == key) {
                _unsafeAccess(self, pos - 1)._value = value;
            } else {
                self.push(CheckpointAddress({_key: key, _value: value}));
            }
            return (last._value, value);
        } else {
            self.push(CheckpointAddress({_key: key, _value: value}));
            return (address(0), value);
        }
    }

    /**
     * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high`
     * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
     * `high`.
     *
     * WARNING: `high` should not be greater than the array's length.
     */
    function _upperBinaryLookup(
        CheckpointAddress[] storage self,
        uint48 key,
        uint256 low,
        uint256 high
    ) private view returns (uint256) {
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self, mid)._key > key) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        return high;
    }

    /**
     * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or
     * `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and
     * exclusive `high`.
     *
     * WARNING: `high` should not be greater than the array's length.
     */
    function _lowerBinaryLookup(
        CheckpointAddress[] storage self,
        uint48 key,
        uint256 low,
        uint256 high
    ) private view returns (uint256) {
        while (low < high) {
            uint256 mid = Math.average(low, high);
            if (_unsafeAccess(self, mid)._key < key) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        return high;
    }

    /**
     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
     */
    function _unsafeAccess(
        CheckpointAddress[] storage self,
        uint256 pos
    ) private pure returns (CheckpointAddress storage result) {
        assembly {
            mstore(0, self.slot)
            result.slot := add(keccak256(0, 0x20), pos)
        }
    }
}

File 4 of 5 : SafeCastLibrary.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

/// @title SafeCast Library
/// @author velodrome.finance
/// @notice Safely convert unsigned and signed integers without overflow / underflow
library SafeCastLibrary {
    error SafeCastOverflow();
    error SafeCastUnderflow();

    /// @dev Safely convert uint256 to int128
    function toInt128(uint256 value) internal pure returns (int128) {
        if (value > uint128(type(int128).max)) revert SafeCastOverflow();
        return int128(uint128(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
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) revert SafeCastOverflow();
        return uint48(value);
    }

    /// @dev Safely convert int128 to uint256
    function toUint256(int128 value) internal pure returns (uint256) {
        if (value < 0) revert SafeCastUnderflow();
        return uint256(int256(value));
    }
}

File 5 of 5 : Time.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

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

/**
 * @notice Adapted from OpenZeppelin's Time library: v5.0.0 for solc 0.8.13
 * @dev This library provides helpers for manipulating time-related objects.
 *
 * It uses the following types:
 * - `uint48` for timepoints
 * - `uint32` for durations
 *
 * While the library doesn't provide specific types for timepoints and duration, it does provide:
 * - a `Delay` type to represent duration that can be programmed to change value automatically at a given point
 * - additional helper functions
 */
library Time {
    using Time for *;

    /**
     * @dev Get the block timestamp as a Timepoint.
     */
    function timestamp() internal view returns (uint48) {
        return SafeCastLibrary.toUint48(block.timestamp);
    }

    /**
     * @dev Get the block number as a Timepoint.
     */
    function blockNumber() internal view returns (uint48) {
        return SafeCastLibrary.toUint48(block.number);
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[],"name":"CheckpointUnorderedInsertions","type":"error"},{"inputs":[],"name":"SafeCastOverflow","type":"error"},{"inputs":[],"name":"SafeCastUnderflow","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"delegatee","type":"address"},{"indexed":false,"internalType":"uint48","name":"timestamp","type":"uint48"},{"indexed":false,"internalType":"int128","name":"slope","type":"int128"},{"indexed":false,"internalType":"int128","name":"bias","type":"int128"},{"indexed":false,"internalType":"int128","name":"permanent","type":"int128"}],"name":"CheckpointDelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"escrowId","type":"uint256"},{"indexed":false,"internalType":"uint48","name":"timestamp","type":"uint48"},{"indexed":false,"internalType":"int128","name":"slope","type":"int128"},{"indexed":false,"internalType":"int128","name":"bias","type":"int128"},{"indexed":false,"internalType":"int128","name":"permanent","type":"int128"}],"name":"CheckpointEscrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"timestamp","type":"uint48"},{"indexed":false,"internalType":"int128","name":"slope","type":"int128"},{"indexed":false,"internalType":"int128","name":"bias","type":"int128"},{"indexed":false,"internalType":"int128","name":"permanent","type":"int128"}],"name":"CheckpointGlobal","type":"event"},{"inputs":[],"name":"CLOCK_UNIT","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TIME","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clock","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalClock","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"}]

61279461003a600b82828239805160001a60731461002d57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100ff5760003560e01c806391ddadf4116100a1578063e680a31011610070578063e680a3101461029f578063f400f3c4146102b2578063f8183ecf146102c5578063f83bb313146102d857600080fd5b806391ddadf4146101d35780639511aa53146101db5780639bdd2e4d14610206578063d7ac26da1461024657600080fd5b806354703153116100dd578063547031531461016a578063683e955b1461018b5780636977e3a9146101ab57806382d4bd17146101cb57600080fd5b8063269499841461010457806334e78a9e146101275780634cd2605614610148575b600080fd5b61010f6303c2670081565b604051600f9190910b81526020015b60405180910390f35b61013a610135366004612216565b6102eb565b60405190815260200161011e565b81801561015457600080fd5b50610168610163366004612254565b610323565b005b61017462093a8081565b60405165ffffffffffff909116815260200161011e565b81801561019757600080fd5b506101686101a6366004612329565b610680565b8180156101b757600080fd5b506101686101c6366004612372565b61097a565b6101746109f2565b610174610a11565b6101ee6101e936600461238b565b610a1b565b6040516001600160a01b03909116815260200161011e565b81801561021257600080fd5b506102266102213660046123d7565b610a3f565b604080516001600160a01b0393841681529290911660208301520161011e565b81801561025257600080fd5b50610266610261366004612414565b610b62565b604080518351600f90810b8252602080860151820b908301529382015190930b9083015265ffffffffffff16606082015260800161011e565b6102666102ad366004612437565b610db5565b61013a6102c0366004612437565b610e70565b61013a6102d3366004612463565b610ed6565b6101ee6102e6366004612496565b610f06565b6000806102f88484610f16565b90506103198160400151826000015161031191906124ce565b600f0b6110f5565b9150505b92915050565b6000806000610360604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b9050600061039c604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b90506103a78561113b565b65ffffffffffff16945088156106685784156103c45760006103c6565b865b600f0b604082015285156103db5760006103dd565b875b600f0b604083015242861180156103f75750600088600f0b135b156104395761040a6303c2670089612545565b600f0b602083015261042461041f428861258c565b611155565b826020015161043391906125a3565b600f0b82525b428511801561044b5750600087600f0b135b156104885761045e6303c2670088612545565b600f0b602082015261047361041f428761258c565b816020015161048291906125a3565b600f0b81525b600086815260018b016020526040902054600f0b935084156104ca578585036104b3578392506104ca565b600085815260018b016020526040902054600f0b92505b4286111561052f5760208201516104e190856124ce565b93508486036104fc5760208101516104f9908561264a565b93505b600086815260018b016020526040902080546fffffffffffffffffffffffffffffffff19166001600160801b0386161790555b42851115610583578585111561058357602081015161054e908461264a565b600086815260018c016020526040902080546fffffffffffffffffffffffffffffffff19166001600160801b03831617905592505b600089815260028b016020526040902061059d908261118c565b50507f8b0a9686e26f875f7c3be0382e7593609c4d02c5161afd329687ca49cc339b60896105c9610a11565b60208401518451604080870151905161061195949392919094855265ffffffffffff939093166020850152600f91820b6040850152810b60608401520b608082015260a00190565b60405180910390a1600089815260048b01602052604081208190610634906111df565b92509250508165ffffffffffff16600014610665576106578c82868b600061126a565b6106658c82858a600161126a565b50505b6106748a8a8484610680565b50505050505050505050565b60008061068c86611539565b925092505060008265ffffffffffff166000036106a957426106ab565b825b905060006106c08265ffffffffffff1661113b565b905060006106d16303c267006110f5565b6106e39065ffffffffffff84166126ac565b90505b428265ffffffffffff1610156108055761070362093a80836126c4565b91506000428365ffffffffffff1611156107275761072042611621565b9250610746565b5065ffffffffffff8216600090815260018a016020526040902054600f0b5b80600f0b6000146107e95761076b61075e85856126ee565b65ffffffffffff16611155565b856020015161077a91906125a3565b8551869061078990839061264a565b600f0b9052506020850180518291906107a39083906124ce565b600f90810b90915286516000910b121590506107be57600085525b60008560200151600f0b12156107d657600060208601525b91925082916107e68a848761164a565b50505b818365ffffffffffff1611156107ff5750610805565b506106e6565b505085156108c05761082261041f65ffffffffffff83164261258c565b826020015161083191906125a3565b825161083d919061264a565b600f0b825260208086015190850151610856919061264a565b8260200181815161086791906124ce565b600f0b9052508451845161087b919061264a565b8251839061088a9083906124ce565b600f0b905250604080860151908501516108a4919061264a565b826040018181516108b591906124ce565b600f0b9052506108fc565b426108d661041f65ffffffffffff84168361258c565b83602001516108e591906125a3565b835184906108f490839061264a565b600f0b905250505b610906878361118c565b50507fdc8ed15e968adee1196e5ff0d83b745b050ee7aa99544d40a74145a036e35e38610931610a11565b6020848101518551604080880151815165ffffffffffff9096168652600f93840b9486019490945290820b908401520b606082015260800160405180910390a150505050505050565b6109ef8160006109b8604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b6040805160608082018352600080835260208084018290529284018190528351918201845280825291810182905291820152610680565b50565b6000610a0c6109ff611696565b65ffffffffffff1661113b565b905090565b6000610a0c611696565b60008281526004840160205260408120610a3590836116a1565b90505b9392505050565b600083815260048501602052604081208190610a5a9061174c565b9150836001600160a01b0316826001600160a01b031603610a7c575082610b59565b600085815260028701602052604081208190610a9790611539565b9250925050610ab38265ffffffffffff164261041f919061258c565b8160200151610ac291906125a3565b81518290610ad190839061264a565b600f90810b90915282516000910b12159050610aec57600081525b856001600160a01b0316846001600160a01b031614158015610b1657506001600160a01b03841615155b15610b2957610b2988858388600061126a565b610b3788878388600161126a565b60008781526004890160205260409020610b519087611788565b508693505050505b94509492505050565b60408051606081018252600080825260208201819052918101919091526001600160a01b03821660009081526003840160205260408120819081908190610ba890611539565b9250925092508094508193508215610d2e576000610bcd8565ffffffffffff1661113b565b90506000610bde6303c267006110f5565b610bf09065ffffffffffff84166126ac565b90505b428265ffffffffffff161015610d2b57610c1062093a80836126c4565b91506000428365ffffffffffff161115610c2c57429250610c5f565b506001600160a01b038816600090815260058a016020908152604080832065ffffffffffff86168452909152902054600f0b5b80600f0b600014610d0f57610c7761075e88856126ee565b8860200151610c8691906125a3565b88518990610c9590839061264a565b600f0b905250602088018051829190610caf9083906124ce565b600f90810b90915289516000910b12159050610cca57600088525b60008860200151600f0b1215610ce257600060208901525b6001600160a01b038916600090815260038b01602052604090209296508692610d0c90848a61164a565b50505b818365ffffffffffff161115610d255750610d2b565b50610bf3565b50505b7fc5396bd0d5b21891ce90363f0f17e19e48b228d8a66186ae447c827403610b3986610d58610a11565b60208881015189516040808c015181516001600160a01b03909716875265ffffffffffff90951693860193909352600f91820b9285019290925290810b60608401520b608082015260a00160405180910390a15050509250929050565b604080516060810182526000808252602082018190529181018290529080610ddc84611621565b600086815260028801602052604081209192509081908190610dfe908561179e565b92509250925082610e155794509250610e68915050565b6000610e2461075e84876126ee565b8260200151610e3391906125a3565b905080600f0b8260000151600f0b12610e58578151610e5390829061264a565b610e5b565b60005b600f0b8252509450925050505b935093915050565b600080610e7c83611621565b90506000610e9386868465ffffffffffff16610db5565b5090508060400151600f0b600014610ebe57610eb58160400151600f0b6110f5565b92505050610a38565b8051610ecc90600f0b6110f5565b9695505050505050565b600080610ee48585856118de565b9050610efd8160400151826000015161031191906124ce565b95945050505050565b6000610a3883836101e942611621565b60408051606081018252600080825260208201819052918101919091528160008080610f42878561179e565b92509250925082610f5857935061031d92505050565b6000610f6b8365ffffffffffff1661113b565b90506000610f7c6303c267006110f5565b610f8e9065ffffffffffff84166126ac565b90505b8765ffffffffffff168265ffffffffffff16101561109c57610fb662093a80836126c4565b915060008865ffffffffffff168365ffffffffffff161115610fda57889250610ff9565b5065ffffffffffff8216600090815260018a016020526040902054600f0b5b80600f0b6000146110805761101161075e86856126ee565b846020015161102091906125a3565b8451859061102f90839061264a565b600f0b9052506020840180518291906110499083906124ce565b600f90810b90915285516000910b1215905061106457600084525b60008460200151600f0b121561107c57600060208501525b8294505b818365ffffffffffff161115611096575061109c565b50610f91565b60006110ab61075e86896126ee565b84602001516110ba91906125a3565b905080600f0b8460000151600f0b126110df5783516110da90829061264a565b6110e2565b60005b600f0b8452509198975050505050505050565b60008082600f0b1215611134576040517f0101bd7400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600f0b90565b600062093a8061114b8184612715565b61031d9190612729565b60006f7fffffffffffffffffffffffffffffff821115611188576040516393dafdf160e01b815260040160405180910390fd5b5090565b60408051606080820183526000808352602080840182905283850182905284519283018552818352820181905292810192909252906111d46111cc610a11565b85908561164a565b915091509250929050565b80546000908190819080820361120057600080600093509350935050611263565b600061121f8661121160018561258c565b600091825260209091200190565b60408051808201909152905465ffffffffffff811680835266010000000000009091046001600160a01b031660209092018290526001965094509250611263915050565b9193909250565b6000806112778787610b62565b9092509050600061129361041f65ffffffffffff84164261258c565b83602001516112a291906125a3565b83516112ae919061264a565b9050836113c1574285111561135a576020808701516001600160a01b038916600090815260058b01835260408082208983529093529182208054919290916112fa908490600f0b6124ce565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055508260200151600f0b8660200151600f0b1261133d576000611351565b85602001518360200151611351919061264a565b600f0b60208401525b80600f0b8660000151600f0b1261137257600061137e565b855161137e908261264a565b600f90810b84526040808501519088015190820b910b126113a05760006113b4565b856040015183604001516113b4919061264a565b600f0b6040840152611478565b4285111561144a576020808701516001600160a01b038916600090815260058b0183526040808220898352909352918220805491929091611406908490600f0b61264a565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055508560200151836020015161144191906124ce565b600f0b60208401525b855161145690826124ce565b600f0b83526040808701519084015161146f91906124ce565b600f0b60408401525b8260200151600f0b60000361148c57600083525b6001600160a01b038716600090815260038901602052604090206114b0908461118c565b50507fc5396bd0d5b21891ce90363f0f17e19e48b228d8a66186ae447c827403610b39876114dc610a11565b60208681015187516040808a015181516001600160a01b03909716875265ffffffffffff90951693860193909352600f91820b9285019290925290810b60608401520b608082015260a00160405180910390a15050505050505050565b604080516060810182526000808252602082018190529181018290528190835460008190036115a95760008061159d604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b93509350935050611263565b60006115bf866115ba60018561258c565b611a9f565b604080518082018252825465ffffffffffff1681528151606081018352600180850154600f81810b8452600160801b909104810b602084810191909152600290960154900b938201939093529281018390525190965094509250611263915050565b600065ffffffffffff821115611188576040516393dafdf160e01b815260040160405180910390fd5b604080516060808201835260008083526020808401829052838501829052845192830185528183528201819052928101929092529061168a858585611ac9565b91509150935093915050565b6000610a0c42611621565b8154600090818160058111156117005760006116bc84611d92565b6116c6908561258c565b60008881526020902090915081015465ffffffffffff90811690871610156116f0578091506116fe565b6116fb8160016126ac565b92505b505b600061170e87878585611e7a565b9050801561173e576117258761121160018461258c565b54660100000000000090046001600160a01b0316611741565b60005b979650505050505050565b8054600090801561177f576117668361121160018461258c565b54660100000000000090046001600160a01b0316610a38565b60009392505050565b6000806111d4611796610a11565b859085611edc565b604080516060810182526000808252602082018190529181018290528190845460008160058111156118175760006117d584611d92565b6117df908561258c565b90506117eb8982611a9f565b5465ffffffffffff908116908916101561180757809150611815565b6118128160016126ac565b92505b505b600061182589898585611eea565b80151597509050866118685760408051606080820183526000808352602080840182905292840181905283519182018452808252918101829052918201526118aa565b611877896115ba60018461258c565b604080516060810182526001830154600f81810b8352600160801b909104810b602083015260029093015490920b908201525b9450866118b85760006118d1565b6118c7896115ba60018461258c565b5465ffffffffffff165b9550505050509250925092565b60408051606081018252600080825260208201819052918101919091526001600160a01b0383166000908152600385016020526040812081908190611923908661179e565b92509250925082611938579250610a38915050565b600061194b8365ffffffffffff1661113b565b9050600061195c6303c267006110f5565b61196e9065ffffffffffff84166126ac565b90505b8665ffffffffffff168265ffffffffffff161015611a905761199662093a80836126c4565b915060008765ffffffffffff168365ffffffffffff1611156119ba578792506119ed565b506001600160a01b038816600090815260058a016020908152604080832065ffffffffffff86168452909152902054600f0b5b80600f0b600014611a7457611a0561075e86856126ee565b8460200151611a1491906125a3565b84518590611a2390839061264a565b600f0b905250602084018051829190611a3d9083906124ce565b600f90810b90915285516000910b12159050611a5857600084525b60008460200151600f0b1215611a7057600060208501525b8294505b818365ffffffffffff161115611a8a5750611a90565b50611971565b60006110ab61075e868a6126ee565b6000828281548110611ab357611ab3612748565b9060005260206000209060030201905092915050565b6040805160608101825260008082526020820181905291810191909152604080516060810182526000808252602082018190529181019190915284548015611cb6576000611b1c876115ba60018561258c565b604080518082018252825465ffffffffffff908116825282516060810184526001850154600f81810b8352600160801b909104810b602083810191909152600290960154900b9381019390935292810191909152805190925087821691161115611b9957604051638553be6160e01b815260040160405180910390fd5b805165ffffffffffff808816911603611c0c5784611bbc886115ba60018661258c565b815160208301516001600160801b03918216600160801b91831691909102176001830155604090920151600290910180546fffffffffffffffffffffffffffffffff191691909216179055611ca6565b60408051808201825265ffffffffffff888116825260208083018981528b5460018082018e5560008e815284902095516003909202909501805465ffffffffffff191691909416178355518051918101516001600160801b03928316600160801b9184169190910217938301939093559190920151600290920180546fffffffffffffffffffffffffffffffff1916929091169190911790555b602001519250839150610e689050565b60408051808201825265ffffffffffff878116825260208083018881528a5460018082018d5560008d815284902095516003909202909501805465ffffffffffff191691909416178355518051918101516001600160801b03928316600160801b9184169190910217938301939093559190920151600290920180546fffffffffffffffffffffffffffffffff191692909116919091179055611d87604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b849250925050610e68565b600081600003611da457506000919050565b60006001611db184611f41565b901c6001901b90506001818481611dca57611dca61252f565b048201901c90506001818481611de257611de261252f565b048201901c90506001818481611dfa57611dfa61252f565b048201901c90506001818481611e1257611e1261252f565b048201901c90506001818481611e2a57611e2a61252f565b048201901c90506001818481611e4257611e4261252f565b048201901c90506001818481611e5a57611e5a61252f565b048201901c9050610a3881828581611e7457611e7461252f565b04611fd5565b60005b81831015611ed4576000611e918484611feb565b60008781526020902090915065ffffffffffff86169082015465ffffffffffff161115611ec057809250611ece565b611ecb8160016126ac565b93505b50611e7d565b509392505050565b60008061168a858585612006565b60005b81831015611ed4576000611f018484611feb565b90508465ffffffffffff16611f168783611a9f565b5465ffffffffffff161115611f2d57809250611f3b565b611f388160016126ac565b93505b50611eed565b600080608083901c15611f5657608092831c92015b604083901c15611f6857604092831c92015b602083901c15611f7a57602092831c92015b601083901c15611f8c57601092831c92015b600883901c15611f9e57600892831c92015b600483901c15611fb057600492831c92015b600283901c15611fc257600292831c92015b600183901c1561031d5760010192915050565b6000818310611fe45781610a38565b5090919050565b6000611ffa6002848418612715565b610a38908484166126ac565b8254600090819080156121715760006120248761121160018561258c565b60408051808201909152905465ffffffffffff80821680845266010000000000009092046001600160a01b03166020840152919250908716101561207b57604051638553be6160e01b815260040160405180910390fd5b805165ffffffffffff8088169116036120e4578461209e8861121160018661258c565b80546001600160a01b03929092166601000000000000027fffffffffffff0000000000000000000000000000000000000000ffffffffffff909216919091179055611ca6565b6040805180820190915265ffffffffffff80881682526001600160a01b0380881660208085019182528b54600181018d5560008d81529190912094519401805491519092166601000000000000027fffffffffffff00000000000000000000000000000000000000000000000000009091169390921692909217179055602001519250839150610e689050565b50506040805180820190915265ffffffffffff80851682526001600160a01b0380851660208085019182528854600181018a5560008a815291822095519501805492519093166601000000000000027fffffffffffff0000000000000000000000000000000000000000000000000000909216949093169390931792909217909155905081610e68565b803565ffffffffffff8116811461221157600080fd5b919050565b6000806040838503121561222957600080fd5b82359150612239602084016121fb565b90509250929050565b8035600f81900b811461221157600080fd5b60008060008060008060c0878903121561226d57600080fd5b863595506020870135945061228460408801612242565b935061229260608801612242565b92506080870135915060a087013590509295509295509295565b6000606082840312156122be57600080fd5b6040516060810181811067ffffffffffffffff821117156122ef57634e487b7160e01b600052604160045260246000fd5b6040529050806122fe83612242565b815261230c60208401612242565b602082015261231d60408401612242565b60408201525092915050565b600080600080610100858703121561234057600080fd5b843593506020850135925061235886604087016122ac565b91506123678660a087016122ac565b905092959194509250565b60006020828403121561238457600080fd5b5035919050565b6000806000606084860312156123a057600080fd5b83359250602084013591506123b7604085016121fb565b90509250925092565b80356001600160a01b038116811461221157600080fd5b600080600080608085870312156123ed57600080fd5b8435935060208501359250612404604086016123c0565b9396929550929360600135925050565b6000806040838503121561242757600080fd5b82359150612239602084016123c0565b60008060006060848603121561244c57600080fd5b505081359360208301359350604090920135919050565b60008060006060848603121561247857600080fd5b83359250612488602085016123c0565b91506123b7604085016121fb565b600080604083850312156124a957600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b600081600f0b83600f0b60008212826f7fffffffffffffffffffffffffffffff03821381151615612501576125016124b8565b826f7fffffffffffffffffffffffffffffff19038212811615612526576125266124b8565b50019392505050565b634e487b7160e01b600052601260045260246000fd5b600081600f0b83600f0b8061255c5761255c61252f565b6f7fffffffffffffffffffffffffffffff19821460001982141615612583576125836124b8565b90059392505050565b60008282101561259e5761259e6124b8565b500390565b600081600f0b83600f0b6f7fffffffffffffffffffffffffffffff6000821360008413838304851182821616156125dc576125dc6124b8565b6f7fffffffffffffffffffffffffffffff196000851286820586128184161615612608576126086124b8565b60008712925085820587128484161615612624576126246124b8565b8585058712818416161561263a5761263a6124b8565b5050509290910295945050505050565b600081600f0b83600f0b60008112816f7fffffffffffffffffffffffffffffff190183128115161561267e5761267e6124b8565b816f7fffffffffffffffffffffffffffffff0183138116156126a2576126a26124b8565b5090039392505050565b600082198211156126bf576126bf6124b8565b500190565b600065ffffffffffff8083168185168083038211156126e5576126e56124b8565b01949350505050565b600065ffffffffffff8381169083168181101561270d5761270d6124b8565b039392505050565b6000826127245761272461252f565b500490565b6000816000190483118215151615612743576127436124b8565b500290565b634e487b7160e01b600052603260045260246000fdfea2646970667358221220748bcf20bdfca2e5f378cebfccd87187ad8ad45e09b55f275f355693caa63a9764736f6c634300080d0033

Deployed Bytecode

0x73a615388bd2f920ee2fec7606f26908b454f0c24930146080604052600436106100ff5760003560e01c806391ddadf4116100a1578063e680a31011610070578063e680a3101461029f578063f400f3c4146102b2578063f8183ecf146102c5578063f83bb313146102d857600080fd5b806391ddadf4146101d35780639511aa53146101db5780639bdd2e4d14610206578063d7ac26da1461024657600080fd5b806354703153116100dd578063547031531461016a578063683e955b1461018b5780636977e3a9146101ab57806382d4bd17146101cb57600080fd5b8063269499841461010457806334e78a9e146101275780634cd2605614610148575b600080fd5b61010f6303c2670081565b604051600f9190910b81526020015b60405180910390f35b61013a610135366004612216565b6102eb565b60405190815260200161011e565b81801561015457600080fd5b50610168610163366004612254565b610323565b005b61017462093a8081565b60405165ffffffffffff909116815260200161011e565b81801561019757600080fd5b506101686101a6366004612329565b610680565b8180156101b757600080fd5b506101686101c6366004612372565b61097a565b6101746109f2565b610174610a11565b6101ee6101e936600461238b565b610a1b565b6040516001600160a01b03909116815260200161011e565b81801561021257600080fd5b506102266102213660046123d7565b610a3f565b604080516001600160a01b0393841681529290911660208301520161011e565b81801561025257600080fd5b50610266610261366004612414565b610b62565b604080518351600f90810b8252602080860151820b908301529382015190930b9083015265ffffffffffff16606082015260800161011e565b6102666102ad366004612437565b610db5565b61013a6102c0366004612437565b610e70565b61013a6102d3366004612463565b610ed6565b6101ee6102e6366004612496565b610f06565b6000806102f88484610f16565b90506103198160400151826000015161031191906124ce565b600f0b6110f5565b9150505b92915050565b6000806000610360604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b9050600061039c604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b90506103a78561113b565b65ffffffffffff16945088156106685784156103c45760006103c6565b865b600f0b604082015285156103db5760006103dd565b875b600f0b604083015242861180156103f75750600088600f0b135b156104395761040a6303c2670089612545565b600f0b602083015261042461041f428861258c565b611155565b826020015161043391906125a3565b600f0b82525b428511801561044b5750600087600f0b135b156104885761045e6303c2670088612545565b600f0b602082015261047361041f428761258c565b816020015161048291906125a3565b600f0b81525b600086815260018b016020526040902054600f0b935084156104ca578585036104b3578392506104ca565b600085815260018b016020526040902054600f0b92505b4286111561052f5760208201516104e190856124ce565b93508486036104fc5760208101516104f9908561264a565b93505b600086815260018b016020526040902080546fffffffffffffffffffffffffffffffff19166001600160801b0386161790555b42851115610583578585111561058357602081015161054e908461264a565b600086815260018c016020526040902080546fffffffffffffffffffffffffffffffff19166001600160801b03831617905592505b600089815260028b016020526040902061059d908261118c565b50507f8b0a9686e26f875f7c3be0382e7593609c4d02c5161afd329687ca49cc339b60896105c9610a11565b60208401518451604080870151905161061195949392919094855265ffffffffffff939093166020850152600f91820b6040850152810b60608401520b608082015260a00190565b60405180910390a1600089815260048b01602052604081208190610634906111df565b92509250508165ffffffffffff16600014610665576106578c82868b600061126a565b6106658c82858a600161126a565b50505b6106748a8a8484610680565b50505050505050505050565b60008061068c86611539565b925092505060008265ffffffffffff166000036106a957426106ab565b825b905060006106c08265ffffffffffff1661113b565b905060006106d16303c267006110f5565b6106e39065ffffffffffff84166126ac565b90505b428265ffffffffffff1610156108055761070362093a80836126c4565b91506000428365ffffffffffff1611156107275761072042611621565b9250610746565b5065ffffffffffff8216600090815260018a016020526040902054600f0b5b80600f0b6000146107e95761076b61075e85856126ee565b65ffffffffffff16611155565b856020015161077a91906125a3565b8551869061078990839061264a565b600f0b9052506020850180518291906107a39083906124ce565b600f90810b90915286516000910b121590506107be57600085525b60008560200151600f0b12156107d657600060208601525b91925082916107e68a848761164a565b50505b818365ffffffffffff1611156107ff5750610805565b506106e6565b505085156108c05761082261041f65ffffffffffff83164261258c565b826020015161083191906125a3565b825161083d919061264a565b600f0b825260208086015190850151610856919061264a565b8260200181815161086791906124ce565b600f0b9052508451845161087b919061264a565b8251839061088a9083906124ce565b600f0b905250604080860151908501516108a4919061264a565b826040018181516108b591906124ce565b600f0b9052506108fc565b426108d661041f65ffffffffffff84168361258c565b83602001516108e591906125a3565b835184906108f490839061264a565b600f0b905250505b610906878361118c565b50507fdc8ed15e968adee1196e5ff0d83b745b050ee7aa99544d40a74145a036e35e38610931610a11565b6020848101518551604080880151815165ffffffffffff9096168652600f93840b9486019490945290820b908401520b606082015260800160405180910390a150505050505050565b6109ef8160006109b8604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b6040805160608082018352600080835260208084018290529284018190528351918201845280825291810182905291820152610680565b50565b6000610a0c6109ff611696565b65ffffffffffff1661113b565b905090565b6000610a0c611696565b60008281526004840160205260408120610a3590836116a1565b90505b9392505050565b600083815260048501602052604081208190610a5a9061174c565b9150836001600160a01b0316826001600160a01b031603610a7c575082610b59565b600085815260028701602052604081208190610a9790611539565b9250925050610ab38265ffffffffffff164261041f919061258c565b8160200151610ac291906125a3565b81518290610ad190839061264a565b600f90810b90915282516000910b12159050610aec57600081525b856001600160a01b0316846001600160a01b031614158015610b1657506001600160a01b03841615155b15610b2957610b2988858388600061126a565b610b3788878388600161126a565b60008781526004890160205260409020610b519087611788565b508693505050505b94509492505050565b60408051606081018252600080825260208201819052918101919091526001600160a01b03821660009081526003840160205260408120819081908190610ba890611539565b9250925092508094508193508215610d2e576000610bcd8565ffffffffffff1661113b565b90506000610bde6303c267006110f5565b610bf09065ffffffffffff84166126ac565b90505b428265ffffffffffff161015610d2b57610c1062093a80836126c4565b91506000428365ffffffffffff161115610c2c57429250610c5f565b506001600160a01b038816600090815260058a016020908152604080832065ffffffffffff86168452909152902054600f0b5b80600f0b600014610d0f57610c7761075e88856126ee565b8860200151610c8691906125a3565b88518990610c9590839061264a565b600f0b905250602088018051829190610caf9083906124ce565b600f90810b90915289516000910b12159050610cca57600088525b60008860200151600f0b1215610ce257600060208901525b6001600160a01b038916600090815260038b01602052604090209296508692610d0c90848a61164a565b50505b818365ffffffffffff161115610d255750610d2b565b50610bf3565b50505b7fc5396bd0d5b21891ce90363f0f17e19e48b228d8a66186ae447c827403610b3986610d58610a11565b60208881015189516040808c015181516001600160a01b03909716875265ffffffffffff90951693860193909352600f91820b9285019290925290810b60608401520b608082015260a00160405180910390a15050509250929050565b604080516060810182526000808252602082018190529181018290529080610ddc84611621565b600086815260028801602052604081209192509081908190610dfe908561179e565b92509250925082610e155794509250610e68915050565b6000610e2461075e84876126ee565b8260200151610e3391906125a3565b905080600f0b8260000151600f0b12610e58578151610e5390829061264a565b610e5b565b60005b600f0b8252509450925050505b935093915050565b600080610e7c83611621565b90506000610e9386868465ffffffffffff16610db5565b5090508060400151600f0b600014610ebe57610eb58160400151600f0b6110f5565b92505050610a38565b8051610ecc90600f0b6110f5565b9695505050505050565b600080610ee48585856118de565b9050610efd8160400151826000015161031191906124ce565b95945050505050565b6000610a3883836101e942611621565b60408051606081018252600080825260208201819052918101919091528160008080610f42878561179e565b92509250925082610f5857935061031d92505050565b6000610f6b8365ffffffffffff1661113b565b90506000610f7c6303c267006110f5565b610f8e9065ffffffffffff84166126ac565b90505b8765ffffffffffff168265ffffffffffff16101561109c57610fb662093a80836126c4565b915060008865ffffffffffff168365ffffffffffff161115610fda57889250610ff9565b5065ffffffffffff8216600090815260018a016020526040902054600f0b5b80600f0b6000146110805761101161075e86856126ee565b846020015161102091906125a3565b8451859061102f90839061264a565b600f0b9052506020840180518291906110499083906124ce565b600f90810b90915285516000910b1215905061106457600084525b60008460200151600f0b121561107c57600060208501525b8294505b818365ffffffffffff161115611096575061109c565b50610f91565b60006110ab61075e86896126ee565b84602001516110ba91906125a3565b905080600f0b8460000151600f0b126110df5783516110da90829061264a565b6110e2565b60005b600f0b8452509198975050505050505050565b60008082600f0b1215611134576040517f0101bd7400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600f0b90565b600062093a8061114b8184612715565b61031d9190612729565b60006f7fffffffffffffffffffffffffffffff821115611188576040516393dafdf160e01b815260040160405180910390fd5b5090565b60408051606080820183526000808352602080840182905283850182905284519283018552818352820181905292810192909252906111d46111cc610a11565b85908561164a565b915091509250929050565b80546000908190819080820361120057600080600093509350935050611263565b600061121f8661121160018561258c565b600091825260209091200190565b60408051808201909152905465ffffffffffff811680835266010000000000009091046001600160a01b031660209092018290526001965094509250611263915050565b9193909250565b6000806112778787610b62565b9092509050600061129361041f65ffffffffffff84164261258c565b83602001516112a291906125a3565b83516112ae919061264a565b9050836113c1574285111561135a576020808701516001600160a01b038916600090815260058b01835260408082208983529093529182208054919290916112fa908490600f0b6124ce565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055508260200151600f0b8660200151600f0b1261133d576000611351565b85602001518360200151611351919061264a565b600f0b60208401525b80600f0b8660000151600f0b1261137257600061137e565b855161137e908261264a565b600f90810b84526040808501519088015190820b910b126113a05760006113b4565b856040015183604001516113b4919061264a565b600f0b6040840152611478565b4285111561144a576020808701516001600160a01b038916600090815260058b0183526040808220898352909352918220805491929091611406908490600f0b61264a565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055508560200151836020015161144191906124ce565b600f0b60208401525b855161145690826124ce565b600f0b83526040808701519084015161146f91906124ce565b600f0b60408401525b8260200151600f0b60000361148c57600083525b6001600160a01b038716600090815260038901602052604090206114b0908461118c565b50507fc5396bd0d5b21891ce90363f0f17e19e48b228d8a66186ae447c827403610b39876114dc610a11565b60208681015187516040808a015181516001600160a01b03909716875265ffffffffffff90951693860193909352600f91820b9285019290925290810b60608401520b608082015260a00160405180910390a15050505050505050565b604080516060810182526000808252602082018190529181018290528190835460008190036115a95760008061159d604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b93509350935050611263565b60006115bf866115ba60018561258c565b611a9f565b604080518082018252825465ffffffffffff1681528151606081018352600180850154600f81810b8452600160801b909104810b602084810191909152600290960154900b938201939093529281018390525190965094509250611263915050565b600065ffffffffffff821115611188576040516393dafdf160e01b815260040160405180910390fd5b604080516060808201835260008083526020808401829052838501829052845192830185528183528201819052928101929092529061168a858585611ac9565b91509150935093915050565b6000610a0c42611621565b8154600090818160058111156117005760006116bc84611d92565b6116c6908561258c565b60008881526020902090915081015465ffffffffffff90811690871610156116f0578091506116fe565b6116fb8160016126ac565b92505b505b600061170e87878585611e7a565b9050801561173e576117258761121160018461258c565b54660100000000000090046001600160a01b0316611741565b60005b979650505050505050565b8054600090801561177f576117668361121160018461258c565b54660100000000000090046001600160a01b0316610a38565b60009392505050565b6000806111d4611796610a11565b859085611edc565b604080516060810182526000808252602082018190529181018290528190845460008160058111156118175760006117d584611d92565b6117df908561258c565b90506117eb8982611a9f565b5465ffffffffffff908116908916101561180757809150611815565b6118128160016126ac565b92505b505b600061182589898585611eea565b80151597509050866118685760408051606080820183526000808352602080840182905292840181905283519182018452808252918101829052918201526118aa565b611877896115ba60018461258c565b604080516060810182526001830154600f81810b8352600160801b909104810b602083015260029093015490920b908201525b9450866118b85760006118d1565b6118c7896115ba60018461258c565b5465ffffffffffff165b9550505050509250925092565b60408051606081018252600080825260208201819052918101919091526001600160a01b0383166000908152600385016020526040812081908190611923908661179e565b92509250925082611938579250610a38915050565b600061194b8365ffffffffffff1661113b565b9050600061195c6303c267006110f5565b61196e9065ffffffffffff84166126ac565b90505b8665ffffffffffff168265ffffffffffff161015611a905761199662093a80836126c4565b915060008765ffffffffffff168365ffffffffffff1611156119ba578792506119ed565b506001600160a01b038816600090815260058a016020908152604080832065ffffffffffff86168452909152902054600f0b5b80600f0b600014611a7457611a0561075e86856126ee565b8460200151611a1491906125a3565b84518590611a2390839061264a565b600f0b905250602084018051829190611a3d9083906124ce565b600f90810b90915285516000910b12159050611a5857600084525b60008460200151600f0b1215611a7057600060208501525b8294505b818365ffffffffffff161115611a8a5750611a90565b50611971565b60006110ab61075e868a6126ee565b6000828281548110611ab357611ab3612748565b9060005260206000209060030201905092915050565b6040805160608101825260008082526020820181905291810191909152604080516060810182526000808252602082018190529181019190915284548015611cb6576000611b1c876115ba60018561258c565b604080518082018252825465ffffffffffff908116825282516060810184526001850154600f81810b8352600160801b909104810b602083810191909152600290960154900b9381019390935292810191909152805190925087821691161115611b9957604051638553be6160e01b815260040160405180910390fd5b805165ffffffffffff808816911603611c0c5784611bbc886115ba60018661258c565b815160208301516001600160801b03918216600160801b91831691909102176001830155604090920151600290910180546fffffffffffffffffffffffffffffffff191691909216179055611ca6565b60408051808201825265ffffffffffff888116825260208083018981528b5460018082018e5560008e815284902095516003909202909501805465ffffffffffff191691909416178355518051918101516001600160801b03928316600160801b9184169190910217938301939093559190920151600290920180546fffffffffffffffffffffffffffffffff1916929091169190911790555b602001519250839150610e689050565b60408051808201825265ffffffffffff878116825260208083018881528a5460018082018d5560008d815284902095516003909202909501805465ffffffffffff191691909416178355518051918101516001600160801b03928316600160801b9184169190910217938301939093559190920151600290920180546fffffffffffffffffffffffffffffffff191692909116919091179055611d87604080516060808201835260008083526020808401829052928401819052835191820184528082529181018290529182015290565b849250925050610e68565b600081600003611da457506000919050565b60006001611db184611f41565b901c6001901b90506001818481611dca57611dca61252f565b048201901c90506001818481611de257611de261252f565b048201901c90506001818481611dfa57611dfa61252f565b048201901c90506001818481611e1257611e1261252f565b048201901c90506001818481611e2a57611e2a61252f565b048201901c90506001818481611e4257611e4261252f565b048201901c90506001818481611e5a57611e5a61252f565b048201901c9050610a3881828581611e7457611e7461252f565b04611fd5565b60005b81831015611ed4576000611e918484611feb565b60008781526020902090915065ffffffffffff86169082015465ffffffffffff161115611ec057809250611ece565b611ecb8160016126ac565b93505b50611e7d565b509392505050565b60008061168a858585612006565b60005b81831015611ed4576000611f018484611feb565b90508465ffffffffffff16611f168783611a9f565b5465ffffffffffff161115611f2d57809250611f3b565b611f388160016126ac565b93505b50611eed565b600080608083901c15611f5657608092831c92015b604083901c15611f6857604092831c92015b602083901c15611f7a57602092831c92015b601083901c15611f8c57601092831c92015b600883901c15611f9e57600892831c92015b600483901c15611fb057600492831c92015b600283901c15611fc257600292831c92015b600183901c1561031d5760010192915050565b6000818310611fe45781610a38565b5090919050565b6000611ffa6002848418612715565b610a38908484166126ac565b8254600090819080156121715760006120248761121160018561258c565b60408051808201909152905465ffffffffffff80821680845266010000000000009092046001600160a01b03166020840152919250908716101561207b57604051638553be6160e01b815260040160405180910390fd5b805165ffffffffffff8088169116036120e4578461209e8861121160018661258c565b80546001600160a01b03929092166601000000000000027fffffffffffff0000000000000000000000000000000000000000ffffffffffff909216919091179055611ca6565b6040805180820190915265ffffffffffff80881682526001600160a01b0380881660208085019182528b54600181018d5560008d81529190912094519401805491519092166601000000000000027fffffffffffff00000000000000000000000000000000000000000000000000009091169390921692909217179055602001519250839150610e689050565b50506040805180820190915265ffffffffffff80851682526001600160a01b0380851660208085019182528854600181018a5560008a815291822095519501805492519093166601000000000000027fffffffffffff0000000000000000000000000000000000000000000000000000909216949093169390931792909217909155905081610e68565b803565ffffffffffff8116811461221157600080fd5b919050565b6000806040838503121561222957600080fd5b82359150612239602084016121fb565b90509250929050565b8035600f81900b811461221157600080fd5b60008060008060008060c0878903121561226d57600080fd5b863595506020870135945061228460408801612242565b935061229260608801612242565b92506080870135915060a087013590509295509295509295565b6000606082840312156122be57600080fd5b6040516060810181811067ffffffffffffffff821117156122ef57634e487b7160e01b600052604160045260246000fd5b6040529050806122fe83612242565b815261230c60208401612242565b602082015261231d60408401612242565b60408201525092915050565b600080600080610100858703121561234057600080fd5b843593506020850135925061235886604087016122ac565b91506123678660a087016122ac565b905092959194509250565b60006020828403121561238457600080fd5b5035919050565b6000806000606084860312156123a057600080fd5b83359250602084013591506123b7604085016121fb565b90509250925092565b80356001600160a01b038116811461221157600080fd5b600080600080608085870312156123ed57600080fd5b8435935060208501359250612404604086016123c0565b9396929550929360600135925050565b6000806040838503121561242757600080fd5b82359150612239602084016123c0565b60008060006060848603121561244c57600080fd5b505081359360208301359350604090920135919050565b60008060006060848603121561247857600080fd5b83359250612488602085016123c0565b91506123b7604085016121fb565b600080604083850312156124a957600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b600081600f0b83600f0b60008212826f7fffffffffffffffffffffffffffffff03821381151615612501576125016124b8565b826f7fffffffffffffffffffffffffffffff19038212811615612526576125266124b8565b50019392505050565b634e487b7160e01b600052601260045260246000fd5b600081600f0b83600f0b8061255c5761255c61252f565b6f7fffffffffffffffffffffffffffffff19821460001982141615612583576125836124b8565b90059392505050565b60008282101561259e5761259e6124b8565b500390565b600081600f0b83600f0b6f7fffffffffffffffffffffffffffffff6000821360008413838304851182821616156125dc576125dc6124b8565b6f7fffffffffffffffffffffffffffffff196000851286820586128184161615612608576126086124b8565b60008712925085820587128484161615612624576126246124b8565b8585058712818416161561263a5761263a6124b8565b5050509290910295945050505050565b600081600f0b83600f0b60008112816f7fffffffffffffffffffffffffffffff190183128115161561267e5761267e6124b8565b816f7fffffffffffffffffffffffffffffff0183138116156126a2576126a26124b8565b5090039392505050565b600082198211156126bf576126bf6124b8565b500190565b600065ffffffffffff8083168185168083038211156126e5576126e56124b8565b01949350505050565b600065ffffffffffff8381169083168181101561270d5761270d6124b8565b039392505050565b6000826127245761272461252f565b500490565b6000816000190483118215151615612743576127436124b8565b500290565b634e487b7160e01b600052603260045260246000fdfea2646970667358221220748bcf20bdfca2e5f378cebfccd87187ad8ad45e09b55f275f355693caa63a9764736f6c634300080d0033

Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

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

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.