ETH Price: $3,043.54 (+2.02%)
 

Overview

ETH Balance

12.72816381011584763 ETH

Eth Value

$38,738.73 (@ $3,043.54/ETH)

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Submit Bid240230502025-12-16 5:22:3517 days ago1765862555IN
Aztec: Continuous Clearing Auction
0.00046753 ETH0.000001260.05
Submit Bid239710452025-12-08 22:25:4724 days ago1765232747IN
Aztec: Continuous Clearing Auction
0.00660189 ETH0.000013470.53106978
Claim Tokens239621132025-12-07 16:20:4725 days ago1765124447IN
Aztec: Continuous Clearing Auction
0 ETH0.000110091.42967733
Exit Bid239621102025-12-07 16:20:1125 days ago1765124411IN
Aztec: Continuous Clearing Auction
0 ETH0.000118461.40442713
Claim Tokens239613302025-12-07 13:43:2325 days ago1765115003IN
Aztec: Continuous Clearing Auction
0 ETH0.000040070.52037247
Exit Bid239613242025-12-07 13:42:1125 days ago1765114931IN
Aztec: Continuous Clearing Auction
0 ETH0.000044520.52784872
Claim Tokens239581602025-12-07 3:03:5926 days ago1765076639IN
Aztec: Continuous Clearing Auction
0 ETH0.000138342.30920275
Exit Bid239581582025-12-07 3:03:3526 days ago1765076615IN
Aztec: Continuous Clearing Auction
0 ETH0.000191212.26691816
Claim Tokens239581532025-12-07 3:02:3526 days ago1765076555IN
Aztec: Continuous Clearing Auction
0 ETH0.000176552.29258707
Exit Bid239581472025-12-07 3:01:2326 days ago1765076483IN
Aztec: Continuous Clearing Auction
0 ETH0.000191422.26945277
Submit Bid239563742025-12-06 21:02:4726 days ago1765054967IN
Aztec: Continuous Clearing Auction
0.02131964 ETH0.000000550.021903
Exit Partially F...239563012025-12-06 20:48:1126 days ago1765054091IN
Aztec: Continuous Clearing Auction
0 ETH0.000148793.02844925
Sweep Unsold Tok...239559842025-12-06 19:44:1126 days ago1765050251IN
Aztec: Continuous Clearing Auction
0 ETH0.000002430.02772142
Checkpoint239559812025-12-06 19:43:3526 days ago1765050215IN
Aztec: Continuous Clearing Auction
0 ETH0.000000820.02315248
Sweep Currency239558442025-12-06 19:15:5926 days ago1765048559IN
Aztec: Continuous Clearing Auction
0 ETH0.000026461.03294055
Claim Tokens239556272025-12-06 18:32:1126 days ago1765045931IN
Aztec: Continuous Clearing Auction
0 ETH0.000002140.02780909
Exit Bid239556262025-12-06 18:31:5926 days ago1765045919IN
Aztec: Continuous Clearing Auction
0 ETH0.000002370.02820332
Exit Bid239556202025-12-06 18:30:4726 days ago1765045847IN
Aztec: Continuous Clearing Auction
0 ETH0.000002320.0275604
Claim Tokens239555372025-12-06 18:14:1126 days ago1765044851IN
Aztec: Continuous Clearing Auction
0 ETH0.000000830.02756163
Claim Tokens239554872025-12-06 18:03:5926 days ago1765044239IN
Aztec: Continuous Clearing Auction
0 ETH0.000002170.02822141
On Tokens Receiv...239554832025-12-06 18:03:1126 days ago1765044191IN
Aztec: Continuous Clearing Auction
0 ETH0.000000690.02977708
Exit Bid239554702025-12-06 18:00:2326 days ago1765044023IN
Aztec: Continuous Clearing Auction
0 ETH0.000002510.02979096
Exit Partially F...239552892025-12-06 17:24:1126 days ago1765041851IN
Aztec: Continuous Clearing Auction
0 ETH0.00001450.12697473
Exit Partially F...239552842025-12-06 17:22:4726 days ago1765041767IN
Aztec: Continuous Clearing Auction
0 ETH0.000003290.02817284
Sweep Currency239552802025-12-06 17:21:5926 days ago1765041719IN
Aztec: Continuous Clearing Auction
0 ETH0.000120082.03366293
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer241326442025-12-31 12:35:3547 hrs ago1767184535
Aztec: Continuous Clearing Auction
0.00029654 ETH
Transfer241306272025-12-31 5:49:472 days ago1767160187
Aztec: Continuous Clearing Auction
0.00063617 ETH
Transfer241306242025-12-31 5:49:112 days ago1767160151
Aztec: Continuous Clearing Auction
0.00063381 ETH
Transfer241306152025-12-31 5:47:232 days ago1767160043
Aztec: Continuous Clearing Auction
0.00064311 ETH
Transfer241306132025-12-31 5:46:592 days ago1767160019
Aztec: Continuous Clearing Auction
0.00062312 ETH
Transfer241306122025-12-31 5:46:472 days ago1767160007
Aztec: Continuous Clearing Auction
0.00064853 ETH
Transfer241306122025-12-31 5:46:472 days ago1767160007
Aztec: Continuous Clearing Auction
0.0006452 ETH
Transfer241306102025-12-31 5:46:232 days ago1767159983
Aztec: Continuous Clearing Auction
0.00063569 ETH
Transfer241306092025-12-31 5:46:112 days ago1767159971
Aztec: Continuous Clearing Auction
0.00065918 ETH
Transfer241306092025-12-31 5:46:112 days ago1767159971
Aztec: Continuous Clearing Auction
0.00065902 ETH
Transfer241306092025-12-31 5:46:112 days ago1767159971
Aztec: Continuous Clearing Auction
0.00065107 ETH
Transfer241271122025-12-30 18:03:112 days ago1767117791
Aztec: Continuous Clearing Auction
0.00103476 ETH
Transfer241269092025-12-30 17:21:592 days ago1767115319
Aztec: Continuous Clearing Auction
0.0014431 ETH
Transfer241265392025-12-30 16:07:592 days ago1767110879
Aztec: Continuous Clearing Auction
0.00076275 ETH
Transfer241265182025-12-30 16:03:472 days ago1767110627
Aztec: Continuous Clearing Auction
0.00273207 ETH
Transfer241264152025-12-30 15:43:112 days ago1767109391
Aztec: Continuous Clearing Auction
0.00249862 ETH
Transfer241264152025-12-30 15:43:112 days ago1767109391
Aztec: Continuous Clearing Auction
0.00259552 ETH
Transfer241264062025-12-30 15:41:232 days ago1767109283
Aztec: Continuous Clearing Auction
0.00247209 ETH
Transfer241264012025-12-30 15:40:232 days ago1767109223
Aztec: Continuous Clearing Auction
0.0024965 ETH
Transfer241263992025-12-30 15:39:592 days ago1767109199
Aztec: Continuous Clearing Auction
0.00246791 ETH
Transfer241251962025-12-30 11:37:593 days ago1767094679
Aztec: Continuous Clearing Auction
0.06302497 ETH
Transfer241210892025-12-29 21:49:113 days ago1767044951
Aztec: Continuous Clearing Auction
0.0261575 ETH
Transfer241210612025-12-29 21:43:353 days ago1767044615
Aztec: Continuous Clearing Auction
0.02715833 ETH
Transfer241210082025-12-29 21:32:593 days ago1767043979
Aztec: Continuous Clearing Auction
0.02724187 ETH
Transfer241209602025-12-29 21:23:113 days ago1767043391
Aztec: Continuous Clearing Auction
0.0263959 ETH
View All Internal Transactions
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ContinuousClearingAuction

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 88888888 runs

Other Settings:
cancun EvmVersion, MIT license
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {BidStorage} from './BidStorage.sol';
import {Checkpoint, CheckpointStorage} from './CheckpointStorage.sol';
import {StepStorage} from './StepStorage.sol';
import {Tick, TickStorage} from './TickStorage.sol';
import {TokenCurrencyStorage} from './TokenCurrencyStorage.sol';
import {AuctionParameters, IContinuousClearingAuction} from './interfaces/IContinuousClearingAuction.sol';
import {IValidationHook} from './interfaces/IValidationHook.sol';
import {IDistributionContract} from './interfaces/external/IDistributionContract.sol';
import {Bid, BidLib} from './libraries/BidLib.sol';
import {CheckpointLib} from './libraries/CheckpointLib.sol';
import {ConstantsLib} from './libraries/ConstantsLib.sol';
import {Currency, CurrencyLibrary} from './libraries/CurrencyLibrary.sol';
import {FixedPoint96} from './libraries/FixedPoint96.sol';
import {MaxBidPriceLib} from './libraries/MaxBidPriceLib.sol';
import {AuctionStep, StepLib} from './libraries/StepLib.sol';
import {ValidationHookLib} from './libraries/ValidationHookLib.sol';
import {ValueX7, ValueX7Lib} from './libraries/ValueX7Lib.sol';
import {FixedPointMathLib} from 'solady/utils/FixedPointMathLib.sol';
import {SafeTransferLib} from 'solady/utils/SafeTransferLib.sol';

/// @title ContinuousClearingAuction
/// @custom:security-contact [email protected]
/// @notice Implements a time weighted uniform clearing price auction
/// @dev Can be constructed directly or through the ContinuousClearingAuctionFactory. In either case, users must validate
///      that the auction parameters are correct and not incorrectly set.
contract ContinuousClearingAuction is
    BidStorage,
    CheckpointStorage,
    StepStorage,
    TickStorage,
    TokenCurrencyStorage,
    IContinuousClearingAuction
{
    using FixedPointMathLib for *;
    using CurrencyLibrary for Currency;
    using BidLib for *;
    using StepLib for *;
    using CheckpointLib for Checkpoint;
    using ValidationHookLib for IValidationHook;
    using ValueX7Lib for *;

    /// @notice The maximum price which a bid can be submitted at
    /// @dev Set during construction to type(uint256).max / TOTAL_SUPPLY
    uint256 public immutable MAX_BID_PRICE;
    /// @notice The block at which purchased tokens can be claimed
    uint64 internal immutable CLAIM_BLOCK;
    /// @notice An optional hook to be called before a bid is registered
    IValidationHook internal immutable VALIDATION_HOOK;

    /// @notice The total currency raised in the auction in Q96 representation, scaled up by X7
    ValueX7 internal $currencyRaisedQ96_X7;
    /// @notice The total tokens sold in the auction so far, in Q96 representation, scaled up by X7
    ValueX7 internal $totalClearedQ96_X7;
    /// @notice The sum of currency demand in ticks above the clearing price
    /// @dev This will increase every time a new bid is submitted, and decrease when bids are outbid.
    uint256 internal $sumCurrencyDemandAboveClearingQ96;
    /// @notice Whether the TOTAL_SUPPLY of tokens has been received
    bool private $_tokensReceived;

    constructor(address _token, uint128 _totalSupply, AuctionParameters memory _parameters)
        StepStorage(_parameters.auctionStepsData, _parameters.startBlock, _parameters.endBlock)
        TokenCurrencyStorage(
            _token,
            _parameters.currency,
            _totalSupply,
            _parameters.tokensRecipient,
            _parameters.fundsRecipient,
            _parameters.requiredCurrencyRaised
        )
        TickStorage(_parameters.tickSpacing, _parameters.floorPrice)
    {
        CLAIM_BLOCK = _parameters.claimBlock;
        VALIDATION_HOOK = IValidationHook(_parameters.validationHook);

        if (CLAIM_BLOCK < END_BLOCK) revert ClaimBlockIsBeforeEndBlock();

        // See MaxBidPriceLib library for more details on the bid price calculations.
        MAX_BID_PRICE = MaxBidPriceLib.maxBidPrice(TOTAL_SUPPLY);
        // The floor price and tick spacing must allow for at least one tick above the floor price to be initialized
        if (_parameters.tickSpacing > MAX_BID_PRICE || _parameters.floorPrice > MAX_BID_PRICE - _parameters.tickSpacing)
        {
            revert FloorPriceAndTickSpacingGreaterThanMaxBidPrice(
                _parameters.floorPrice + _parameters.tickSpacing, MAX_BID_PRICE
            );
        }
    }

    /// @notice Modifier for functions which can only be called after the auction is over
    modifier onlyAfterAuctionIsOver() {
        if (block.number < END_BLOCK) revert AuctionIsNotOver();
        _;
    }

    /// @notice Modifier for claim related functions which can only be called after the claim block
    modifier onlyAfterClaimBlock() {
        if (block.number < CLAIM_BLOCK) revert NotClaimable();
        _;
    }

    /// @notice Modifier for functions which can only be called after the auction is started and the tokens have been received
    modifier onlyActiveAuction() {
        _onlyActiveAuction();
        _;
    }

    /// @notice Internal function to check if the auction is active
    /// @dev Submitting bids or checkpointing is not allowed unless the auction is active
    function _onlyActiveAuction() internal view {
        if (block.number < START_BLOCK) revert AuctionNotStarted();
        if (!$_tokensReceived) revert TokensNotReceived();
    }

    /// @notice Modifier for functions which require the latest checkpoint to be up to date
    modifier ensureEndBlockIsCheckpointed() {
        if ($lastCheckpointedBlock != END_BLOCK) {
            checkpoint();
        }
        _;
    }

    /// @inheritdoc IDistributionContract
    function onTokensReceived() external {
        // Don't check balance or emit the TokensReceived event if the tokens have already been received
        if ($_tokensReceived) return;
        // Use the normal totalSupply value instead of the Q96 value
        if (TOKEN.balanceOf(address(this)) < TOTAL_SUPPLY) {
            revert InvalidTokenAmountReceived();
        }
        $_tokensReceived = true;
        emit TokensReceived(TOTAL_SUPPLY);
    }

    /// @inheritdoc IContinuousClearingAuction
    function isGraduated() external view returns (bool) {
        return _isGraduated();
    }

    /// @notice Whether the auction has graduated as of the given checkpoint
    /// @dev The auction is considered `graudated` if the currency raised is greater than or equal to the required currency raised
    function _isGraduated() internal view returns (bool) {
        return ValueX7.unwrap($currencyRaisedQ96_X7) >= ValueX7.unwrap(REQUIRED_CURRENCY_RAISED_Q96_X7);
    }

    /// @inheritdoc IContinuousClearingAuction
    function currencyRaised() external view returns (uint256) {
        return _currencyRaised();
    }

    /// @notice Return the currency raised in uint256 representation
    /// @return The currency raised
    function _currencyRaised() internal view returns (uint256) {
        return $currencyRaisedQ96_X7.divUint256(FixedPoint96.Q96).scaleDownToUint256();
    }

    /// @notice Return a new checkpoint after advancing the current checkpoint by some `mps`
    ///         This function updates the cumulative values of the checkpoint, and
    ///         requires that the clearing price is up to date
    /// @param _checkpoint The checkpoint to sell tokens at its clearing price
    /// @param deltaMps The number of mps to sell
    /// @return The checkpoint with all cumulative values updated
    function _sellTokensAtClearingPrice(Checkpoint memory _checkpoint, uint24 deltaMps)
        internal
        returns (Checkpoint memory)
    {
        // Advance the auction by selling an additional `deltaMps` share of TOTAL_SUPPLY at the current clearing price.
        //
        // Units and scaling:
        // - Prices: Q96 (× 2^96)
        // - Demand: Q96
        // - deltaMps: X7 (milli-basis points, 1e7 = 100%)
        // - Currency/Token flows in this function: Q96*X7 (demand or currency × deltaMps)
        //
        // Algorithm overview:
        // 1) Assume all demand is strictly above the clearing price: contribution = sumAboveClearingQ96 × deltaMps.
        // 2) If the clearing price is exactly on an initialized tick that has demand, account for the partially filled
        //    bids at the clearing tick. There are two ways to derive the at-clearing contribution when the price is
        //    not rounded up:
        //       (A) total implied currency at the rounded-up price − contribution from above-clearing
        //       (B) tick demand at clearing × deltaMps
        //    If the clearing price was rounded up to the tick boundary, (A) can exceed (B); cap with min(A, B).

        uint256 priceQ96 = _checkpoint.clearingPrice;
        uint256 deltaMpsU = uint256(deltaMps);
        uint256 sumAboveQ96 = $sumCurrencyDemandAboveClearingQ96;

        // Base case: demand strictly above the clearing price only
        // Most bids are strictly above clearing; contribution = (Q96 demand above clearing) × (X7 delta)
        uint256 currencyFromAboveQ96X7;
        unchecked {
            currencyFromAboveQ96X7 = sumAboveQ96 * deltaMpsU;
        }

        // Special case: clearing price equals a tick with demand (partially filled tick)
        // Bidders at that tick can be partially filled over this increment. Split into:
        // - (1) above-clearing contribution (already computed) and
        // - (2) at-clearing contribution.
        if (priceQ96 % TICK_SPACING == 0) {
            uint256 demandAtPriceQ96 = _getTick(priceQ96).currencyDemandQ96;
            if (demandAtPriceQ96 > 0) {
                uint256 currencyRaisedAboveClearingQ96X7 = currencyFromAboveQ96X7;

                // (A) Total implied currency at the (rounded-up) clearing price for this delta:
                //     TOTAL_SUPPLY × priceQ96 (Q96) × deltaMps (X7) = Q96*X7
                //     Note: on a tick boundary we use the rounded-up clearing price, which can slightly overestimate.
                uint256 totalCurrencyForDeltaQ96X7;
                unchecked {
                    totalCurrencyForDeltaQ96X7 = (uint256(TOTAL_SUPPLY) * priceQ96) * deltaMpsU;
                }

                // Portion attributable to the clearing tick by subtraction: A − above-clearing
                uint256 demandAtClearingQ96X7 = totalCurrencyForDeltaQ96X7 - currencyRaisedAboveClearingQ96X7;

                // (B) Expected currency from bids at the clearing tick, scaling the tick demand by deltaMps
                uint256 expectedAtClearingTickQ96X7;
                unchecked {
                    expectedAtClearingTickQ96X7 = demandAtPriceQ96 * deltaMpsU;
                }

                // If price was rounded up, (A) can exceed (B). In that case, at-clearing contribution is bounded by actual
                // tick demand; take min((A), (B)). If the price was not rounded up, (A) == (B).
                uint256 currencyAtClearingTickQ96X7 =
                    FixedPointMathLib.min(demandAtClearingQ96X7, expectedAtClearingTickQ96X7);

                // Actual currency raised across this delta = above-clearing + at-clearing
                currencyFromAboveQ96X7 = currencyAtClearingTickQ96X7 + currencyRaisedAboveClearingQ96X7;
                // Track cumulative currency raised exactly at this clearing price (used for partial exits)
                _checkpoint.currencyRaisedAtClearingPriceQ96_X7 = ValueX7.wrap(
                    ValueX7.unwrap(_checkpoint.currencyRaisedAtClearingPriceQ96_X7) + currencyAtClearingTickQ96X7
                );
            }
        }

        // Convert currency to tokens at price, rounding up, and update global cleared tokens.
        // Intentional round-up leaves a small amount of dust to sweep, ensuring cleared tokens never exceed TOTAL_SUPPLY
        // even when using rounded-up clearing prices on tick boundaries.
        uint256 tokensClearedQ96X7 = currencyFromAboveQ96X7.fullMulDivUp(FixedPoint96.Q96, priceQ96);
        $totalClearedQ96_X7 = ValueX7.wrap(ValueX7.unwrap($totalClearedQ96_X7) + tokensClearedQ96X7);
        // Update global currency raised
        $currencyRaisedQ96_X7 = ValueX7.wrap(ValueX7.unwrap($currencyRaisedQ96_X7) + currencyFromAboveQ96X7);

        _checkpoint.cumulativeMps += deltaMps;
        // Harmonic-mean accumulator: add (mps / price) using the rounded-up clearing price for this increment
        _checkpoint.cumulativeMpsPerPrice += CheckpointLib.getMpsPerPrice(deltaMps, priceQ96);
        return _checkpoint;
    }

    /// @notice Fast forward to the start of the current step and return the number of `mps` sold since the last checkpoint
    /// @param _blockNumber The current block number
    /// @param _lastCheckpointedBlock The block number of the last checkpointed block
    /// @return step The current step in the auction which contains `_blockNumber`
    /// @return deltaMps The number of `mps` sold between the last checkpointed block and the start of the current step
    function _advanceToStartOfCurrentStep(uint64 _blockNumber, uint64 _lastCheckpointedBlock)
        internal
        returns (AuctionStep memory step, uint24 deltaMps)
    {
        // Advance the current step until the current block is within the step
        // Start at the larger of the last checkpointed block or the start block of the current step
        step = $step;
        uint64 start = uint64(FixedPointMathLib.max(step.startBlock, _lastCheckpointedBlock));
        uint64 end = step.endBlock;

        uint24 mps = step.mps;
        while (_blockNumber > end) {
            uint64 blockDelta = end - start;
            unchecked {
                deltaMps += uint24(blockDelta * mps);
            }
            start = end;
            if (end == END_BLOCK) break;
            step = _advanceStep();
            mps = step.mps;
            end = step.endBlock;
        }
    }

    /// @notice Iterate to find the tick where the total demand at and above it is strictly less than the remaining supply in the auction
    /// @dev If the loop reaches the highest tick in the book, `nextActiveTickPrice` will be set to MAX_TICK_PTR
    /// @param _checkpoint The latest checkpoint
    /// @return The new clearing price
    function _iterateOverTicksAndFindClearingPrice(Checkpoint memory _checkpoint) internal returns (uint256) {
        // The clearing price can never be lower than the last checkpoint.
        // If the clearing price is zero, set it to the floor price
        uint256 minimumClearingPrice = _checkpoint.clearingPrice.coalesce(FLOOR_PRICE);
        // If there are no more remaining mps in the auction, we don't need to iterate over ticks
        // and we can return the minimum clearing price above
        if (_checkpoint.remainingMpsInAuction() == 0) {
            return minimumClearingPrice;
        }

        // Place state variables on the stack to save gas
        bool updateStateVariables;
        uint256 sumCurrencyDemandAboveClearingQ96_ = $sumCurrencyDemandAboveClearingQ96;
        uint256 nextActiveTickPrice_ = $nextActiveTickPrice;

        /**
         * We have the current demand above the clearing price, and we want to see if it is enough to fully purchase
         * all of the remaining supply being sold at the nextActiveTickPrice. We only need to check `nextActiveTickPrice`
         * because we know that there are no bids in between the current clearing price and that price.
         *
         * Observe that we need a certain amount of collective demand to increase the auction from the floor price.
         * - This is equal to `totalSupply * floorPrice`
         *
         * If the auction was fully subscribed in the first block which it was active, then the total CURRENCY REQUIRED
         * at any given price is equal to totalSupply * p', where p' is that price.
         */
        uint256 clearingPrice = sumCurrencyDemandAboveClearingQ96_.divUp(TOTAL_SUPPLY);
        while (
            // Loop while the currency amount above the clearing price is greater than the required currency at `nextActiveTickPrice_`
            (nextActiveTickPrice_ != MAX_TICK_PTR
                    && sumCurrencyDemandAboveClearingQ96_ >= TOTAL_SUPPLY * nextActiveTickPrice_)
                // If the demand above clearing rounds up to the `nextActiveTickPrice`, we need to keep iterating over ticks
                // This ensures that the `nextActiveTickPrice` is always the next initialized tick strictly above the clearing price
                || clearingPrice == nextActiveTickPrice_
        ) {
            Tick storage $nextActiveTick = _getTick(nextActiveTickPrice_);
            // Subtract the demand at the current nextActiveTick from the total demand
            sumCurrencyDemandAboveClearingQ96_ -= $nextActiveTick.currencyDemandQ96;
            // Save the previous next active tick price
            minimumClearingPrice = nextActiveTickPrice_;
            // Advance to the next tick
            nextActiveTickPrice_ = $nextActiveTick.next;
            clearingPrice = sumCurrencyDemandAboveClearingQ96_.divUp(TOTAL_SUPPLY);
            updateStateVariables = true;
        }
        // Set the values into storage if we found a new next active tick price
        if (updateStateVariables) {
            $sumCurrencyDemandAboveClearingQ96 = sumCurrencyDemandAboveClearingQ96_;
            $nextActiveTickPrice = nextActiveTickPrice_;
            emit NextActiveTickUpdated(nextActiveTickPrice_);
        }

        // The minimum clearing price is either the floor price or the last tick we iterated over.
        // With the exception of the first iteration, the minimum price is a lower bound on the clearing price
        // because we already verified that we had enough demand to purchase all of the remaining supply at that price.
        if (clearingPrice < minimumClearingPrice) {
            return minimumClearingPrice;
        }
        // Otherwise, return the calculated clearing price
        else {
            return clearingPrice;
        }
    }

    /// @notice Internal function for checkpointing at a specific block number
    /// @dev This updates the state of the auction accounting for the bids placed after the last checkpoint
    ///      Checkpoints are created at the top of each block with a new bid and does NOT include that bid
    ///      Because of this, we need to calculate what the new state of the Auction should be before updating
    ///      purely on the supply we will sell to the potentially updated `sumCurrencyDemandAboveClearingQ96` value
    /// @param blockNumber The block number to checkpoint at
    function _checkpointAtBlock(uint64 blockNumber) internal returns (Checkpoint memory _checkpoint) {
        uint64 lastCheckpointedBlock = $lastCheckpointedBlock;
        if (blockNumber == lastCheckpointedBlock) return latestCheckpoint();

        _checkpoint = latestCheckpoint();
        uint256 clearingPrice = _iterateOverTicksAndFindClearingPrice(_checkpoint);
        if (clearingPrice != _checkpoint.clearingPrice) {
            // Set the new clearing price
            _checkpoint.clearingPrice = clearingPrice;
            // Reset the currencyRaisedAtClearingPrice to zero since the clearing price has changed
            _checkpoint.currencyRaisedAtClearingPriceQ96_X7 = ValueX7.wrap(0);
            emit ClearingPriceUpdated(blockNumber, clearingPrice);
        }

        // Calculate the percentage of the supply that has been sold since the last checkpoint and the start of the current step
        (AuctionStep memory step, uint24 deltaMps) = _advanceToStartOfCurrentStep(blockNumber, lastCheckpointedBlock);
        // `deltaMps` above is equal to the percentage of tokens sold up until the start of the current step.
        // If the last checkpointed block is more recent than the start of the current step, account for the percentage
        // sold since the last checkpointed block. Otherwise, add the percent sold since the start of the current step.
        uint64 blockDelta = blockNumber - uint64(FixedPointMathLib.max(step.startBlock, lastCheckpointedBlock));
        unchecked {
            deltaMps += uint24(blockDelta * step.mps);
        }

        // Sell the percentage of outstanding tokens since the last checkpoint at the current clearing price
        _checkpoint = _sellTokensAtClearingPrice(_checkpoint, deltaMps);
        // Insert the checkpoint into storage, updating latest pointer and the linked list
        _insertCheckpoint(_checkpoint, blockNumber);

        emit CheckpointUpdated(blockNumber, _checkpoint.clearingPrice, _checkpoint.cumulativeMps);
    }

    /// @notice Return the final checkpoint of the auction
    /// @dev Only called when the auction is over. Changes the current state of the `step` to the final step in the auction
    ///      any future calls to `step.mps` will return the mps of the last step in the auction
    function _getFinalCheckpoint() internal returns (Checkpoint memory) {
        return _checkpointAtBlock(END_BLOCK);
    }

    /// @notice Internal function for bid submission
    /// @dev Validates `maxPrice`, calls the validation hook (if set) and updates global state variables
    ///      For gas efficiency, `prevTickPrice` should be the price of the tick immediately before `maxPrice`.
    /// @dev Does not check that the actual value `amount` was received by the contract
    /// @return bidId The id of the created bid
    function _submitBid(uint256 maxPrice, uint128 amount, address owner, uint256 prevTickPrice, bytes calldata hookData)
        internal
        returns (uint256 bidId)
    {
        // Reject bids which would cause TOTAL_SUPPLY * maxPrice to overflow a uint256
        if (maxPrice > MAX_BID_PRICE) revert InvalidBidPriceTooHigh(maxPrice, MAX_BID_PRICE);

        // Get the latest checkpoint before validating the bid
        Checkpoint memory _checkpoint = checkpoint();
        // Revert if there are no more tokens to be sold
        if (_checkpoint.remainingMpsInAuction() == 0) revert AuctionSoldOut();
        // We don't allow bids to be submitted at or below the clearing price
        if (maxPrice <= _checkpoint.clearingPrice) revert BidMustBeAboveClearingPrice();

        // Initialize the tick if needed. This will no-op if the tick is already initialized.
        _initializeTickIfNeeded(prevTickPrice, maxPrice);

        // Call the validation hook and bubble up the revert reason if it reverts
        VALIDATION_HOOK.handleValidate(maxPrice, amount, owner, msg.sender, hookData);

        Bid memory bid;
        uint256 amountQ96 = uint256(amount) << FixedPoint96.RESOLUTION;
        (bid, bidId) = _createBid(amountQ96, owner, maxPrice, _checkpoint.cumulativeMps);

        // Scale the amount according to the rest of the supply schedule, accounting for past blocks
        // This is only used in demand related internal calculations
        uint256 bidEffectiveAmountQ96 = bid.toEffectiveAmount();
        // Update the tick demand with the bid's scaled amount
        _updateTickDemand(maxPrice, bidEffectiveAmountQ96);
        // Update the global sum of currency demand above the clearing price tracker
        // Per the validation checks above this bid must be above the clearing price
        $sumCurrencyDemandAboveClearingQ96 += bidEffectiveAmountQ96;

        // If the sum of demand above clearing price becomes large enough to overflow a multiplication an X7 value,
        // revert to prevent the bid from being submitted.
        if ($sumCurrencyDemandAboveClearingQ96 >= ConstantsLib.X7_UPPER_BOUND) {
            revert InvalidBidUnableToClear();
        }

        emit BidSubmitted(bidId, owner, maxPrice, amount);
    }

    /// @notice Internal function for processing the exit of a bid
    /// @dev Given a bid, tokens filled and refund, process the transfers and refund
    ///      `exitedBlock` MUST be checked by the caller to prevent double spending
    /// @param bidId The id of the bid to exit
    /// @param tokensFilled The number of tokens filled
    /// @param currencySpentQ96 The amount of currency the bid spent
    function _processExit(uint256 bidId, uint256 tokensFilled, uint256 currencySpentQ96) internal {
        Bid storage $bid = _getBid(bidId);
        address _owner = $bid.owner;

        uint256 refund = ($bid.amountQ96 - currencySpentQ96) >> FixedPoint96.RESOLUTION;

        $bid.tokensFilled = tokensFilled;
        $bid.exitedBlock = uint64(block.number);

        if (refund > 0) {
            CURRENCY.transfer(_owner, refund);
        }

        emit BidExited(bidId, _owner, tokensFilled, refund);
    }

    /// @inheritdoc IContinuousClearingAuction
    function checkpoint() public onlyActiveAuction returns (Checkpoint memory) {
        if (block.number > END_BLOCK) {
            return _getFinalCheckpoint();
        } else {
            return _checkpointAtBlock(uint64(block.number));
        }
    }

    /// @inheritdoc IContinuousClearingAuction
    /// @dev Bids can be submitted anytime between the startBlock and the endBlock.
    function submitBid(uint256 maxPrice, uint128 amount, address owner, uint256 prevTickPrice, bytes calldata hookData)
        public
        payable
        onlyActiveAuction
        returns (uint256)
    {
        // Bids cannot be submitted at the endBlock or after
        if (block.number >= END_BLOCK) revert AuctionIsOver();
        if (amount == 0) revert BidAmountTooSmall();
        if (owner == address(0)) revert BidOwnerCannotBeZeroAddress();
        if (CURRENCY.isAddressZero()) {
            if (msg.value != amount) revert InvalidAmount();
        } else {
            if (msg.value != 0) revert CurrencyIsNotNative();
            SafeTransferLib.permit2TransferFrom(Currency.unwrap(CURRENCY), msg.sender, address(this), amount);
        }
        return _submitBid(maxPrice, amount, owner, prevTickPrice, hookData);
    }

    /// @inheritdoc IContinuousClearingAuction
    /// @dev The call to `submitBid` checks `onlyActiveAuction` so it's not required on this function
    function submitBid(uint256 maxPrice, uint128 amount, address owner, bytes calldata hookData)
        external
        payable
        returns (uint256)
    {
        return submitBid(maxPrice, amount, owner, FLOOR_PRICE, hookData);
    }

    /// @inheritdoc IContinuousClearingAuction
    function exitBid(uint256 bidId) external onlyAfterAuctionIsOver {
        Bid memory bid = _getBid(bidId);
        if (bid.exitedBlock != 0) revert BidAlreadyExited();
        Checkpoint memory finalCheckpoint = _getFinalCheckpoint();
        if (!_isGraduated()) {
            // In the case that the auction did not graduate, fully refund the bid
            return _processExit(bidId, 0, 0);
        }
        // Only bids with a max price strictly above the final clearing price can be exited via `exitBid`
        if (bid.maxPrice <= finalCheckpoint.clearingPrice) revert CannotExitBid();

        // Account for the fully filled checkpoints
        Checkpoint memory startCheckpoint = _getCheckpoint(bid.startBlock);
        (uint256 tokensFilled, uint256 currencySpentQ96) =
            _accountFullyFilledCheckpoints(finalCheckpoint, startCheckpoint, bid);

        _processExit(bidId, tokensFilled, currencySpentQ96);
    }

    /// @inheritdoc IContinuousClearingAuction
    function exitPartiallyFilledBid(uint256 bidId, uint64 lastFullyFilledCheckpointBlock, uint64 outbidBlock) external {
        // Checkpoint before checking any of the hints because they could depend on the latest checkpoint
        Checkpoint memory currentBlockCheckpoint = checkpoint();

        Bid memory bid = _getBid(bidId);
        if (bid.exitedBlock != 0) revert BidAlreadyExited();

        // Prevent bids from being exited before graduation
        if (!_isGraduated()) {
            if (block.number >= END_BLOCK) {
                // If the auction is over, fully refund the bid
                return _processExit(bidId, 0, 0);
            }
            revert CannotPartiallyExitBidBeforeGraduation();
        }

        uint256 bidMaxPrice = bid.maxPrice;
        uint64 bidStartBlock = bid.startBlock;

        // Get the last fully filled checkpoint from the user's provided hint
        Checkpoint memory lastFullyFilledCheckpoint = _getCheckpoint(lastFullyFilledCheckpointBlock);
        // Since `lastFullyFilledCheckpointBlock` points to the last fully filled Checkpoint, it must be < bid.maxPrice
        // The next Checkpoint after `lastFullyFilledCheckpoint` must be partially or fully filled (clearingPrice >= bid.maxPrice)
        // `lastFullyFilledCheckpoint` also cannot be before the bid's startCheckpoint
        if (
            lastFullyFilledCheckpoint.clearingPrice >= bidMaxPrice
                || _getCheckpoint(lastFullyFilledCheckpoint.next).clearingPrice < bidMaxPrice
                || lastFullyFilledCheckpointBlock < bidStartBlock
        ) {
            revert InvalidLastFullyFilledCheckpointHint();
        }

        // There is guaranteed to be a checkpoint at the bid's startBlock because we always checkpoint before bid submission
        Checkpoint memory startCheckpoint = _getCheckpoint(bidStartBlock);

        // Initialize the tokens filled and currency spent trackers
        uint256 tokensFilled;
        uint256 currencySpentQ96;

        // If the lastFullyFilledCheckpoint is provided, account for the fully filled checkpoints
        if (lastFullyFilledCheckpoint.clearingPrice > 0) {
            // Assign the calculated tokens filled and currency spent to `tokensFilled` and `currencySpentQ96`
            (tokensFilled, currencySpentQ96) =
                _accountFullyFilledCheckpoints(lastFullyFilledCheckpoint, startCheckpoint, bid);
        }

        // Upper checkpoint is the last checkpoint where the bid is partially filled
        Checkpoint memory upperCheckpoint;
        // If outbidBlock is not zero, the bid was outbid and the bidder is requesting an early exit
        // This can be done before the auction's endBlock
        if (outbidBlock != 0) {
            // If the provided hint is the current block, use the checkpoint returned by `checkpoint()` instead of getting it from storage
            Checkpoint memory outbidCheckpoint;
            if (outbidBlock == block.number) {
                outbidCheckpoint = currentBlockCheckpoint;
            } else {
                outbidCheckpoint = _getCheckpoint(outbidBlock);
            }

            upperCheckpoint = _getCheckpoint(outbidCheckpoint.prev);
            // We require that the outbid checkpoint is > bid max price AND the checkpoint before it is <= bid max price, revert if either of these conditions are not met
            if (outbidCheckpoint.clearingPrice <= bidMaxPrice || upperCheckpoint.clearingPrice > bidMaxPrice) {
                revert InvalidOutbidBlockCheckpointHint();
            }
        } else {
            // The only other partially exitable case is if the auction ends with the clearing price equal to the bid's max price
            // These bids can only be exited after the auction ends
            if (block.number < END_BLOCK) revert CannotPartiallyExitBidBeforeEndBlock();
            // Set the upper checkpoint to the checkpoint returned when we initially called `checkpoint()`
            // This must be the final checkpoint because `checkpoint()` will return the final checkpoint after the auction is over
            upperCheckpoint = currentBlockCheckpoint;
            // Revert if the final checkpoint's clearing price is not equal to the bid's max price
            if (upperCheckpoint.clearingPrice != bidMaxPrice) {
                revert CannotExitBid();
            }
        }

        // If there is an `upperCheckpoint` that means that the bid had a period where it was partially filled
        // From the logic above, `upperCheckpoint` now points to the last checkpoint where the clearingPrice == bidMaxPrice.
        // Because the clearing price can never decrease between checkpoints, and the fact that you cannot enter a bid
        // at or below the current clearing price, the bid MUST have been active during the entire partial fill period.
        // And `upperCheckpoint` tracks the cumulative currency raised at that clearing price since the first partially filled checkpoint.
        if (upperCheckpoint.clearingPrice == bidMaxPrice) {
            uint256 tickDemandQ96 = _getTick(bidMaxPrice).currencyDemandQ96;
            (uint256 partialTokensFilled, uint256 partialCurrencySpentQ96) = _accountPartiallyFilledCheckpoints(
                bid, tickDemandQ96, upperCheckpoint.currencyRaisedAtClearingPriceQ96_X7
            );
            // Add the tokensFilled and currencySpentQ96 from the partially filled checkpoints to the total
            tokensFilled += partialTokensFilled;
            currencySpentQ96 += partialCurrencySpentQ96;
        }

        _processExit(bidId, tokensFilled, currencySpentQ96);
    }

    /// @inheritdoc IContinuousClearingAuction
    function claimTokens(uint256 _bidId) external onlyAfterClaimBlock ensureEndBlockIsCheckpointed {
        // Tokens cannot be claimed if the auction did not graduate
        if (!_isGraduated()) revert NotGraduated();

        (address owner, uint256 tokensFilled) = _internalClaimTokens(_bidId);

        if (tokensFilled > 0) {
            Currency.wrap(address(TOKEN)).transfer(owner, tokensFilled);
            emit TokensClaimed(_bidId, owner, tokensFilled);
        }
    }

    /// @inheritdoc IContinuousClearingAuction
    function claimTokensBatch(address _owner, uint256[] calldata _bidIds)
        external
        onlyAfterClaimBlock
        ensureEndBlockIsCheckpointed
    {
        // Tokens cannot be claimed if the auction did not graduate
        if (!_isGraduated()) revert NotGraduated();

        uint256 tokensFilled = 0;
        for (uint256 i = 0; i < _bidIds.length; i++) {
            (address bidOwner, uint256 bidTokensFilled) = _internalClaimTokens(_bidIds[i]);

            if (bidOwner != _owner) {
                revert BatchClaimDifferentOwner(_owner, bidOwner);
            }

            tokensFilled += bidTokensFilled;

            if (bidTokensFilled > 0) {
                emit TokensClaimed(_bidIds[i], bidOwner, bidTokensFilled);
            }
        }

        if (tokensFilled > 0) {
            Currency.wrap(address(TOKEN)).transfer(_owner, tokensFilled);
        }
    }

    /// @notice Internal function to claim tokens for a single bid
    /// @param bidId The id of the bid
    /// @return owner The owner of the bid
    /// @return tokensFilled The amount of tokens filled
    function _internalClaimTokens(uint256 bidId) internal returns (address owner, uint256 tokensFilled) {
        Bid storage $bid = _getBid(bidId);
        if ($bid.exitedBlock == 0) revert BidNotExited();

        // Set return values
        owner = $bid.owner;
        tokensFilled = $bid.tokensFilled;

        // Set the tokens filled to 0
        $bid.tokensFilled = 0;
    }

    /// @inheritdoc IContinuousClearingAuction
    function sweepCurrency() external onlyAfterAuctionIsOver ensureEndBlockIsCheckpointed {
        // Cannot sweep if already swept
        if (sweepCurrencyBlock != 0) revert CannotSweepCurrency();
        // Cannot sweep currency if the auction has not graduated, as all of the Currency must be refunded
        if (!_isGraduated()) revert NotGraduated();
        _sweepCurrency(_currencyRaised());
    }

    /// @inheritdoc IContinuousClearingAuction
    function sweepUnsoldTokens() external onlyAfterAuctionIsOver ensureEndBlockIsCheckpointed {
        if (sweepUnsoldTokensBlock != 0) revert CannotSweepTokens();
        uint256 unsoldTokens;
        if (_isGraduated()) {
            unsoldTokens = TOTAL_SUPPLY_Q96.scaleUpToX7().sub($totalClearedQ96_X7).divUint256(FixedPoint96.Q96)
                .scaleDownToUint256();
        } else {
            unsoldTokens = TOTAL_SUPPLY;
        }
        _sweepUnsoldTokens(unsoldTokens);
    }

    // Getters
    /// @inheritdoc IContinuousClearingAuction
    function claimBlock() external view returns (uint64) {
        return CLAIM_BLOCK;
    }

    /// @inheritdoc IContinuousClearingAuction
    function validationHook() external view returns (IValidationHook) {
        return VALIDATION_HOOK;
    }

    /// @inheritdoc IContinuousClearingAuction
    function currencyRaisedQ96_X7() external view returns (ValueX7) {
        return $currencyRaisedQ96_X7;
    }

    /// @inheritdoc IContinuousClearingAuction
    function sumCurrencyDemandAboveClearingQ96() external view returns (uint256) {
        return $sumCurrencyDemandAboveClearingQ96;
    }

    /// @inheritdoc IContinuousClearingAuction
    function totalClearedQ96_X7() external view returns (ValueX7) {
        return $totalClearedQ96_X7;
    }

    /// @inheritdoc IContinuousClearingAuction
    function totalCleared() external view returns (uint256) {
        return $totalClearedQ96_X7.divUint256(FixedPoint96.Q96).scaleDownToUint256();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {IBidStorage} from './interfaces/IBidStorage.sol';
import {Bid} from './libraries/BidLib.sol';

/// @notice Abstract contract for managing bid storage
abstract contract BidStorage is IBidStorage {
    /// @notice The id of the next bid to be created
    uint256 private $_nextBidId;
    /// @notice The mapping of bid ids to bids
    mapping(uint256 bidId => Bid bid) private $_bids;

    /// @notice Get a bid from storage
    /// @param bidId The id of the bid to get
    /// @return bid The bid
    function _getBid(uint256 bidId) internal view returns (Bid storage) {
        if (bidId >= $_nextBidId) revert BidIdDoesNotExist(bidId);
        return $_bids[bidId];
    }

    /// @notice Create a new bid
    /// @param amount The amount of the bid
    /// @param owner The owner of the bid
    /// @param maxPrice The maximum price for the bid
    /// @param startCumulativeMps The cumulative mps at the start of the bid
    /// @return bid The created bid
    /// @return bidId The id of the created bid
    function _createBid(uint256 amount, address owner, uint256 maxPrice, uint24 startCumulativeMps)
        internal
        returns (Bid memory bid, uint256 bidId)
    {
        bid = Bid({
            startBlock: uint64(block.number),
            startCumulativeMps: startCumulativeMps,
            exitedBlock: 0,
            maxPrice: maxPrice,
            amountQ96: amount,
            owner: owner,
            tokensFilled: 0
        });

        bidId = $_nextBidId;
        $_bids[bidId] = bid;
        $_nextBidId++;
    }

    /// Getters
    /// @inheritdoc IBidStorage
    function nextBidId() external view returns (uint256) {
        return $_nextBidId;
    }

    /// @inheritdoc IBidStorage
    function bids(uint256 bidId) external view returns (Bid memory) {
        return _getBid(bidId);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {ICheckpointStorage} from './interfaces/ICheckpointStorage.sol';
import {Bid, BidLib} from './libraries/BidLib.sol';
import {CheckpointAccountingLib} from './libraries/CheckpointAccountingLib.sol';
import {Checkpoint, CheckpointLib} from './libraries/CheckpointLib.sol';
import {FixedPoint96} from './libraries/FixedPoint96.sol';
import {ValueX7, ValueX7Lib} from './libraries/ValueX7Lib.sol';

/// @title CheckpointStorage
/// @notice Abstract contract for managing auction checkpoints and bid fill calculations
abstract contract CheckpointStorage is ICheckpointStorage {
    /// @notice Maximum block number value used as sentinel for last checkpoint
    uint64 public constant MAX_BLOCK_NUMBER = type(uint64).max;

    /// @notice Storage of checkpoints
    mapping(uint64 blockNumber => Checkpoint) private $_checkpoints;
    /// @notice The block number of the last checkpointed block
    uint64 internal $lastCheckpointedBlock;

    /// @inheritdoc ICheckpointStorage
    function latestCheckpoint() public view returns (Checkpoint memory) {
        return _getCheckpoint($lastCheckpointedBlock);
    }

    /// @inheritdoc ICheckpointStorage
    function clearingPrice() external view returns (uint256) {
        return _getCheckpoint($lastCheckpointedBlock).clearingPrice;
    }

    /// @notice Get a checkpoint from storage
    function _getCheckpoint(uint64 blockNumber) internal view returns (Checkpoint memory) {
        return $_checkpoints[blockNumber];
    }

    /// @notice Insert a checkpoint into storage
    /// @dev This function updates the prev and next pointers of the latest checkpoint and the new checkpoint
    function _insertCheckpoint(Checkpoint memory checkpoint, uint64 blockNumber) internal {
        uint64 _lastCheckpointedBlock = $lastCheckpointedBlock;
        // Enforce strictly increasing checkpoint block numbers
        if (blockNumber <= _lastCheckpointedBlock) revert CheckpointBlockNotIncreasing();
        // Link new checkpoint to the previous checkpoint
        checkpoint.prev = _lastCheckpointedBlock;
        checkpoint.next = MAX_BLOCK_NUMBER;
        // Link previous checkpoint to the new checkpoint
        $_checkpoints[_lastCheckpointedBlock].next = blockNumber;
        // Write the new checkpoint
        $_checkpoints[blockNumber] = checkpoint;
        // Update the last checkpointed block
        $lastCheckpointedBlock = blockNumber;
    }

    /// @notice Calculate the tokens sold and proportion of input used for a fully filled bid between two checkpoints
    /// @dev This function MUST only be used for checkpoints where the bid's max price is strictly greater than the clearing price
    ///      because it uses lazy accounting to calculate the tokens filled
    /// @param upper The upper checkpoint
    /// @param startCheckpoint The start checkpoint of the bid
    /// @param bid The bid
    /// @return tokensFilled The tokens sold
    /// @return currencySpentQ96 The amount of currency spent in Q96 form
    function _accountFullyFilledCheckpoints(Checkpoint memory upper, Checkpoint memory startCheckpoint, Bid memory bid)
        internal
        pure
        returns (uint256 tokensFilled, uint256 currencySpentQ96)
    {
        return CheckpointAccountingLib.accountFullyFilledCheckpoints(upper, startCheckpoint, bid);
    }

    /// @notice Calculate the tokens sold and currency spent for a partially filled bid
    /// @param bid The bid
    /// @param tickDemandQ96 The total demand at the tick
    /// @param currencyRaisedAtClearingPriceQ96_X7 The cumulative supply sold to the clearing price
    /// @return tokensFilled The tokens sold
    /// @return currencySpentQ96 The amount of currency spent in Q96 form
    function _accountPartiallyFilledCheckpoints(
        Bid memory bid,
        uint256 tickDemandQ96,
        ValueX7 currencyRaisedAtClearingPriceQ96_X7
    ) internal pure returns (uint256 tokensFilled, uint256 currencySpentQ96) {
        return CheckpointAccountingLib.accountPartiallyFilledCheckpoints(
            bid, tickDemandQ96, currencyRaisedAtClearingPriceQ96_X7
        );
    }

    /// @inheritdoc ICheckpointStorage
    function lastCheckpointedBlock() external view returns (uint64) {
        return $lastCheckpointedBlock;
    }

    /// @inheritdoc ICheckpointStorage
    function checkpoints(uint64 blockNumber) external view returns (Checkpoint memory) {
        return $_checkpoints[blockNumber];
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {IStepStorage} from './interfaces/IStepStorage.sol';
import {ConstantsLib} from './libraries/ConstantsLib.sol';
import {AuctionStep, StepLib} from './libraries/StepLib.sol';
import {SSTORE2} from 'solady/utils/SSTORE2.sol';

/// @title StepStorage
/// @notice Abstract contract to store and read information about the auction issuance schedule
abstract contract StepStorage is IStepStorage {
    using StepLib for *;
    using SSTORE2 for *;

    /// @notice The block at which the auction starts
    uint64 internal immutable START_BLOCK;
    /// @notice The block at which the auction ends
    uint64 internal immutable END_BLOCK;
    /// @notice Cached length of the auction steps data provided in the constructor
    uint256 internal immutable _LENGTH;

    /// @notice The address pointer to the contract deployed by SSTORE2
    address private immutable $_pointer;
    /// @notice The word offset of the last read step in `auctionStepsData` bytes
    uint256 private $_offset;
    /// @notice The current active auction step
    AuctionStep internal $step;

    constructor(bytes memory _auctionStepsData, uint64 _startBlock, uint64 _endBlock) {
        if (_startBlock >= _endBlock) revert InvalidEndBlock();
        START_BLOCK = _startBlock;
        END_BLOCK = _endBlock;
        _LENGTH = _auctionStepsData.length;

        address _pointer = _auctionStepsData.write();
        _validate(_pointer);
        $_pointer = _pointer;

        _advanceStep();
    }

    /// @notice Validate the data provided in the constructor
    /// @dev Checks that the contract was correctly deployed by SSTORE2 and that the total mps and blocks are valid
    function _validate(address _pointer) internal view {
        bytes memory _auctionStepsData = _pointer.read();
        if (
            _auctionStepsData.length == 0 || _auctionStepsData.length % StepLib.UINT64_SIZE != 0
                || _auctionStepsData.length != _LENGTH
        ) revert InvalidAuctionDataLength();

        // Loop through the auction steps data and check if the mps is valid
        uint256 sumMps = 0;
        uint64 sumBlockDelta = 0;
        for (uint256 i = 0; i < _LENGTH; i += StepLib.UINT64_SIZE) {
            (uint24 mps, uint40 blockDelta) = _auctionStepsData.get(i);
            // Prevent the block delta from being set to zero
            if (blockDelta == 0) revert StepBlockDeltaCannotBeZero();
            sumMps += mps * blockDelta;
            sumBlockDelta += blockDelta;
        }
        if (sumMps != ConstantsLib.MPS) revert InvalidStepDataMps(sumMps, ConstantsLib.MPS);
        uint64 calculatedEndBlock = START_BLOCK + sumBlockDelta;
        if (calculatedEndBlock != END_BLOCK) revert InvalidEndBlockGivenStepData(calculatedEndBlock, END_BLOCK);
    }

    /// @notice Advance the current auction step
    /// @dev This function is called on every new bid if the current step is complete
    function _advanceStep() internal returns (AuctionStep memory) {
        if ($_offset >= _LENGTH) revert AuctionIsOver();

        bytes8 _auctionStep = bytes8($_pointer.read($_offset, $_offset + StepLib.UINT64_SIZE));
        (uint24 mps, uint40 blockDelta) = _auctionStep.parse();

        uint64 _startBlock = $step.endBlock;
        if (_startBlock == 0) _startBlock = START_BLOCK;
        uint64 _endBlock = _startBlock + uint64(blockDelta);

        $step = AuctionStep({startBlock: _startBlock, endBlock: _endBlock, mps: mps});

        $_offset += StepLib.UINT64_SIZE;

        emit AuctionStepRecorded(_startBlock, _endBlock, mps);
        return $step;
    }

    /// @inheritdoc IStepStorage
    function step() external view returns (AuctionStep memory) {
        return $step;
    }

    // Getters
    /// @inheritdoc IStepStorage
    function startBlock() external view returns (uint64) {
        return START_BLOCK;
    }

    /// @inheritdoc IStepStorage
    function endBlock() external view returns (uint64) {
        return END_BLOCK;
    }

    /// @inheritdoc IStepStorage
    function pointer() external view returns (address) {
        return $_pointer;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {ITickStorage, Tick} from './interfaces/ITickStorage.sol';
import {ConstantsLib} from './libraries/ConstantsLib.sol';

/// @title TickStorage
/// @notice Abstract contract for handling tick storage
abstract contract TickStorage is ITickStorage {
    /// @notice Mapping of price levels to tick data
    mapping(uint256 price => Tick) private $_ticks;

    /// @notice The price of the next initialized tick above the clearing price
    /// @dev This will be equal to the clearingPrice if no ticks have been initialized yet
    uint256 internal $nextActiveTickPrice;
    /// @notice The floor price of the auction
    uint256 internal immutable FLOOR_PRICE;
    /// @notice The tick spacing of the auction - bids must be placed at discrete tick intervals
    uint256 internal immutable TICK_SPACING;

    /// @notice Sentinel value for the next pointer of the highest tick in the book
    uint256 public constant MAX_TICK_PTR = type(uint256).max;

    constructor(uint256 _tickSpacing, uint256 _floorPrice) {
        if (_tickSpacing < ConstantsLib.MIN_TICK_SPACING) revert TickSpacingTooSmall();
        TICK_SPACING = _tickSpacing;
        if (_floorPrice == 0) revert FloorPriceIsZero();
        if (_floorPrice < ConstantsLib.MIN_FLOOR_PRICE) revert FloorPriceTooLow();
        FLOOR_PRICE = _floorPrice;
        // Initialize the floor price as the first tick
        // _getTick will validate that it is also at a tick boundary
        _getTick(FLOOR_PRICE).next = MAX_TICK_PTR;
        $nextActiveTickPrice = MAX_TICK_PTR;
        emit NextActiveTickUpdated(MAX_TICK_PTR);
        emit TickInitialized(FLOOR_PRICE);
    }

    /// @notice Internal function to get a tick at a price
    /// @dev The returned tick is not guaranteed to be initialized
    function _getTick(uint256 price) internal view returns (Tick storage) {
        // Validate `price` is at a boundary designated by the tick spacing
        if (price % TICK_SPACING != 0) revert TickPriceNotAtBoundary();
        return $_ticks[price];
    }

    /// @notice Initialize a tick at `price` if it does not exist already
    /// @dev `prevPrice` MUST be the price of an initialized tick before the new price.
    ///      Ideally, it is the price of the tick immediately preceding the desired price. If not,
    ///      we will iterate through the ticks until we find the next price which requires more gas.
    ///      If `price` is < `nextActiveTickPrice`, then `price` will be set as the nextActiveTickPrice
    /// @param prevPrice The price of the previous tick
    /// @param price The price of the tick
    function _initializeTickIfNeeded(uint256 prevPrice, uint256 price) internal {
        if (price == MAX_TICK_PTR) revert InvalidTickPrice();
        // _getTick will validate that `price` is at a boundary designated by the tick spacing
        Tick storage $newTick = _getTick(price);
        // Early return if the tick is already initialized
        if ($newTick.next != 0) return;
        // Otherwise, we need to iterate through the linked list to find the correct position for the new tick
        // Require that `prevPrice` is less than `price` since we can only iterate forward
        if (prevPrice >= price) revert TickPreviousPriceInvalid();
        uint256 nextPrice = _getTick(prevPrice).next;
        // Revert if the next price is 0 as that means the `prevPrice` hint was not an initialized tick
        if (nextPrice == 0) revert TickPreviousPriceInvalid();
        // Move the `prevPrice` pointer up until its next pointer is a tick greater than or equal to `price`
        // If `price` would be the highest tick in the list, this will iterate until `nextPrice` == MAX_TICK_PTR,
        // which will end the loop since we don't allow for ticks to be initialized at MAX_TICK_PTR.
        // Iterating to find the tick right before `price` ensures that it is correctly positioned in the linked list.
        while (nextPrice < price) {
            prevPrice = nextPrice;
            nextPrice = _getTick(nextPrice).next;
        }
        // Update linked list pointers
        $newTick.next = nextPrice;
        _getTick(prevPrice).next = price;
        // If the next tick is the nextActiveTick, update nextActiveTick to the new tick
        // In the base case, where next == 0 and nextActiveTickPrice == 0, this will set nextActiveTickPrice to price
        if (nextPrice == $nextActiveTickPrice) {
            $nextActiveTickPrice = price;
            emit NextActiveTickUpdated(price);
        }

        emit TickInitialized(price);
    }

    /// @notice Internal function to add demand to a tick
    /// @param price The price of the tick
    /// @param currencyDemandQ96 The demand to add
    function _updateTickDemand(uint256 price, uint256 currencyDemandQ96) internal {
        Tick storage $tick = _getTick(price);
        if ($tick.next == 0) revert CannotUpdateUninitializedTick();
        $tick.currencyDemandQ96 += currencyDemandQ96;
    }

    // Getters
    /// @inheritdoc ITickStorage
    function floorPrice() external view returns (uint256) {
        return FLOOR_PRICE;
    }

    /// @inheritdoc ITickStorage
    function tickSpacing() external view returns (uint256) {
        return TICK_SPACING;
    }

    /// @inheritdoc ITickStorage
    function nextActiveTickPrice() external view returns (uint256) {
        return $nextActiveTickPrice;
    }

    /// @inheritdoc ITickStorage
    function ticks(uint256 price) external view returns (Tick memory) {
        return _getTick(price);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {ITokenCurrencyStorage} from './interfaces/ITokenCurrencyStorage.sol';
import {IERC20Minimal} from './interfaces/external/IERC20Minimal.sol';
import {ConstantsLib} from './libraries/ConstantsLib.sol';
import {Currency, CurrencyLibrary} from './libraries/CurrencyLibrary.sol';
import {FixedPoint96} from './libraries/FixedPoint96.sol';
import {ValueX7, ValueX7Lib} from './libraries/ValueX7Lib.sol';

/// @title TokenCurrencyStorage
abstract contract TokenCurrencyStorage is ITokenCurrencyStorage {
    using ValueX7Lib for *;
    using CurrencyLibrary for Currency;

    /// @notice The currency being raised in the auction
    Currency internal immutable CURRENCY;
    /// @notice The token being sold in the auction
    IERC20Minimal internal immutable TOKEN;
    /// @notice The total supply of tokens to sell
    uint128 internal immutable TOTAL_SUPPLY;
    /// @notice The total supply of tokens to sell in 160.96 form
    uint256 internal immutable TOTAL_SUPPLY_Q96;
    /// @notice The recipient of any unsold tokens at the end of the auction
    address internal immutable TOKENS_RECIPIENT;
    /// @notice The recipient of the raised Currency from the auction
    address internal immutable FUNDS_RECIPIENT;
    /// @notice The amount of currency required to be raised for the auction
    ///         to graduate in Q96 form, scaled up by X7
    ValueX7 internal immutable REQUIRED_CURRENCY_RAISED_Q96_X7;

    /// @notice The block at which the currency was swept
    uint256 public sweepCurrencyBlock;
    /// @notice The block at which the tokens were swept
    uint256 public sweepUnsoldTokensBlock;

    constructor(
        address _token,
        address _currency,
        uint128 _totalSupply,
        address _tokensRecipient,
        address _fundsRecipient,
        uint128 _requiredCurrencyRaised
    ) {
        if (_token == address(0)) revert TokenIsAddressZero();
        if (_token == _currency) revert TokenAndCurrencyCannotBeTheSame();
        if (_totalSupply == 0) revert TotalSupplyIsZero();
        if (_totalSupply > ConstantsLib.MAX_TOTAL_SUPPLY) revert TotalSupplyIsTooLarge();
        if (_tokensRecipient == address(0)) revert TokensRecipientIsZero();
        if (_fundsRecipient == address(0)) revert FundsRecipientIsZero();

        TOKEN = IERC20Minimal(_token);
        CURRENCY = Currency.wrap(_currency);
        TOTAL_SUPPLY = _totalSupply;
        TOTAL_SUPPLY_Q96 = uint256(_totalSupply) << FixedPoint96.RESOLUTION;
        TOKENS_RECIPIENT = _tokensRecipient;
        FUNDS_RECIPIENT = _fundsRecipient;
        REQUIRED_CURRENCY_RAISED_Q96_X7 = (uint256(_requiredCurrencyRaised) << FixedPoint96.RESOLUTION).scaleUpToX7();
    }

    function _sweepCurrency(uint256 amount) internal {
        sweepCurrencyBlock = block.number;
        if (amount > 0) {
            CURRENCY.transfer(FUNDS_RECIPIENT, amount);
        }
        emit CurrencySwept(FUNDS_RECIPIENT, amount);
    }

    function _sweepUnsoldTokens(uint256 amount) internal {
        sweepUnsoldTokensBlock = block.number;
        if (amount > 0) {
            Currency.wrap(address(TOKEN)).transfer(TOKENS_RECIPIENT, amount);
        }
        emit TokensSwept(TOKENS_RECIPIENT, amount);
    }

    // Getters
    /// @inheritdoc ITokenCurrencyStorage
    function currency() external view returns (Currency) {
        return CURRENCY;
    }

    /// @inheritdoc ITokenCurrencyStorage
    function token() external view returns (IERC20Minimal) {
        return TOKEN;
    }

    /// @inheritdoc ITokenCurrencyStorage
    function totalSupply() external view returns (uint128) {
        return TOTAL_SUPPLY;
    }

    /// @inheritdoc ITokenCurrencyStorage
    function tokensRecipient() external view returns (address) {
        return TOKENS_RECIPIENT;
    }

    /// @inheritdoc ITokenCurrencyStorage
    function fundsRecipient() external view returns (address) {
        return FUNDS_RECIPIENT;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Checkpoint} from '../libraries/CheckpointLib.sol';
import {ValueX7} from '../libraries/ValueX7Lib.sol';
import {IBidStorage} from './IBidStorage.sol';
import {ICheckpointStorage} from './ICheckpointStorage.sol';
import {IStepStorage} from './IStepStorage.sol';
import {ITickStorage} from './ITickStorage.sol';
import {ITokenCurrencyStorage} from './ITokenCurrencyStorage.sol';
import {IValidationHook} from './IValidationHook.sol';
import {IDistributionContract} from './external/IDistributionContract.sol';

/// @notice Parameters for the auction
/// @dev token and totalSupply are passed as constructor arguments
struct AuctionParameters {
    address currency; // token to raise funds in. Use address(0) for ETH
    address tokensRecipient; // address to receive leftover tokens
    address fundsRecipient; // address to receive all raised funds
    uint64 startBlock; // Block which the first step starts
    uint64 endBlock; // When the auction finishes
    uint64 claimBlock; // Block when the auction can claimed
    uint256 tickSpacing; // Fixed granularity for prices
    address validationHook; // Optional hook called before a bid
    uint256 floorPrice; // Starting floor price for the auction
    uint128 requiredCurrencyRaised; // Amount of currency required to be raised for the auction to graduate
    bytes auctionStepsData; // Packed bytes describing token issuance schedule
}

/// @notice Interface for the ContinuousClearingAuction contract
interface IContinuousClearingAuction is
    IDistributionContract,
    ICheckpointStorage,
    ITickStorage,
    IStepStorage,
    ITokenCurrencyStorage,
    IBidStorage
{
    /// @notice Error thrown when the amount received is invalid
    error InvalidTokenAmountReceived();

    /// @notice Error thrown when an invalid value is deposited
    error InvalidAmount();
    /// @notice Error thrown when the bid owner is the zero address
    error BidOwnerCannotBeZeroAddress();
    /// @notice Error thrown when the bid price is below the clearing price
    error BidMustBeAboveClearingPrice();
    /// @notice Error thrown when the bid price is too high given the auction's total supply
    /// @param maxPrice The price of the bid
    /// @param maxBidPrice The max price allowed for a bid
    error InvalidBidPriceTooHigh(uint256 maxPrice, uint256 maxBidPrice);
    /// @notice Error thrown when the bid amount is too small
    error BidAmountTooSmall();
    /// @notice Error thrown when msg.value is non zero when currency is not ETH
    error CurrencyIsNotNative();
    /// @notice Error thrown when the auction is not started
    error AuctionNotStarted();
    /// @notice Error thrown when the tokens required for the auction have not been received
    error TokensNotReceived();
    /// @notice Error thrown when the claim block is before the end block
    error ClaimBlockIsBeforeEndBlock();
    /// @notice Error thrown when the floor price plus tick spacing is greater than the maximum bid price
    error FloorPriceAndTickSpacingGreaterThanMaxBidPrice(uint256 nextTick, uint256 maxBidPrice);
    /// @notice Error thrown when the floor price plus tick spacing would overflow a uint256
    error FloorPriceAndTickSpacingTooLarge();
    /// @notice Error thrown when the bid has already been exited
    error BidAlreadyExited();
    /// @notice Error thrown when the bid is higher than the clearing price
    error CannotExitBid();
    /// @notice Error thrown when the bid cannot be partially exited before the end block
    error CannotPartiallyExitBidBeforeEndBlock();
    /// @notice Error thrown when the last fully filled checkpoint hint is invalid
    error InvalidLastFullyFilledCheckpointHint();
    /// @notice Error thrown when the outbid block checkpoint hint is invalid
    error InvalidOutbidBlockCheckpointHint();
    /// @notice Error thrown when the bid is not claimable
    error NotClaimable();
    /// @notice Error thrown when the bids are not owned by the same owner
    error BatchClaimDifferentOwner(address expectedOwner, address receivedOwner);
    /// @notice Error thrown when the bid has not been exited
    error BidNotExited();
    /// @notice Error thrown when the bid cannot be partially exited before the auction has graduated
    error CannotPartiallyExitBidBeforeGraduation();
    /// @notice Error thrown when the token transfer fails
    error TokenTransferFailed();
    /// @notice Error thrown when the auction is not over
    error AuctionIsNotOver();
    /// @notice Error thrown when the bid is too large
    error InvalidBidUnableToClear();
    /// @notice Error thrown when the auction has sold the entire total supply of tokens
    error AuctionSoldOut();

    /// @notice Emitted when the tokens are received
    /// @param totalSupply The total supply of tokens received
    event TokensReceived(uint256 totalSupply);

    /// @notice Emitted when a bid is submitted
    /// @param id The id of the bid
    /// @param owner The owner of the bid
    /// @param price The price of the bid
    /// @param amount The amount of the bid
    event BidSubmitted(uint256 indexed id, address indexed owner, uint256 price, uint128 amount);

    /// @notice Emitted when a new checkpoint is created
    /// @param blockNumber The block number of the checkpoint
    /// @param clearingPrice The clearing price of the checkpoint
    /// @param cumulativeMps The cumulative percentage of total tokens allocated across all previous steps, represented in ten-millionths of the total supply (1e7 = 100%)
    event CheckpointUpdated(uint256 blockNumber, uint256 clearingPrice, uint24 cumulativeMps);

    /// @notice Emitted when the clearing price is updated
    /// @param blockNumber The block number when the clearing price was updated
    /// @param clearingPrice The new clearing price
    event ClearingPriceUpdated(uint256 blockNumber, uint256 clearingPrice);

    /// @notice Emitted when a bid is exited
    /// @param bidId The id of the bid
    /// @param owner The owner of the bid
    /// @param tokensFilled The amount of tokens filled
    /// @param currencyRefunded The amount of currency refunded
    event BidExited(uint256 indexed bidId, address indexed owner, uint256 tokensFilled, uint256 currencyRefunded);

    /// @notice Emitted when a bid is claimed
    /// @param bidId The id of the bid
    /// @param owner The owner of the bid
    /// @param tokensFilled The amount of tokens claimed
    event TokensClaimed(uint256 indexed bidId, address indexed owner, uint256 tokensFilled);

    /// @notice Submit a new bid
    /// @param maxPrice The maximum price the bidder is willing to pay
    /// @param amount The amount of the bid
    /// @param owner The owner of the bid
    /// @param prevTickPrice The price of the previous tick
    /// @param hookData Additional data to pass to the hook required for validation
    /// @return bidId The id of the bid
    function submitBid(uint256 maxPrice, uint128 amount, address owner, uint256 prevTickPrice, bytes calldata hookData)
        external
        payable
        returns (uint256 bidId);

    /// @notice Submit a new bid without specifying the previous tick price
    /// @dev It is NOT recommended to use this function unless you are sure that `maxPrice` is already initialized
    ///      as this function will iterate through every tick starting from the floor price if it is not.
    /// @param maxPrice The maximum price the bidder is willing to pay
    /// @param amount The amount of the bid
    /// @param owner The owner of the bid
    /// @param hookData Additional data to pass to the hook required for validation
    /// @return bidId The id of the bid
    function submitBid(uint256 maxPrice, uint128 amount, address owner, bytes calldata hookData)
        external
        payable
        returns (uint256 bidId);

    /// @notice Register a new checkpoint
    /// @dev This function is called every time a new bid is submitted above the current clearing price
    /// @dev If the auction is over, it returns the final checkpoint
    /// @return _checkpoint The checkpoint at the current block
    function checkpoint() external returns (Checkpoint memory _checkpoint);

    /// @notice Whether the auction has graduated as of the given checkpoint
    /// @dev The auction is considered graduated if the currency raised is greater than or equal to the required currency raised
    /// @dev Be aware that the latest checkpoint may be out of date
    /// @return bool True if the auction has graduated, false otherwise
    function isGraduated() external view returns (bool);

    /// @notice Get the currency raised at the last checkpointed block
    /// @dev This may be less than the balance of this contract if there are outstanding refunds for bidders
    /// @dev Be aware that the latest checkpoint may be out of date
    /// @return The currency raised
    function currencyRaised() external view returns (uint256);

    /// @notice Exit a bid
    /// @dev This function can only be used for bids where the max price is above the final clearing price after the auction has ended
    /// @param bidId The id of the bid
    function exitBid(uint256 bidId) external;

    /// @notice Exit a bid which has been partially filled
    /// @dev This function can be used only for partially filled bids. For fully filled bids, `exitBid` must be used
    /// @param bidId The id of the bid
    /// @param lastFullyFilledCheckpointBlock The last checkpointed block where the clearing price is strictly < bid.maxPrice
    /// @param outbidBlock The first checkpointed block where the clearing price is strictly > bid.maxPrice, or 0 if the bid is partially filled at the end of the auction
    function exitPartiallyFilledBid(uint256 bidId, uint64 lastFullyFilledCheckpointBlock, uint64 outbidBlock) external;

    /// @notice Claim tokens after the auction's claim block
    /// @notice The bid must be exited before claiming tokens
    /// @dev Anyone can claim tokens for any bid, the tokens are transferred to the bid owner
    /// @param bidId The id of the bid
    function claimTokens(uint256 bidId) external;

    /// @notice Claim tokens for multiple bids
    /// @dev Anyone can claim tokens for bids of the same owner, the tokens are transferred to the owner
    /// @dev A TokensClaimed event is emitted for each bid but only one token transfer will be made
    /// @param owner The owner of the bids
    /// @param bidIds The ids of the bids
    function claimTokensBatch(address owner, uint256[] calldata bidIds) external;

    /// @notice Withdraw all of the currency raised
    /// @dev Can be called by anyone after the auction has ended
    function sweepCurrency() external;

    /// @notice The block at which the auction can be claimed
    function claimBlock() external view returns (uint64);

    /// @notice The address of the validation hook for the auction
    function validationHook() external view returns (IValidationHook);

    /// @notice Sweep any leftover tokens to the tokens recipient
    /// @dev This function can only be called after the auction has ended
    function sweepUnsoldTokens() external;

    /// @notice The currency raised as of the last checkpoint
    function currencyRaisedQ96_X7() external view returns (ValueX7);

    /// @notice The sum of demand in ticks above the clearing price
    function sumCurrencyDemandAboveClearingQ96() external view returns (uint256);

    /// @notice The total currency raised as of the last checkpoint
    function totalClearedQ96_X7() external view returns (ValueX7);

    /// @notice The total tokens cleared as of the last checkpoint in uint256 representation
    function totalCleared() external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Interface for custom bid validation logic
interface IValidationHook {
    /// @notice Validate a bid
    /// @dev MUST revert if the bid is invalid
    /// @param maxPrice The maximum price the bidder is willing to pay
    /// @param amount The amount of the bid
    /// @param owner The owner of the bid
    /// @param sender The sender of the bid
    /// @param hookData Additional data to pass to the hook required for validation
    function validate(uint256 maxPrice, uint128 amount, address owner, address sender, bytes calldata hookData) external;
}

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

/// @title IDistributionContract
/// @notice Interface for token distribution contracts.
interface IDistributionContract {
    /// @notice Notify a distribution contract that it has received the tokens to distribute
    function onTokensReceived() external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {ConstantsLib} from './ConstantsLib.sol';

struct Bid {
    uint64 startBlock; // Block number when the bid was first made in
    uint24 startCumulativeMps; // Cumulative mps at the start of the bid
    uint64 exitedBlock; // Block number when the bid was exited
    uint256 maxPrice; // The max price of the bid
    address owner; // Who will receive the tokens filled and currency refunded
    uint256 amountQ96; // User's currency amount in Q96 form
    uint256 tokensFilled; // Amount of tokens filled
}

/// @title BidLib
library BidLib {
    using BidLib for *;

    /// @dev Error thrown when a bid is submitted with no remaining percentage of the auction
    ///      This is prevented by the auction contract as bids cannot be submitted when the auction is sold out,
    ///      but we catch it instead of reverting with division by zero.
    error MpsRemainingIsZero();

    /// @notice Calculate the number of mps remaining in the auction since the bid was submitted
    /// @param bid The bid to calculate the remaining mps for
    /// @return The number of mps remaining in the auction
    function mpsRemainingInAuctionAfterSubmission(Bid memory bid) internal pure returns (uint24) {
        return ConstantsLib.MPS - bid.startCumulativeMps;
    }

    /// @notice Scale a bid amount to its effective amount over the remaining percentage of the auction
    ///         This is an important normalization step to ensure that we can calculate the currencyRaised
    ///         when cumulative demand is less than supply using the original supply schedule.
    /// @param bid The bid to scale
    /// @return The scaled amount
    function toEffectiveAmount(Bid memory bid) internal pure returns (uint256) {
        uint24 mpsRemainingInAuction = bid.mpsRemainingInAuctionAfterSubmission();
        if (mpsRemainingInAuction == 0) revert MpsRemainingIsZero();
        return bid.amountQ96 * ConstantsLib.MPS / mpsRemainingInAuction;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ConstantsLib} from './ConstantsLib.sol';
import {ValueX7} from './ValueX7Lib.sol';

struct Checkpoint {
    uint256 clearingPrice; // The X96 price which the auction is currently clearing at
    ValueX7 currencyRaisedAtClearingPriceQ96_X7; // The currency raised so far to this clearing price
    uint256 cumulativeMpsPerPrice; // A running sum of the ratio between mps and price
    uint24 cumulativeMps; // The number of mps sold in the auction so far (via the original supply schedule)
    uint64 prev; // Block number of the previous checkpoint
    uint64 next; // Block number of the next checkpoint
}

/// @title CheckpointLib
library CheckpointLib {
    /// @notice Get the remaining mps in the auction at the given checkpoint
    /// @param _checkpoint The checkpoint with `cumulativeMps` so far
    /// @return The remaining mps in the auction
    function remainingMpsInAuction(Checkpoint memory _checkpoint) internal pure returns (uint24) {
        return ConstantsLib.MPS - _checkpoint.cumulativeMps;
    }

    /// @notice Calculate the supply to price ratio. Will return zero if `price` is zero
    /// @dev This function returns a value in Q96 form
    /// @param mps The number of supply mps sold
    /// @param price The price they were sold at
    /// @return the ratio
    function getMpsPerPrice(uint24 mps, uint256 price) internal pure returns (uint256) {
        if (price == 0) return 0;
        // The bitshift cannot overflow because a uint24 shifted left FixedPoint96.RESOLUTION * 2 (192) bits will always be less than 2^256
        return (uint256(mps) << 192) / price;
    }
}

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

/// @title ConstantsLib
/// @notice Library containing protocol constants
library ConstantsLib {
    /// @notice we use milli-bips, or one thousandth of a basis point
    uint24 constant MPS = 1e7;
    /// @notice The upper bound of a ValueX7 value
    uint256 constant X7_UPPER_BOUND = type(uint256).max / 1e7;

    /// @notice The maximum total supply of tokens that can be sold in the Auction
    /// @dev    This is set to 2^100 tokens, which is just above 1e30, or one trillion units of a token with 18 decimals.
    ///         This upper bound is chosen to prevent the Auction from being used with an extremely large token supply,
    ///         which would restrict the clearing price to be a very low price in the calculation below.
    uint128 constant MAX_TOTAL_SUPPLY = 1 << 100;

    /// @notice The minimum allowable floor price is type(uint32).max + 1
    /// @dev This is the minimum price that fits in a uint160 after being inversed
    uint256 constant MIN_FLOOR_PRICE = uint256(type(uint32).max) + 1;

    /// @notice The minimum allowable tick spacing
    /// @dev We don't support tick spacings of 1 to avoid edge cases where the rounding of the clearing price
    ///      would cause the price to move between initialized ticks.
    uint256 constant MIN_TICK_SPACING = 2;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {IERC20Minimal} from '../interfaces/external/IERC20Minimal.sol';

type Currency is address;

using CurrencyLibrary for Currency global;

/// @title CurrencyLibrary
/// @dev This library allows for transferring and holding native tokens and ERC20 tokens
/// @dev Forked from https://github.com/Uniswap/v4-core/blob/main/src/types/Currency.sol but modified to not bubble up reverts
library CurrencyLibrary {
    /// @notice Thrown when a native transfer fails
    error NativeTransferFailed();

    /// @notice Thrown when an ERC20 transfer fails
    error ERC20TransferFailed();

    /// @notice A constant to represent the native currency
    Currency public constant ADDRESS_ZERO = Currency.wrap(address(0));

    function transfer(Currency currency, address to, uint256 amount) internal {
        // altered from https://github.com/transmissions11/solmate/blob/44a9963d4c78111f77caa0e65d677b8b46d6f2e6/src/utils/SafeTransferLib.sol
        // modified custom error selectors

        bool success;
        if (currency.isAddressZero()) {
            assembly ('memory-safe') {
                // Transfer the ETH and revert if it fails.
                success := call(gas(), to, amount, 0, 0, 0, 0)
            }
            // revert with NativeTransferFailed
            if (!success) {
                revert NativeTransferFailed();
            }
        } else {
            assembly ('memory-safe') {
                // Get a pointer to some free memory.
                let fmp := mload(0x40)

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

                success := and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), currency, 0, fmp, 68, 0, 32)
                )

                // Now clean the memory we used
                mstore(fmp, 0) // 4 byte `selector` and 28 bytes of `to` were stored here
                mstore(add(fmp, 0x20), 0) // 4 bytes of `to` and 28 bytes of `amount` were stored here
                mstore(add(fmp, 0x40), 0) // 4 bytes of `amount` were stored here
            }
            // revert with ERC20TransferFailed
            if (!success) {
                revert ERC20TransferFailed();
            }
        }
    }

    function balanceOf(Currency currency, address owner) internal view returns (uint256) {
        if (currency.isAddressZero()) {
            return owner.balance;
        } else {
            return IERC20Minimal(Currency.unwrap(currency)).balanceOf(owner);
        }
    }

    function isAddressZero(Currency currency) internal pure returns (bool) {
        return Currency.unwrap(currency) == Currency.unwrap(ADDRESS_ZERO);
    }
}

File 14 of 28 : FixedPoint96.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title FixedPoint96
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
/// @dev Copied from https://github.com/Uniswap/v4-core/blob/main/src/libraries/FixedPoint96.sol
library FixedPoint96 {
    uint8 internal constant RESOLUTION = 96;
    uint256 internal constant Q96 = 0x1000000000000000000000000;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {FixedPointMathLib} from 'solady/utils/FixedPointMathLib.sol';

/// @title MaxBidPriceLib
/// @notice Library for calculating the maximum bid price for a given total supply
/// @dev The two are generally inversely correlated with certain constraints.
library MaxBidPriceLib {
    /**
     * @dev Given a total supply we want to find the maximum bid price such that both the
     * token liquidity and currency liquidity at the end of the Auction are less than the
     * maximum liquidity supported by Uniswap v4.
     *
     * The chart below shows the shaded area of valid (max bid price, total supply) value pairs such that
     * both calculated liquidity values are less than the maximum liquidity supported by Uniswap v4.
     * (x axis represents the max bid price in log form, and y is the total supply in log form)
     *
     * y ↑
     * |               :                         :   :
     * |                                            :                                  :
     * 128 +               :                               :
     * |                                                  :                            :
     * |               :                                 :   :
     * |                                                    :                          :
     * |               :                                       :
     * |                                                          :                    :
     * |               :                                         :   : (x=110, y=100)
     * | : : : : : : : +#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+#+ : ::: : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :
     * 96 +            +############################################   :
     * |               #################################################  :            :
     * |               +#################################################+#  :
     * |               #####################################################+          :
     * |               +#######################################################:
     * |               ########################################################## :    :
     * |               +#########################################################+#  :
     * |               #############################################################+  :
     * 64 +            +###############################################################: (x=160, y=62)
     * |               ################################################################:  :
     * |               +###############################################################  :   :
     * |               ################################################################:    :
     * |               +###############################################################        :
     * |               ################################################################:          :
     * |               +###############################################################          :   :
     * |               ################################################################:            :
     * 32 +            +###############################################################                :
     * |               ################################################################:                  :
     * |               +###############################################################                  :   :
     * |               ################################################################:                    :
     * |               +###############################################################                        :
     * |               ################################################################:                          :
     * |               +###############################################################+               +         :   : +
     * +---------------+###############+###############+###############+###############+---------------+---------------+--------------- x (max price)
     *  0              32              64              96              128             160             192             224           256
     *
     *
     * Legend:
     * x = max bid price in log form
     * y = total supply in log form
     * L_max = 2^107 (the lowest max liquidity per tick supported in Uniswap v4)
     * p_sqrtMax = 1461446703485210103287273052203988822378723970342 (max sqrt price in Uniswap v4)
     * p_sqrtMin = 4295128739 (min sqrt price in Uniswap v4)
     * x < 160, x > 32; (minimum price of 2^32, maximum price of 2^160)
     * y < 100; (minimum supply of 2^0 or 1, maximum supply of 2^100)
     *
     * Equations for liquidity amounts in Uniswap v4:
     * 1) If currencyIsCurrency1, L_0 = (2^y * ((2^((x+96)/2) * 2^160) / 2^96)) / |2^((x+96)/2)-p_sqrtMax| < L_max
     * 2)                         L_1 = (2^(x+y)) / |2^((x+96)/2)-p_sqrtMin| < L_max
     * 3) if currencyIsCurrency0, L_0 = (2^y * p_sqrtMax * 2^((192-x+96)/2)) / (2^(192-x+96) * |p_sqrtMax-2^((192-x+96)/2)|) < L_max
     * 4)                         L_1 = (2^(y+96)) / |2^((192-x+96)/2)-p_sqrtMin| < L_max
     */
    /// @notice The maximum allowable price for a bid is type(uint160).max
    /// @dev This is the maximum price that can be shifted left by 96 bits without overflowing a uint256
    uint256 constant MAX_V4_PRICE = type(uint160).max;

    /// @notice The total supply value below which the maximum bid price is capped at MAX_V4_PRICE
    /// @dev Since the two are inversely correlated, generally lower total supply = higher max bid price
    ///      However, for very small total supply values we still can't exceed the max v4 price.
    ///      This is the intersection of `maxPriceKeepingCurrencyRaisedUnderInt128Max` and MAX_V4_PRICE,
    ///      meaning that because we can't support prices above uint160.max, all total supply values at or below
    ///      this threshold are capped at MAX_V4_PRICE.
    uint256 constant LOWER_TOTAL_SUPPLY_THRESHOLD = 1 << 62;

    /// @notice Calculates the maximum bid price for a given total supply
    /// @dev Total supply values under the LOWER_TOTAL_SUPPLY_THRESHOLD are capped at MAX_V4_PRICE
    function maxBidPrice(uint128 _totalSupply) internal pure returns (uint256) {
        // Small total supply values would return a price which exceeds the max v4 price, so we cap it at MAX_V4_PRICE
        if (_totalSupply <= LOWER_TOTAL_SUPPLY_THRESHOLD) return MAX_V4_PRICE;
        /**
         * Derivation: For a given total supply y (in log space), find the max bid price x (in log space)
         * The equations in the chart are equivalent for both currency/token sort orders (intuitive given a full range position).
         * Token1 liquidity is the limiting factor, so we use L_1 for simplicity:
         *  2^(x+y) / |2^((x+96)/2)-p_sqrtMin| < L_max
         *  2^(x+y) < L_max * |2^((x+96)/2)-p_sqrtMin|
         * We substitute a larger number than p_sqrtMin such that |2^((x+96)/2)-p_sqrtMin| ~ 2^((x+96)/2 - 1)
         *  2^(x+y) < L_max * 2^((x+96)/2 - 1)
         * Using 2^107 for L_max, we get:
         *  2^(x+y) < 2^107 * 2^((x+96)/2 - 1)
         * Taking the log2 of both sides, we get:
         *  x + y < 107 + (x+96) / 2 - 1
         *  x + y < 107 + x/2 + 48 - 1
         * Since we are given total supply (y), we can solve for x:
         *  x/2 = 107 + 47 - y
         *  x/2 = 154 - y
         *  x = 2 * (154 - y)
         * We want to find 2^x, not `x` so we take both sides to the power of 2:
         *  2^x = (2^154 / y) ** 2
         *
         * Because we return early if total supply is less than 2^62 the result of this will not overflow a uint256.
         */
        uint256 maxPriceKeepingLiquidityUnderMax = uint256((1 << 154) / _totalSupply) ** 2;

        // Additionally, we need to ensure that the currency raised is <= int128.max (2^127 - 1)
        // since PoolManager will cast it to int128 when the position is created.
        // The maxmimum currencyRaised in the auction is equal to totalSupply * maxBidPrice / Q96
        // To be conservative, we ensure that it is under 2^126, and rearranging the equation we get:
        // maxBidPrice < (2^126 * Q96) / totalSupply = 2^222 / totalSupply
        uint256 maxPriceKeepingCurrencyRaisedUnderInt128Max = uint256(1 << 222) / _totalSupply;

        // Take the minimum of the two to ensure that the (max bid price, total supply) pair is within the valid range.
        return FixedPointMathLib.min(maxPriceKeepingLiquidityUnderMax, maxPriceKeepingCurrencyRaisedUnderInt128Max);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

struct AuctionStep {
    uint24 mps; // Mps to sell per block in the step
    uint64 startBlock; // Start block of the step (inclusive)
    uint64 endBlock; // Ending block of the step (exclusive)
}

/// @notice Library for auction step calculations and parsing
library StepLib {
    using StepLib for *;

    /// @notice The size of a uint64 in bytes
    uint256 public constant UINT64_SIZE = 8;

    /// @notice Error thrown when the offset is too large for the data length
    error StepLib__InvalidOffsetTooLarge();
    /// @notice Error thrown when the offset is not at a step boundary - a uint64 aligned offset
    error StepLib__InvalidOffsetNotAtStepBoundary();

    /// @notice Unpack the mps and block delta from the auction steps data
    function parse(bytes8 data) internal pure returns (uint24 mps, uint40 blockDelta) {
        mps = uint24(bytes3(data));
        blockDelta = uint40(uint64(data));
    }

    /// @notice Load a word at `offset` from data and parse it into mps and blockDelta
    function get(bytes memory data, uint256 offset) internal pure returns (uint24 mps, uint40 blockDelta) {
        // Offset cannot be greater than the data length
        if (offset >= data.length) revert StepLib__InvalidOffsetTooLarge();
        // Offset must be a multiple of a step (uint64 -  uint24|uint40)
        if (offset % UINT64_SIZE != 0) revert StepLib__InvalidOffsetNotAtStepBoundary();

        assembly {
            let packedValue := mload(add(add(data, 0x20), offset))
            packedValue := shr(192, packedValue)
            mps := shr(40, packedValue)
            blockDelta := and(packedValue, 0xFFFFFFFFFF)
        }
    }
}

File 17 of 28 : ValidationHookLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import {IValidationHook} from '../interfaces/IValidationHook.sol';

/// @title ValidationHookLib
/// @notice Library for handling calls to validation hooks and bubbling up the revert reason
library ValidationHookLib {
    /// @notice Error thrown when a validation hook call fails
    /// @param reason The bubbled up revert reason
    error ValidationHookCallFailed(bytes reason);

    /// @notice Handles calling a validation hook and bubbling up the revert reason
    function handleValidate(
        IValidationHook hook,
        uint256 maxPrice,
        uint128 amount,
        address owner,
        address sender,
        bytes calldata hookData
    ) internal {
        if (address(hook) == address(0)) return;

        try hook.validate(maxPrice, amount, owner, sender, hookData) {}
        catch (bytes memory reason) {
            revert ValidationHookCallFailed(reason);
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ConstantsLib} from './ConstantsLib.sol';
import {FixedPointMathLib} from 'solady/utils/FixedPointMathLib.sol';

/// @notice A ValueX7 is a uint256 value that has been multiplied by MPS
/// @dev X7 values are used for demand values to avoid intermediate division by MPS
type ValueX7 is uint256;

using {sub, divUint256} for ValueX7 global;

/// @notice Subtract two ValueX7 values
function sub(ValueX7 a, ValueX7 b) pure returns (ValueX7) {
    return ValueX7.wrap(ValueX7.unwrap(a) - ValueX7.unwrap(b));
}

/// @notice Divide a ValueX7 value by a uint256
function divUint256(ValueX7 a, uint256 b) pure returns (ValueX7) {
    return ValueX7.wrap(ValueX7.unwrap(a) / b);
}

/// @title ValueX7Lib
library ValueX7Lib {
    using ValueX7Lib for ValueX7;

    /// @notice The scaling factor for ValueX7 values (ConstantsLib.MPS)
    uint256 public constant X7 = ConstantsLib.MPS;

    /// @notice Multiply a uint256 value by MPS
    /// @dev This ensures that future operations will not lose precision
    /// @return The result as a ValueX7
    function scaleUpToX7(uint256 value) internal pure returns (ValueX7) {
        return ValueX7.wrap(value * X7);
    }

    /// @notice Divide a ValueX7 value by MPS
    /// @return The result as a uint256
    function scaleDownToUint256(ValueX7 value) internal pure returns (uint256) {
        return ValueX7.unwrap(value) / X7;
    }
}

File 19 of 28 : FixedPointMathLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error ExpOverflow();

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error FactorialOverflow();

    /// @dev The operation failed, due to an overflow.
    error RPowOverflow();

    /// @dev The mantissa is too big to fit.
    error MantissaOverflow();

    /// @dev The operation failed, due to an multiplication overflow.
    error MulWadFailed();

    /// @dev The operation failed, due to an multiplication overflow.
    error SMulWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error DivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error SDivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error MulDivFailed();

    /// @dev The division failed, as the denominator is zero.
    error DivFailed();

    /// @dev The full precision multiply-divide operation failed, either due
    /// to the result being larger than 256 bits, or a division by a zero.
    error FullMulDivFailed();

    /// @dev The output is undefined, as the input is less-than-or-equal to zero.
    error LnWadUndefined();

    /// @dev The input outside the acceptable domain.
    error OutOfDomain();

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

    /// @dev The scalar of ETH and most ERC20s.
    uint256 internal constant WAD = 1e18;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              SIMPLIFIED FIXED POINT OPERATIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if gt(x, div(not(0), y)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
            if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
                mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up.
    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if iszero(eq(div(z, y), x)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
    function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, WAD)
            // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
            if iszero(mul(y, eq(sdiv(z, WAD), x))) {
                mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up.
    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
    function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `x` to the power of `y`.
    /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
    /// Note: This function is an approximation.
    function powWad(int256 x, int256 y) internal pure returns (int256) {
        // Using `ln(x)` means `x` must be greater than 0.
        return expWad((lnWad(x) * y) / int256(WAD));
    }

    /// @dev Returns `exp(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function expWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            // When the result is less than 0.5 we return zero.
            // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
            if (x <= -41446531673892822313) return r;

            /// @solidity memory-safe-assembly
            assembly {
                // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
                // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
                if iszero(slt(x, 135305999368893231589)) {
                    mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
                    revert(0x1c, 0x04)
                }
            }

            // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
            // for more intermediate precision and a binary basis. This base conversion
            // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
            x = (x << 78) / 5 ** 18;

            // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
            // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
            // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
            int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
            x = x - k * 54916777467707473351141471128;

            // `k` is in the range `[-61, 195]`.

            // Evaluate using a (6, 7)-term rational approximation.
            // `p` is made monic, we'll multiply by a scale factor later.
            int256 y = x + 1346386616545796478920950773328;
            y = ((y * x) >> 96) + 57155421227552351082224309758442;
            int256 p = y + x - 94201549194550492254356042504812;
            p = ((p * y) >> 96) + 28719021644029726153956944680412240;
            p = p * x + (4385272521454847904659076985693276 << 96);

            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
            int256 q = x - 2855989394907223263936484059900;
            q = ((q * x) >> 96) + 50020603652535783019961831881945;
            q = ((q * x) >> 96) - 533845033583426703283633433725380;
            q = ((q * x) >> 96) + 3604857256930695427073651918091429;
            q = ((q * x) >> 96) - 14423608567350463180887372962807573;
            q = ((q * x) >> 96) + 26449188498355588339934803723976023;

            /// @solidity memory-safe-assembly
            assembly {
                // Div in assembly because solidity adds a zero check despite the unchecked.
                // The q polynomial won't have zeros in the domain as all its roots are complex.
                // No scaling is necessary because p is already `2**96` too large.
                r := sdiv(p, q)
            }

            // r should be in the range `(0.09, 0.25) * 2**96`.

            // We now need to multiply r by:
            // - The scale factor `s ≈ 6.031367120`.
            // - The `2**k` factor from the range reduction.
            // - The `1e18 / 2**96` factor for base conversion.
            // We do this all at once, with an intermediate result in `2**213`
            // basis, so the final right shift is always by a positive amount.
            r = int256(
                (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
            );
        }
    }

    /// @dev Returns `ln(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function lnWad(int256 x) internal pure returns (int256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
            // We do this by multiplying by `2**96 / 10**18`. But since
            // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
            // and add `ln(2**96 / 10**18)` at the end.

            // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // We place the check here for more optimal stack operations.
            if iszero(sgt(x, 0)) {
                mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
                revert(0x1c, 0x04)
            }
            // forgefmt: disable-next-item
            r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))

            // Reduce range of x to (1, 2) * 2**96
            // ln(2^k * x) = k * ln(2) + ln(x)
            x := shr(159, shl(r, x))

            // Evaluate using a (8, 8)-term rational approximation.
            // `p` is made monic, we will multiply by a scale factor later.
            // forgefmt: disable-next-item
            let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
                sar(96, mul(add(43456485725739037958740375743393,
                sar(96, mul(add(24828157081833163892658089445524,
                sar(96, mul(add(3273285459638523848632254066296,
                    x), x))), x))), x)), 11111509109440967052023855526967)
            p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
            p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
            p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.

            // `q` is monic by convention.
            let q := add(5573035233440673466300451813936, x)
            q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
            q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
            q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
            q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
            q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
            q := add(909429971244387300277376558375, sar(96, mul(x, q)))

            // `p / q` is in the range `(0, 0.125) * 2**96`.

            // Finalization, we need to:
            // - Multiply by the scale factor `s = 5.549…`.
            // - Add `ln(2**96 / 10**18)`.
            // - Add `k * ln(2)`.
            // - Multiply by `10**18 / 2**96 = 5**18 >> 78`.

            // The q polynomial is known not to have zeros in the domain.
            // No scaling required because p is already `2**96` too large.
            p := sdiv(p, q)
            // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
            p := mul(1677202110996718588342820967067443963516166, p)
            // Add `ln(2) * k * 5**18 * 2**192`.
            // forgefmt: disable-next-item
            p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
            // Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
            p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
            // Base conversion: mul `2**18 / 2**192`.
            r := sar(174, p)
        }
    }

    /// @dev Returns `W_0(x)`, denominated in `WAD`.
    /// See: https://en.wikipedia.org/wiki/Lambert_W_function
    /// a.k.a. Product log function. This is an approximation of the principal branch.
    /// Note: This function is an approximation. Monotonically increasing.
    function lambertW0Wad(int256 x) internal pure returns (int256 w) {
        // forgefmt: disable-next-item
        unchecked {
            if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
            (int256 wad, int256 p) = (int256(WAD), x);
            uint256 c; // Whether we need to avoid catastrophic cancellation.
            uint256 i = 4; // Number of iterations.
            if (w <= 0x1ffffffffffff) {
                if (-0x4000000000000 <= w) {
                    i = 1; // Inputs near zero only take one step to converge.
                } else if (w <= -0x3ffffffffffffff) {
                    i = 32; // Inputs near `-1/e` take very long to converge.
                }
            } else if (uint256(w >> 63) == uint256(0)) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Inline log2 for more performance, since the range is small.
                    let v := shr(49, w)
                    let l := shl(3, lt(0xff, v))
                    l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
                        0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
                    w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
                    c := gt(l, 60)
                    i := add(2, add(gt(l, 53), c))
                }
            } else {
                int256 ll = lnWad(w = lnWad(w));
                /// @solidity memory-safe-assembly
                assembly {
                    // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
                    w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
                    i := add(3, iszero(shr(68, x)))
                    c := iszero(shr(143, x))
                }
                if (c == uint256(0)) {
                    do { // If `x` is big, use Newton's so that intermediate values won't overflow.
                        int256 e = expWad(w);
                        /// @solidity memory-safe-assembly
                        assembly {
                            let t := mul(w, div(e, wad))
                            w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
                        }
                        if (p <= w) break;
                        p = w;
                    } while (--i != uint256(0));
                    /// @solidity memory-safe-assembly
                    assembly {
                        w := sub(w, sgt(w, 2))
                    }
                    return w;
                }
            }
            do { // Otherwise, use Halley's for faster convergence.
                int256 e = expWad(w);
                /// @solidity memory-safe-assembly
                assembly {
                    let t := add(w, wad)
                    let s := sub(mul(w, e), mul(x, wad))
                    w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
                }
                if (p <= w) break;
                p = w;
            } while (--i != c);
            /// @solidity memory-safe-assembly
            assembly {
                w := sub(w, sgt(w, 2))
            }
            // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
            // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
            if (c == uint256(0)) return w;
            int256 t = w | 1;
            /// @solidity memory-safe-assembly
            assembly {
                x := sdiv(mul(x, wad), t)
            }
            x = (t * (wad + lnWad(x)));
            /// @solidity memory-safe-assembly
            assembly {
                w := sdiv(x, add(wad, t))
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  GENERAL NUMBER UTILITIES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `a * b == x * y`, with full precision.
    function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
    function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // 512-bit multiply `[p1 p0] = x * y`.
            // Compute the product mod `2**256` and mod `2**256 - 1`
            // then use the Chinese Remainder Theorem to reconstruct
            // the 512 bit result. The result is stored in two 256
            // variables such that `product = p1 * 2**256 + p0`.

            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`.
            for {} 1 {} {
                // If overflows.
                if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.

                    /*------------------- 512 by 256 division --------------------*/

                    // Make division exact by subtracting the remainder from `[p1 p0]`.
                    let r := mulmod(x, y, d) // Compute remainder using mulmod.
                    let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
                    // Make sure `z` is less than `2**256`. Also prevents `d == 0`.
                    // Placing the check here seems to give more optimal stack operations.
                    if iszero(gt(d, p1)) {
                        mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                        revert(0x1c, 0x04)
                    }
                    d := div(d, t) // Divide `d` by `t`, which is a power of two.
                    // Invert `d mod 2**256`
                    // Now that `d` is an odd number, it has an inverse
                    // modulo `2**256` such that `d * inv = 1 mod 2**256`.
                    // Compute the inverse by starting with a seed that is correct
                    // correct for four bits. That is, `d * inv = 1 mod 2**4`.
                    let inv := xor(2, mul(3, d))
                    // Now use 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.
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
                    z :=
                        mul(
                            // Divide [p1 p0] by the factors of two.
                            // Shift in bits from `p1` into `p0`. For this we need
                            // to flip `t` such that it is `2**256 / t`.
                            or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                            mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
                        )
                    break
                }
                z := div(z, d)
                break
            }
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
    /// Performs the full 512 bit calculation regardless.
    function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            let mm := mulmod(x, y, not(0))
            let p1 := sub(mm, add(z, lt(mm, z)))
            let t := and(d, sub(0, d))
            let r := mulmod(x, y, d)
            d := div(d, t)
            let inv := xor(2, mul(3, d))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            z :=
                mul(
                    or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                    mul(sub(2, mul(d, inv)), inv)
                )
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Uniswap-v3-core under MIT license:
    /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
    function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        z = fullMulDiv(x, y, d);
        /// @solidity memory-safe-assembly
        assembly {
            if mulmod(x, y, d) {
                z := add(z, 1)
                if iszero(z) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
    /// Throws if result overflows a uint256.
    /// Credit to Philogy under MIT license:
    /// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
    function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
            for {} 1 {} {
                if iszero(or(iszero(x), eq(div(z, x), y))) {
                    let k := and(n, 0xff) // `n`, cleaned.
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
                    //         |      p1     |      z     |
                    // Before: | p1_0 ¦ p1_1 | z_0  ¦ z_1 |
                    // Final:  |   0  ¦ p1_0 | p1_1 ¦ z_0 |
                    // Check that final `z` doesn't overflow by checking that p1_0 = 0.
                    if iszero(shr(k, p1)) {
                        z := add(shl(sub(256, k), p1), shr(k, z))
                        break
                    }
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
                z := shr(and(n, 0xff), z)
                break
            }
        }
    }

    /// @dev Returns `floor(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(z, d)
        }
    }

    /// @dev Returns `ceil(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(z, d))), div(z, d))
        }
    }

    /// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
    function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
        /// @solidity memory-safe-assembly
        assembly {
            let g := n
            let r := mod(a, n)
            for { let y := 1 } 1 {} {
                let q := div(g, r)
                let t := g
                g := r
                r := sub(t, mul(r, q))
                let u := x
                x := y
                y := sub(u, mul(y, q))
                if iszero(r) { break }
            }
            x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
        }
    }

    /// @dev Returns `ceil(x / d)`.
    /// Reverts if `d` is zero.
    function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(d) {
                mstore(0x00, 0x65244e4e) // `DivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(x, d))), div(x, d))
        }
    }

    /// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`.
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `max(0, x - y)`.
    function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `min(2 ** 256 - 1, x + y)`.
    function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(sub(0, lt(add(x, y), x)), add(x, y))
        }
    }

    /// @dev Returns `min(2 ** 256 - 1, x * y)`.
    function saturatingMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(sub(or(iszero(x), eq(div(mul(x, y), x), y)), 1), mul(x, y))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, address x, address y) internal pure returns (address z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `x != 0 ? x : y`, without branching.
    function coalesce(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(x)))
        }
    }

    /// @dev Returns `x != bytes32(0) ? x : y`, without branching.
    function coalesce(bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(x)))
        }
    }

    /// @dev Returns `x != address(0) ? x : y`, without branching.
    function coalesce(address x, address y) internal pure returns (address z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(shl(96, x))))
        }
    }

    /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
    /// Reverts if the computation overflows.
    function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
            if x {
                z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
                let half := shr(1, b) // Divide `b` by 2.
                // Divide `y` by 2 every iteration.
                for { y := shr(1, y) } y { y := shr(1, y) } {
                    let xx := mul(x, x) // Store x squared.
                    let xxRound := add(xx, half) // Round to the nearest number.
                    // Revert if `xx + half` overflowed, or if `x ** 2` overflows.
                    if or(lt(xxRound, xx), shr(128, x)) {
                        mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    x := div(xxRound, b) // Set `x` to scaled `xxRound`.
                    // If `y` is odd:
                    if and(y, 1) {
                        let zx := mul(z, x) // Compute `z * x`.
                        let zxRound := add(zx, half) // Round to the nearest number.
                        // If `z * x` overflowed or `zx + half` overflowed:
                        if or(xor(div(zx, x), z), lt(zxRound, zx)) {
                            // Revert if `x` is non-zero.
                            if x {
                                mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                                revert(0x1c, 0x04)
                            }
                        }
                        z := div(zxRound, b) // Return properly scaled `zxRound`.
                    }
                }
            }
        }
    }

    /// @dev Returns the square root of `x`, rounded down.
    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
            // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffffff, shr(r, x))))
            z := shl(shr(1, r), z)

            // Goal was to get `z*z*y` within a small factor of `x`. More iterations could
            // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
            // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
            // That's not possible if `x < 256` but we can just verify those cases exhaustively.

            // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
            // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
            // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.

            // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
            // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
            // with largest error when `s = 1` and when `s = 256` or `1/256`.

            // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
            // Then we can estimate `sqrt(y)` using
            // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.

            // There is no overflow risk here since `y < 2**136` after the first branch above.
            z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If `x+1` is a perfect square, the Babylonian method cycles between
            // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            z := sub(z, lt(div(x, z), z))
        }
    }

    /// @dev Returns the cube root of `x`, rounded down.
    /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
    /// https://github.com/pcaversaccio/snekmate/blob/main/src/snekmate/utils/math.vy
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // Makeshift lookup table to nudge the approximate log2 result.
            z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
            // Newton-Raphson's.
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            // Round down.
            z := sub(z, lt(div(x, mul(z, z)), z))
        }
    }

    /// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
    function sqrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
            z = (1 + sqrt(x)) * 10 ** 9;
            z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
        }
        /// @solidity memory-safe-assembly
        assembly {
            z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
        }
    }

    /// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
            z = (1 + cbrt(x)) * 10 ** 12;
            z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
        }
        /// @solidity memory-safe-assembly
        assembly {
            let p := x
            for {} 1 {} {
                if iszero(shr(229, p)) {
                    if iszero(shr(199, p)) {
                        p := mul(p, 100000000000000000) // 10 ** 17.
                        break
                    }
                    p := mul(p, 100000000) // 10 ** 8.
                    break
                }
                if iszero(shr(249, p)) { p := mul(p, 100) }
                break
            }
            let t := mulmod(mul(z, z), z, p)
            z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
        }
    }

    /// @dev Returns the factorial of `x`.
    function factorial(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := 1
            if iszero(lt(x, 58)) {
                mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
                revert(0x1c, 0x04)
            }
            for {} x { x := sub(x, 1) } { z := mul(z, x) }
        }
    }

    /// @dev Returns the log2 of `x`.
    /// Equivalent to computing the index of the most significant bit (MSB) of `x`.
    /// Returns 0 if `x` is zero.
    function log2(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020504060203020504030106050205030304010505030400000000))
        }
    }

    /// @dev Returns the log2 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log2Up(uint256 x) internal pure returns (uint256 r) {
        r = log2(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(r, 1), x))
        }
    }

    /// @dev Returns the log10 of `x`.
    /// Returns 0 if `x` is zero.
    function log10(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(lt(x, 100000000000000000000000000000000000000)) {
                x := div(x, 100000000000000000000000000000000000000)
                r := 38
            }
            if iszero(lt(x, 100000000000000000000)) {
                x := div(x, 100000000000000000000)
                r := add(r, 20)
            }
            if iszero(lt(x, 10000000000)) {
                x := div(x, 10000000000)
                r := add(r, 10)
            }
            if iszero(lt(x, 100000)) {
                x := div(x, 100000)
                r := add(r, 5)
            }
            r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
        }
    }

    /// @dev Returns the log10 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log10Up(uint256 x) internal pure returns (uint256 r) {
        r = log10(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(exp(10, r), x))
        }
    }

    /// @dev Returns the log256 of `x`.
    /// Returns 0 if `x` is zero.
    function log256(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(shr(3, r), lt(0xff, shr(r, x)))
        }
    }

    /// @dev Returns the log256 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log256Up(uint256 x) internal pure returns (uint256 r) {
        r = log256(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(shl(3, r), 1), x))
        }
    }

    /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
    /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
    function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
        /// @solidity memory-safe-assembly
        assembly {
            mantissa := x
            if mantissa {
                if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
                    mantissa := div(mantissa, 1000000000000000000000000000000000)
                    exponent := 33
                }
                if iszero(mod(mantissa, 10000000000000000000)) {
                    mantissa := div(mantissa, 10000000000000000000)
                    exponent := add(exponent, 19)
                }
                if iszero(mod(mantissa, 1000000000000)) {
                    mantissa := div(mantissa, 1000000000000)
                    exponent := add(exponent, 12)
                }
                if iszero(mod(mantissa, 1000000)) {
                    mantissa := div(mantissa, 1000000)
                    exponent := add(exponent, 6)
                }
                if iszero(mod(mantissa, 10000)) {
                    mantissa := div(mantissa, 10000)
                    exponent := add(exponent, 4)
                }
                if iszero(mod(mantissa, 100)) {
                    mantissa := div(mantissa, 100)
                    exponent := add(exponent, 2)
                }
                if iszero(mod(mantissa, 10)) {
                    mantissa := div(mantissa, 10)
                    exponent := add(exponent, 1)
                }
            }
        }
    }

    /// @dev Convenience function for packing `x` into a smaller number using `sci`.
    /// The `mantissa` will be in bits [7..255] (the upper 249 bits).
    /// The `exponent` will be in bits [0..6] (the lower 7 bits).
    /// Use `SafeCastLib` to safely ensure that the `packed` number is small
    /// enough to fit in the desired unsigned integer type:
    /// ```
    ///     uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
    /// ```
    function packSci(uint256 x) internal pure returns (uint256 packed) {
        (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
        /// @solidity memory-safe-assembly
        assembly {
            if shr(249, x) {
                mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
                revert(0x1c, 0x04)
            }
            packed := or(shl(7, x), packed)
        }
    }

    /// @dev Convenience function for unpacking a packed number from `packSci`.
    function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
        unchecked {
            unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards zero.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = (x & y) + ((x ^ y) >> 1);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
    function avg(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @dev Returns the absolute value of `x`.
    function abs(int256 x) internal pure returns (uint256 z) {
        unchecked {
            z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(int256 x, int256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), slt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), gt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), sgt(y, x)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(uint256 x, uint256 minValue, uint256 maxValue)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
        }
    }

    /// @dev Returns greatest common divisor of `x` and `y`.
    function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            for { z := x } y {} {
                let t := y
                y := mod(z, y)
                z := t
            }
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
        internal
        pure
        returns (uint256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        unchecked {
            if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
            return a - fullMulDiv(a - b, t - begin, end - begin);
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
        internal
        pure
        returns (int256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        // forgefmt: disable-next-item
        unchecked {
            if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
                uint256(t - begin), uint256(end - begin)));
            return int256(uint256(a) - fullMulDiv(uint256(a - b),
                uint256(t - begin), uint256(end - begin)));
        }
    }

    /// @dev Returns if `x` is an even number. Some people may need this.
    function isEven(uint256 x) internal pure returns (bool) {
        return x & uint256(1) == uint256(0);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RAW NUMBER OPERATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(x, y)
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mod(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := smod(x, y)
        }
    }

    /// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
    function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := addmod(x, y, d)
        }
    }

    /// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
    function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mulmod(x, y, d)
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The ERC20 `totalSupply` query has failed.
    error TotalSupplyQueryFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

    /// @dev The Permit2 approve operation has failed.
    error Permit2ApproveFailed();

    /// @dev The Permit2 lockdown operation has failed.
    error Permit2LockdownFailed();

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

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x34, 0) // Store 0 for the `amount`.
                    mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                    pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                    mstore(0x34, amount) // Store back the original `amount`.
                    // Retry the approval, reverting upon failure.
                    success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    if iszero(and(eq(mload(0x00), 1), success)) {
                        // Check the `extcodesize` again just in case the token selfdestructs lol.
                        if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                            mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Performs a `token.balanceOf(account)` check.
    /// `implemented` denotes whether the `token` does not implement `balanceOf`.
    /// `amount` is zero if the `token` does not implement `balanceOf`.
    function checkBalanceOf(address token, address account)
        internal
        view
        returns (bool implemented, uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            implemented :=
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                )
            amount := mul(mload(0x20), implemented)
        }
    }

    /// @dev Returns the total supply of the `token`.
    /// Reverts if the token does not exist or does not implement `totalSupply()`.
    function totalSupply(address token) internal view returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x18160ddd) // `totalSupply()`.
            if iszero(
                and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
            ) {
                mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
                revert(0x1c, 0x04)
            }
            result := mload(0x00)
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(
                and(
                    call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00),
                    lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists.
                )
            ) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(
                        add(m, 0x94),
                        lt(iszero(amount), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    )
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `amount != 0` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero( // Revert if token does not have code, or if the call fails.
            mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Approves `spender` to spend `amount` of `token` for `address(this)`.
    function permit2Approve(address token, address spender, uint160 amount, uint48 expiration)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let addressMask := shr(96, not(0))
            let m := mload(0x40)
            mstore(m, 0x87517c45) // `approve(address,address,uint160,uint48)`.
            mstore(add(m, 0x20), and(addressMask, token))
            mstore(add(m, 0x40), and(addressMask, spender))
            mstore(add(m, 0x60), and(addressMask, amount))
            mstore(add(m, 0x80), and(0xffffffffffff, expiration))
            if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
                mstore(0x00, 0x324f14ae) // `Permit2ApproveFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Revokes an approval for `token` and `spender` for `address(this)`.
    function permit2Lockdown(address token, address spender) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0xcc53287f) // `Permit2.lockdown`.
            mstore(add(m, 0x20), 0x20) // Offset of the `approvals`.
            mstore(add(m, 0x40), 1) // `approvals.length`.
            mstore(add(m, 0x60), shr(96, shl(96, token)))
            mstore(add(m, 0x80), shr(96, shl(96, spender)))
            if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
                mstore(0x00, 0x96b3de23) // `Permit2LockdownFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Bid} from '../libraries/BidLib.sol';

/// @notice Interface for bid storage operations
interface IBidStorage {
    /// @notice Error thrown when doing an operation on a bid that does not exist
    error BidIdDoesNotExist(uint256 bidId);

    /// @notice Get the id of the next bid to be created
    /// @return The id of the next bid to be created
    function nextBidId() external view returns (uint256);

    /// @notice Get a bid from storage
    /// @dev Will revert if the bid does not exist
    /// @param bidId The id of the bid to get
    /// @return The bid
    function bids(uint256 bidId) external view returns (Bid memory);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Checkpoint} from '../libraries/CheckpointLib.sol';

/// @notice Interface for checkpoint storage operations
interface ICheckpointStorage {
    /// @notice Revert when attempting to insert a checkpoint at a block number not strictly greater than the last one
    error CheckpointBlockNotIncreasing();

    /// @notice Get the latest checkpoint at the last checkpointed block
    /// @dev Be aware that the latest checkpoint may not be up to date, it is recommended
    ///      to always call `checkpoint()` before using getter functions
    /// @return The latest checkpoint
    function latestCheckpoint() external view returns (Checkpoint memory);

    /// @notice Get the clearing price at the last checkpointed block
    /// @dev Be aware that the latest checkpoint may not be up to date, it is recommended
    ///      to always call `checkpoint()` before using getter functions
    /// @return The current clearing price in Q96 form
    function clearingPrice() external view returns (uint256);

    /// @notice Get the number of the last checkpointed block
    /// @dev Be aware that the last checkpointed block may not be up to date, it is recommended
    ///      to always call `checkpoint()` before using getter functions
    /// @return The block number of the last checkpoint
    function lastCheckpointedBlock() external view returns (uint64);

    /// @notice Get a checkpoint at a block number
    /// @param blockNumber The block number to get the checkpoint for
    function checkpoints(uint64 blockNumber) external view returns (Checkpoint memory);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {Bid, BidLib} from '../libraries/BidLib.sol';
import {Checkpoint} from '../libraries/CheckpointLib.sol';
import {FixedPoint96} from '../libraries/FixedPoint96.sol';
import {ValueX7} from '../libraries/ValueX7Lib.sol';
import {FixedPointMathLib} from 'solady/utils/FixedPointMathLib.sol';

/// @title CheckpointAccountingLib
/// @notice Pure accounting helpers for computing fills and currency spent across checkpoints
library CheckpointAccountingLib {
    using FixedPointMathLib for *;
    using BidLib for *;

    /// @notice Calculate the tokens sold and proportion of input used for a fully filled bid between two checkpoints
    /// @dev MUST only be used for checkpoints where the bid's max price is strictly greater than the clearing price
    ///      because it uses lazy accounting to calculate the tokens filled
    /// @param upper The upper checkpoint
    /// @param startCheckpoint The start checkpoint of the bid
    /// @param bid The bid
    /// @return tokensFilled The tokens sold
    /// @return currencySpentQ96 The amount of currency spent in Q96 form
    function accountFullyFilledCheckpoints(Checkpoint memory upper, Checkpoint memory startCheckpoint, Bid memory bid)
        internal
        pure
        returns (uint256 tokensFilled, uint256 currencySpentQ96)
    {
        (tokensFilled, currencySpentQ96) = calculateFill(
            bid,
            upper.cumulativeMpsPerPrice - startCheckpoint.cumulativeMpsPerPrice,
            upper.cumulativeMps - startCheckpoint.cumulativeMps
        );
    }

    /// @notice Calculate the tokens sold and currency spent for a partially filled bid
    /// @param bid The bid
    /// @param tickDemandQ96 The total demand at the tick
    /// @param currencyRaisedAtClearingPriceQ96_X7 The cumulative supply sold to the clearing price
    /// @return tokensFilled The tokens sold
    /// @return currencySpentQ96 The amount of currency spent in Q96 form
    function accountPartiallyFilledCheckpoints(
        Bid memory bid,
        uint256 tickDemandQ96,
        ValueX7 currencyRaisedAtClearingPriceQ96_X7
    ) internal pure returns (uint256 tokensFilled, uint256 currencySpentQ96) {
        if (tickDemandQ96 == 0) return (0, 0);

        // Apply the ratio between bid demand and tick demand to the currencyRaisedAtClearingPriceQ96_X7 value
        // If currency spent is calculated to have a remainder, we round up.
        // In the case where the result would have been 0, we will return 1 wei.
        uint256 denominator = tickDemandQ96 * bid.mpsRemainingInAuctionAfterSubmission();
        currencySpentQ96 = bid.amountQ96.fullMulDivUp(ValueX7.unwrap(currencyRaisedAtClearingPriceQ96_X7), denominator);

        // We derive tokens filled from the currency spent by dividing it by the max price.
        // If the currency spent is 0, tokens filled will be 0 as well.
        tokensFilled =
            bid.amountQ96.fullMulDiv(ValueX7.unwrap(currencyRaisedAtClearingPriceQ96_X7), denominator) / bid.maxPrice;
    }

    /// @notice Calculate the tokens filled and currency spent for a bid
    /// @dev Uses lazy accounting to efficiently calculate fills across time periods without iterating blocks.
    ///      MUST only be used when the bid's max price is strictly greater than the clearing price throughout.
    /// @param bid the bid to evaluate
    /// @param cumulativeMpsPerPriceDelta the cumulative sum of supply to price ratio
    /// @param cumulativeMpsDelta the cumulative sum of mps values across the block range
    /// @return tokensFilled the amount of tokens filled for this bid
    /// @return currencySpentQ96 the amount of currency spent by this bid in Q96 form
    function calculateFill(Bid memory bid, uint256 cumulativeMpsPerPriceDelta, uint24 cumulativeMpsDelta)
        internal
        pure
        returns (uint256 tokensFilled, uint256 currencySpentQ96)
    {
        uint24 mpsRemainingInAuctionAfterSubmission = bid.mpsRemainingInAuctionAfterSubmission();

        // Currency spent is original currency amount multiplied by percentage fully filled over percentage allocated
        currencySpentQ96 = bid.amountQ96.fullMulDivUp(cumulativeMpsDelta, mpsRemainingInAuctionAfterSubmission);

        // Tokens filled are calculated from the effective amount over the allocation
        tokensFilled = bid.amountQ96
            .fullMulDiv(
                cumulativeMpsPerPriceDelta,
                (FixedPoint96.Q96 << FixedPoint96.RESOLUTION) * mpsRemainingInAuctionAfterSubmission
            );
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {AuctionStep} from '../libraries/StepLib.sol';

/// @notice Interface for managing auction step storage
interface IStepStorage {
    /// @notice Error thrown when the end block is equal to or before the start block
    error InvalidEndBlock();
    /// @notice Error thrown when the auction is over
    error AuctionIsOver();
    /// @notice Error thrown when the auction data length is invalid
    error InvalidAuctionDataLength();
    /// @notice Error thrown when the block delta in a step is zero
    error StepBlockDeltaCannotBeZero();
    /// @notice Error thrown when the mps is invalid
    /// @param actualMps The sum of the mps times the block delta
    /// @param expectedMps The expected mps of the auction (ConstantsLib.MPS)
    error InvalidStepDataMps(uint256 actualMps, uint256 expectedMps);
    /// @notice Error thrown when the calculated end block is invalid
    /// @param actualEndBlock The calculated end block from the step data
    /// @param expectedEndBlock The expected end block from the constructor
    error InvalidEndBlockGivenStepData(uint64 actualEndBlock, uint64 expectedEndBlock);

    /// @notice The block at which the auction starts
    /// @return The starting block number
    function startBlock() external view returns (uint64);
    /// @notice The block at which the auction ends
    /// @return The ending block number
    function endBlock() external view returns (uint64);

    /// @notice The address pointer to the contract deployed by SSTORE2
    /// @return The address pointer
    function pointer() external view returns (address);

    /// @notice Get the current active auction step
    function step() external view returns (AuctionStep memory);

    /// @notice Emitted when an auction step is recorded
    /// @param startBlock The start block of the auction step
    /// @param endBlock The end block of the auction step
    /// @param mps The percentage of total tokens to sell per block during this auction step, represented in ten-millionths of the total supply (1e7 = 100%)
    event AuctionStepRecorded(uint256 startBlock, uint256 endBlock, uint24 mps);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SSTORE2.sol)
/// @author Saw-mon-and-Natalie (https://github.com/Saw-mon-and-Natalie)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
/// @author Modified from SSTORE3 (https://github.com/Philogy/sstore3)
library SSTORE2 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The proxy initialization code.
    uint256 private constant _CREATE3_PROXY_INITCODE = 0x67363d3d37363d34f03d5260086018f3;

    /// @dev Hash of the `_CREATE3_PROXY_INITCODE`.
    /// Equivalent to `keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3"))`.
    bytes32 internal constant CREATE3_PROXY_INITCODE_HASH =
        0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unable to deploy the storage contract.
    error DeploymentFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         WRITE LOGIC                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Writes `data` into the bytecode of a storage contract and returns its address.
    function write(bytes memory data) internal returns (address pointer) {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data) // Let `l` be `n + 1`. +1 as we prefix a STOP opcode.
            /**
             * ---------------------------------------------------+
             * Opcode | Mnemonic       | Stack     | Memory       |
             * ---------------------------------------------------|
             * 61 l   | PUSH2 l        | l         |              |
             * 80     | DUP1           | l l       |              |
             * 60 0xa | PUSH1 0xa      | 0xa l l   |              |
             * 3D     | RETURNDATASIZE | 0 0xa l l |              |
             * 39     | CODECOPY       | l         | [0..l): code |
             * 3D     | RETURNDATASIZE | 0 l       | [0..l): code |
             * F3     | RETURN         |           | [0..l): code |
             * 00     | STOP           |           |              |
             * ---------------------------------------------------+
             * @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called.
             * Also PUSH2 is used since max contract size cap is 24,576 bytes which is less than 2 ** 16.
             */
            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
            // Deploy a new contract with the generated creation code.
            pointer := create(0, add(data, 0x15), add(n, 0xb))
            if iszero(pointer) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /// @dev Writes `data` into the bytecode of a storage contract with `salt`
    /// and returns its normal CREATE2 deterministic address.
    function writeCounterfactual(bytes memory data, bytes32 salt)
        internal
        returns (address pointer)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data)
            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
            // Deploy a new contract with the generated creation code.
            pointer := create2(0, add(data, 0x15), add(n, 0xb), salt)
            if iszero(pointer) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /// @dev Writes `data` into the bytecode of a storage contract and returns its address.
    /// This uses the so-called "CREATE3" workflow,
    /// which means that `pointer` is agnostic to `data, and only depends on `salt`.
    function writeDeterministic(bytes memory data, bytes32 salt)
        internal
        returns (address pointer)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data)
            mstore(0x00, _CREATE3_PROXY_INITCODE) // Store the `_PROXY_INITCODE`.
            let proxy := create2(0, 0x10, 0x10, salt)
            if iszero(proxy) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, proxy) // Store the proxy's address.
            // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
            // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
            mstore(0x00, 0xd694)
            mstore8(0x34, 0x01) // Nonce of the proxy contract (1).
            pointer := keccak256(0x1e, 0x17)

            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            mstore(add(data, gt(n, 0xfffe)), add(0xfe61000180600a3d393df300, shl(0x40, n)))
            if iszero(
                mul( // The arguments of `mul` are evaluated last to first.
                    extcodesize(pointer),
                    call(gas(), proxy, 0, add(data, 0x15), add(n, 0xb), codesize(), 0x00)
                )
            ) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    ADDRESS CALCULATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the initialization code hash of the storage contract for `data`.
    /// Used for mining vanity addresses with create2crunch.
    function initCodeHash(bytes memory data) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data)
            // Do a out-of-gas revert if `n + 1` is more than 2 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xfffe))
            mstore(data, add(0x61000180600a3d393df300, shl(0x40, n)))
            hash := keccak256(add(data, 0x15), add(n, 0xb))
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /// @dev Equivalent to `predictCounterfactualAddress(data, salt, address(this))`
    function predictCounterfactualAddress(bytes memory data, bytes32 salt)
        internal
        view
        returns (address pointer)
    {
        pointer = predictCounterfactualAddress(data, salt, address(this));
    }

    /// @dev Returns the CREATE2 address of the storage contract for `data`
    /// deployed with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictCounterfactualAddress(bytes memory data, bytes32 salt, address deployer)
        internal
        pure
        returns (address predicted)
    {
        bytes32 hash = initCodeHash(data);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, hash)
            mstore(0x01, shl(96, deployer))
            mstore(0x15, salt)
            predicted := keccak256(0x00, 0x55)
            // Restore the part of the free memory pointer that has been overwritten.
            mstore(0x35, 0)
        }
    }

    /// @dev Equivalent to `predictDeterministicAddress(salt, address(this))`.
    function predictDeterministicAddress(bytes32 salt) internal view returns (address pointer) {
        pointer = predictDeterministicAddress(salt, address(this));
    }

    /// @dev Returns the "CREATE3" deterministic address for `salt` with `deployer`.
    function predictDeterministicAddress(bytes32 salt, address deployer)
        internal
        pure
        returns (address pointer)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, deployer) // Store `deployer`.
            mstore8(0x0b, 0xff) // Store the prefix.
            mstore(0x20, salt) // Store the salt.
            mstore(0x40, CREATE3_PROXY_INITCODE_HASH) // Store the bytecode hash.

            mstore(0x14, keccak256(0x0b, 0x55)) // Store the proxy's address.
            mstore(0x40, m) // Restore the free memory pointer.
            // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01).
            // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex).
            mstore(0x00, 0xd694)
            mstore8(0x34, 0x01) // Nonce of the proxy contract (1).
            pointer := keccak256(0x1e, 0x17)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         READ LOGIC                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `read(pointer, 0, 2 ** 256 - 1)`.
    function read(address pointer) internal view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            data := mload(0x40)
            let n := and(0xffffffffff, sub(extcodesize(pointer), 0x01))
            extcodecopy(pointer, add(data, 0x1f), 0x00, add(n, 0x21))
            mstore(data, n) // Store the length.
            mstore(0x40, add(n, add(data, 0x40))) // Allocate memory.
        }
    }

    /// @dev Equivalent to `read(pointer, start, 2 ** 256 - 1)`.
    function read(address pointer, uint256 start) internal view returns (bytes memory data) {
        /// @solidity memory-safe-assembly
        assembly {
            data := mload(0x40)
            let n := and(0xffffffffff, sub(extcodesize(pointer), 0x01))
            let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
            extcodecopy(pointer, add(data, 0x1f), start, add(l, 0x21))
            mstore(data, mul(sub(n, start), lt(start, n))) // Store the length.
            mstore(0x40, add(data, add(0x40, mload(data)))) // Allocate memory.
        }
    }

    /// @dev Returns a slice of the data on `pointer` from `start` to `end`.
    /// `start` and `end` will be clamped to the range `[0, args.length]`.
    /// The `pointer` MUST be deployed via the SSTORE2 write functions.
    /// Otherwise, the behavior is undefined.
    /// Out-of-gas reverts if `pointer` does not have any code.
    function read(address pointer, uint256 start, uint256 end)
        internal
        view
        returns (bytes memory data)
    {
        /// @solidity memory-safe-assembly
        assembly {
            data := mload(0x40)
            if iszero(lt(end, 0xffff)) { end := 0xffff }
            let d := mul(sub(end, start), lt(start, end))
            extcodecopy(pointer, add(data, 0x1f), start, add(d, 0x01))
            if iszero(and(0xff, mload(add(data, d)))) {
                let n := sub(extcodesize(pointer), 0x01)
                returndatacopy(returndatasize(), returndatasize(), shr(40, n))
                d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
            }
            mstore(data, d) // Store the length.
            mstore(add(add(data, 0x20), d), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(add(data, 0x40), d)) // Allocate memory.
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Each tick contains a pointer to the next price in the linked list
///         and the cumulative currency demand at the tick's price level
struct Tick {
    uint256 next;
    uint256 currencyDemandQ96;
}

/// @title ITickStorage
/// @notice Interface for the TickStorage contract
interface ITickStorage {
    /// @notice Error thrown when the tick spacing is too small
    error TickSpacingTooSmall();
    /// @notice Error thrown when the floor price is zero
    error FloorPriceIsZero();
    /// @notice Error thrown when the floor price is below the minimum floor price
    error FloorPriceTooLow();
    /// @notice Error thrown when the previous price hint is invalid (higher than the new price)
    error TickPreviousPriceInvalid();
    /// @notice Error thrown when the tick price is not increasing
    error TickPriceNotIncreasing();
    /// @notice Error thrown when the price is not at a boundary designated by the tick spacing
    error TickPriceNotAtBoundary();
    /// @notice Error thrown when the tick price is invalid
    error InvalidTickPrice();
    /// @notice Error thrown when trying to update the demand of an uninitialized tick
    error CannotUpdateUninitializedTick();

    /// @notice Emitted when a tick is initialized
    /// @param price The price of the tick
    event TickInitialized(uint256 price);

    /// @notice Emitted when the nextActiveTick is updated
    /// @param price The price of the tick
    event NextActiveTickUpdated(uint256 price);

    /// @notice The price of the next initialized tick above the clearing price
    /// @dev This will be equal to the clearingPrice if no ticks have been initialized yet
    /// @return The price of the next active tick
    function nextActiveTickPrice() external view returns (uint256);

    /// @notice Get the floor price of the auction
    /// @return The minimum price for bids
    function floorPrice() external view returns (uint256);

    /// @notice Get the tick spacing enforced for bid prices
    /// @return The tick spacing value
    function tickSpacing() external view returns (uint256);

    /// @notice Get a tick at a price
    /// @dev The returned tick is not guaranteed to be initialized
    /// @param price The price of the tick, which must be at a boundary designated by the tick spacing
    /// @return The tick at the given price
    function ticks(uint256 price) external view returns (Tick memory);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Currency} from '../libraries/CurrencyLibrary.sol';
import {IERC20Minimal} from './external/IERC20Minimal.sol';

/// @notice Interface for token and currency storage operations
interface ITokenCurrencyStorage {
    /// @notice Error thrown when the token is the native currency
    error TokenIsAddressZero();
    /// @notice Error thrown when the token and currency are the same
    error TokenAndCurrencyCannotBeTheSame();
    /// @notice Error thrown when the total supply is zero
    error TotalSupplyIsZero();
    /// @notice Error thrown when the total supply is too large
    error TotalSupplyIsTooLarge();
    /// @notice Error thrown when the funds recipient is the zero address
    error FundsRecipientIsZero();
    /// @notice Error thrown when the tokens recipient is the zero address
    error TokensRecipientIsZero();
    /// @notice Error thrown when the currency cannot be swept
    error CannotSweepCurrency();
    /// @notice Error thrown when the tokens cannot be swept
    error CannotSweepTokens();
    /// @notice Error thrown when the auction has not graduated
    error NotGraduated();

    /// @notice Emitted when the tokens are swept
    /// @param tokensRecipient The address of the tokens recipient
    /// @param tokensAmount The amount of tokens swept
    event TokensSwept(address indexed tokensRecipient, uint256 tokensAmount);

    /// @notice Emitted when the currency is swept
    /// @param fundsRecipient The address of the funds recipient
    /// @param currencyAmount The amount of currency swept
    event CurrencySwept(address indexed fundsRecipient, uint256 currencyAmount);

    /// @notice The currency being raised in the auction
    function currency() external view returns (Currency);

    /// @notice The token being sold in the auction
    function token() external view returns (IERC20Minimal);

    /// @notice The total supply of tokens to sell
    function totalSupply() external view returns (uint128);

    /// @notice The recipient of any unsold tokens at the end of the auction
    function tokensRecipient() external view returns (address);

    /// @notice The recipient of the raised Currency from the auction
    function fundsRecipient() external view returns (address);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Minimal ERC20 interface
interface IERC20Minimal {
    /// @notice Returns an account's balance in the token
    /// @param account The account for which to look up the number of tokens it has, i.e. its balance
    /// @return The number of tokens held by the account
    function balanceOf(address account) external view returns (uint256);

    /// @notice Transfers the amount of token from the `msg.sender` to the recipient
    /// @param recipient The account that will receive the amount transferred
    /// @param amount The number of tokens to send from the sender to the recipient
    /// @return Returns true for a successful transfer, false for an unsuccessful transfer
    function transfer(address recipient, uint256 amount) external returns (bool);

    /// @notice Approves the spender to spend the amount of tokens from the `msg.sender`
    /// @param spender The account that will be allowed to spend the amount
    /// @param amount The number of tokens to allow the spender to spend
    /// @return Returns true for a successful approval, false for an unsuccessful approval
    function approve(address spender, uint256 amount) external returns (bool);
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "solady/=lib/solady/src/",
    "permit2/=lib/permit2/",
    "v4-periphery/=lib/v4-periphery/",
    "continuous-clearing-auction/=src/",
    "test/=test/",
    "btt/=test/btt/",
    "@ensdomains/=lib/v4-periphery/lib/v4-core/node_modules/@ensdomains/",
    "@uniswap/v4-core/=lib/v4-periphery/lib/v4-core/",
    "ds-test/=lib/permit2/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/forge-gas-snapshot/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "hardhat/=lib/v4-periphery/lib/v4-core/node_modules/hardhat/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solmate/=lib/permit2/lib/solmate/",
    "v4-core/=lib/v4-periphery/lib/v4-core/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 88888888
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint128","name":"_totalSupply","type":"uint128"},{"components":[{"internalType":"address","name":"currency","type":"address"},{"internalType":"address","name":"tokensRecipient","type":"address"},{"internalType":"address","name":"fundsRecipient","type":"address"},{"internalType":"uint64","name":"startBlock","type":"uint64"},{"internalType":"uint64","name":"endBlock","type":"uint64"},{"internalType":"uint64","name":"claimBlock","type":"uint64"},{"internalType":"uint256","name":"tickSpacing","type":"uint256"},{"internalType":"address","name":"validationHook","type":"address"},{"internalType":"uint256","name":"floorPrice","type":"uint256"},{"internalType":"uint128","name":"requiredCurrencyRaised","type":"uint128"},{"internalType":"bytes","name":"auctionStepsData","type":"bytes"}],"internalType":"struct AuctionParameters","name":"_parameters","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AuctionIsNotOver","type":"error"},{"inputs":[],"name":"AuctionIsOver","type":"error"},{"inputs":[],"name":"AuctionNotStarted","type":"error"},{"inputs":[],"name":"AuctionSoldOut","type":"error"},{"inputs":[{"internalType":"address","name":"expectedOwner","type":"address"},{"internalType":"address","name":"receivedOwner","type":"address"}],"name":"BatchClaimDifferentOwner","type":"error"},{"inputs":[],"name":"BidAlreadyExited","type":"error"},{"inputs":[],"name":"BidAmountTooSmall","type":"error"},{"inputs":[{"internalType":"uint256","name":"bidId","type":"uint256"}],"name":"BidIdDoesNotExist","type":"error"},{"inputs":[],"name":"BidMustBeAboveClearingPrice","type":"error"},{"inputs":[],"name":"BidNotExited","type":"error"},{"inputs":[],"name":"BidOwnerCannotBeZeroAddress","type":"error"},{"inputs":[],"name":"CannotExitBid","type":"error"},{"inputs":[],"name":"CannotPartiallyExitBidBeforeEndBlock","type":"error"},{"inputs":[],"name":"CannotPartiallyExitBidBeforeGraduation","type":"error"},{"inputs":[],"name":"CannotSweepCurrency","type":"error"},{"inputs":[],"name":"CannotSweepTokens","type":"error"},{"inputs":[],"name":"CannotUpdateUninitializedTick","type":"error"},{"inputs":[],"name":"CheckpointBlockNotIncreasing","type":"error"},{"inputs":[],"name":"ClaimBlockIsBeforeEndBlock","type":"error"},{"inputs":[],"name":"CurrencyIsNotNative","type":"error"},{"inputs":[],"name":"ERC20TransferFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"nextTick","type":"uint256"},{"internalType":"uint256","name":"maxBidPrice","type":"uint256"}],"name":"FloorPriceAndTickSpacingGreaterThanMaxBidPrice","type":"error"},{"inputs":[],"name":"FloorPriceAndTickSpacingTooLarge","type":"error"},{"inputs":[],"name":"FloorPriceIsZero","type":"error"},{"inputs":[],"name":"FloorPriceTooLow","type":"error"},{"inputs":[],"name":"FundsRecipientIsZero","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidAuctionDataLength","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"uint256","name":"maxBidPrice","type":"uint256"}],"name":"InvalidBidPriceTooHigh","type":"error"},{"inputs":[],"name":"InvalidBidUnableToClear","type":"error"},{"inputs":[],"name":"InvalidEndBlock","type":"error"},{"inputs":[{"internalType":"uint64","name":"actualEndBlock","type":"uint64"},{"internalType":"uint64","name":"expectedEndBlock","type":"uint64"}],"name":"InvalidEndBlockGivenStepData","type":"error"},{"inputs":[],"name":"InvalidLastFullyFilledCheckpointHint","type":"error"},{"inputs":[],"name":"InvalidOutbidBlockCheckpointHint","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualMps","type":"uint256"},{"internalType":"uint256","name":"expectedMps","type":"uint256"}],"name":"InvalidStepDataMps","type":"error"},{"inputs":[],"name":"InvalidTickPrice","type":"error"},{"inputs":[],"name":"InvalidTokenAmountReceived","type":"error"},{"inputs":[],"name":"MpsRemainingIsZero","type":"error"},{"inputs":[],"name":"NativeTransferFailed","type":"error"},{"inputs":[],"name":"NotClaimable","type":"error"},{"inputs":[],"name":"NotGraduated","type":"error"},{"inputs":[],"name":"StepBlockDeltaCannotBeZero","type":"error"},{"inputs":[],"name":"StepLib__InvalidOffsetNotAtStepBoundary","type":"error"},{"inputs":[],"name":"StepLib__InvalidOffsetTooLarge","type":"error"},{"inputs":[],"name":"TickPreviousPriceInvalid","type":"error"},{"inputs":[],"name":"TickPriceNotAtBoundary","type":"error"},{"inputs":[],"name":"TickPriceNotIncreasing","type":"error"},{"inputs":[],"name":"TickSpacingTooSmall","type":"error"},{"inputs":[],"name":"TokenAndCurrencyCannotBeTheSame","type":"error"},{"inputs":[],"name":"TokenIsAddressZero","type":"error"},{"inputs":[],"name":"TokenTransferFailed","type":"error"},{"inputs":[],"name":"TokensNotReceived","type":"error"},{"inputs":[],"name":"TokensRecipientIsZero","type":"error"},{"inputs":[],"name":"TotalSupplyIsTooLarge","type":"error"},{"inputs":[],"name":"TotalSupplyIsZero","type":"error"},{"inputs":[{"internalType":"bytes","name":"reason","type":"bytes"}],"name":"ValidationHookCallFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"startBlock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endBlock","type":"uint256"},{"indexed":false,"internalType":"uint24","name":"mps","type":"uint24"}],"name":"AuctionStepRecorded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"bidId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensFilled","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"currencyRefunded","type":"uint256"}],"name":"BidExited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"}],"name":"BidSubmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"blockNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"clearingPrice","type":"uint256"},{"indexed":false,"internalType":"uint24","name":"cumulativeMps","type":"uint24"}],"name":"CheckpointUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"blockNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"clearingPrice","type":"uint256"}],"name":"ClearingPriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fundsRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"currencyAmount","type":"uint256"}],"name":"CurrencySwept","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"NextActiveTickUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"TickInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"bidId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensFilled","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalSupply","type":"uint256"}],"name":"TokensReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokensRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensAmount","type":"uint256"}],"name":"TokensSwept","type":"event"},{"inputs":[],"name":"MAX_BID_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BLOCK_NUMBER","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TICK_PTR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bidId","type":"uint256"}],"name":"bids","outputs":[{"components":[{"internalType":"uint64","name":"startBlock","type":"uint64"},{"internalType":"uint24","name":"startCumulativeMps","type":"uint24"},{"internalType":"uint64","name":"exitedBlock","type":"uint64"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"amountQ96","type":"uint256"},{"internalType":"uint256","name":"tokensFilled","type":"uint256"}],"internalType":"struct Bid","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[{"components":[{"internalType":"uint256","name":"clearingPrice","type":"uint256"},{"internalType":"ValueX7","name":"currencyRaisedAtClearingPriceQ96_X7","type":"uint256"},{"internalType":"uint256","name":"cumulativeMpsPerPrice","type":"uint256"},{"internalType":"uint24","name":"cumulativeMps","type":"uint24"},{"internalType":"uint64","name":"prev","type":"uint64"},{"internalType":"uint64","name":"next","type":"uint64"}],"internalType":"struct Checkpoint","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"blockNumber","type":"uint64"}],"name":"checkpoints","outputs":[{"components":[{"internalType":"uint256","name":"clearingPrice","type":"uint256"},{"internalType":"ValueX7","name":"currencyRaisedAtClearingPriceQ96_X7","type":"uint256"},{"internalType":"uint256","name":"cumulativeMpsPerPrice","type":"uint256"},{"internalType":"uint24","name":"cumulativeMps","type":"uint24"},{"internalType":"uint64","name":"prev","type":"uint64"},{"internalType":"uint64","name":"next","type":"uint64"}],"internalType":"struct Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimBlock","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_bidId","type":"uint256"}],"name":"claimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256[]","name":"_bidIds","type":"uint256[]"}],"name":"claimTokensBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clearingPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currency","outputs":[{"internalType":"Currency","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currencyRaised","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currencyRaisedQ96_X7","outputs":[{"internalType":"ValueX7","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endBlock","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bidId","type":"uint256"}],"name":"exitBid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bidId","type":"uint256"},{"internalType":"uint64","name":"lastFullyFilledCheckpointBlock","type":"uint64"},{"internalType":"uint64","name":"outbidBlock","type":"uint64"}],"name":"exitPartiallyFilledBid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"floorPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundsRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isGraduated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastCheckpointedBlock","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestCheckpoint","outputs":[{"components":[{"internalType":"uint256","name":"clearingPrice","type":"uint256"},{"internalType":"ValueX7","name":"currencyRaisedAtClearingPriceQ96_X7","type":"uint256"},{"internalType":"uint256","name":"cumulativeMpsPerPrice","type":"uint256"},{"internalType":"uint24","name":"cumulativeMps","type":"uint24"},{"internalType":"uint64","name":"prev","type":"uint64"},{"internalType":"uint64","name":"next","type":"uint64"}],"internalType":"struct Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextActiveTickPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextBidId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onTokensReceived","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pointer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startBlock","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"step","outputs":[{"components":[{"internalType":"uint24","name":"mps","type":"uint24"},{"internalType":"uint64","name":"startBlock","type":"uint64"},{"internalType":"uint64","name":"endBlock","type":"uint64"}],"internalType":"struct AuctionStep","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"bytes","name":"hookData","type":"bytes"}],"name":"submitBid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"prevTickPrice","type":"uint256"},{"internalType":"bytes","name":"hookData","type":"bytes"}],"name":"submitBid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"sumCurrencyDemandAboveClearingQ96","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sweepCurrency","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sweepCurrencyBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sweepUnsoldTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sweepUnsoldTokensBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tickSpacing","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"name":"ticks","outputs":[{"components":[{"internalType":"uint256","name":"next","type":"uint256"},{"internalType":"uint256","name":"currencyDemandQ96","type":"uint256"}],"internalType":"struct Tick","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20Minimal","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokensRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCleared","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalClearedQ96_X7","outputs":[{"internalType":"ValueX7","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"validationHook","outputs":[{"internalType":"contract IValidationHook","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

610280604052348015610010575f80fd5b506040516154e73803806154e783398101604081905261002f91610a8e565b82815f015183836020015184604001518561012001518660c0015187610100015188610140015189606001518a60800151806001600160401b0316826001600160401b03161061009257604051633dea3a3b60e11b815260040160405180910390fd5b6001600160401b03808316608052811660a052825160c0525f6100b484610415565b90506100bf81610456565b6001600160a01b03811660e0526100d46105d5565b505050505060028210156100fb57604051630b7f3b4b60e11b815260040160405180910390fd5b6101208290525f81900361012257604051636773fbb160e11b815260040160405180910390fd5b61013163ffffffff6001610bd1565b81101561015157604051632f53fe5160e21b815260040160405180910390fd5b6101008190525f19610162826107b1565b555f1960078190556040519081527fb9a86892440ed5515518351623ecfc523d283b21e92f1505e533ef26137be5b09060200160405180910390a17f7fdd20e2dbf90ff60a7d9be5ad62f1ec6d9d9cba8b36174a3839cafd059f0958610100516040516101d191815260200190565b60405180910390a150506001600160a01b0386166102025760405163591b78bb60e01b815260040160405180910390fd5b846001600160a01b0316866001600160a01b031603610234576040516325fe0f5960e21b815260040160405180910390fd5b836001600160801b03165f0361025d57604051634253080360e01b815260040160405180910390fd5b6c100000000000000000000000006001600160801b03851611156102945760405163ef88f51d60e01b815260040160405180910390fd5b6001600160a01b0383166102bb57604051638b64f4ed60e01b815260040160405180910390fd5b6001600160a01b0382166102e2576040516352b5cbf760e01b815260040160405180910390fd5b6001600160a01b0380871661016052858116610140526001600160801b03851661018052600160601b600160e01b03606086811b82166101a0528583166101c0529184166101e0526103379183901b166107f0565b6102005250505060a0808501516001600160401b0390811661024081905260e08701516001600160a01b0316610260529151161115925061038e915050576040516312078d1960e31b815260040160405180910390fd5b6101805161039b90610804565b61022081905260c082015111806103c857508060c00151610220516103c09190610be4565b816101000151115b1561040d578060c001518161010001516103e29190610bd1565b610220516040516366eb34e560e11b8152600481019290925260248201526044015b60405180910390fd5b505050610df2565b5f81518060401b6bfe61000180600a3d393df3000161fffe8211840152600b8101601584015ff09150816104505763301164255f526004601cfd5b90915290565b5f6104696001600160a01b038316610883565b905080515f14806104865750600881516104839190610c0b565b15155b80610494575060c051815114155b156104b257604051630157988b60e31b815260040160405180910390fd5b5f805f5b60c051811015610542575f806104cc86846108af565b915091508064ffffffffff165f036104f75760405163039b7d7b60e41b815260040160405180910390fd5b6105068162ffffff8416610c1e565b6105179064ffffffffff1686610bd1565b945061052a64ffffffffff821685610c45565b9350505060088161053b9190610bd1565b90506104b6565b506298968082146105725760405163054d62c160e51b815260048101839052629896806024820152604401610404565b5f816080516105819190610c45565b905060a0516001600160401b0316816001600160401b0316146105ce5760a051604051634c179fb760e01b81526001600160401b0380841660048301529091166024820152604401610404565b5050505050565b604080516060810182525f808252602082018190529181019190915260c0516004541061061557604051635f0ae8b560e01b815260040160405180910390fd5b6004545f9061063c90610629600882610bd1565b60e0516001600160a01b03169190610919565b61064590610c64565b60055490915060e882901c9060c083901c906b01000000000000000000000090046001600160401b03165f81900361067c57506080515b5f61068e64ffffffffff841683610c45565b6040805160608101825262ffffff87168082526001600160401b038681166020840181905290851692909301829052600580546001600160581b031916909117630100000090930292909217600160581b600160981b0319166b010000000000000000000000909102179055600480549192506008915f90610711908490610bd1565b9091555050604080516001600160401b0384811682528316602082015262ffffff86168183015290517f6863f2b489f9186bf89231dc73aa0e9836f536b9ddb0f708f74260ed3160f2979181900360600190a150506040805160608101825260055462ffffff811682526001600160401b0363010000008204811660208401526b0100000000000000000000009091041691810191909152949350505050565b5f61012051826107c19190610c0b565b156107df5760405163d76fb50f60e01b815260040160405180910390fd5b505f90815260066020526040902090565b5f6107fe6298968083610ca2565b92915050565b5f674000000000000000826001600160801b03161161082b57506001600160a01b03919050565b5f60026108456001600160801b0385166001609a1b610cb9565b6001600160a01b03166108589190610dca565b90505f6108726001600160801b038516600160de1b610ddf565b828118908311029091189392505050565b60405164ffffffffff5f19833b0116602181015f601f8401853c80825260408201810160405250919050565b5f80835183106108d25760405163c77eafef60e01b815260040160405180910390fd5b6108dd600884610c0b565b156108fb57604051631106b04360e11b815260040160405180910390fd5b505060209101015160e881901c9160c09190911c64ffffffffff1690565b60405161ffff821061092b5761ffff91505b818310838303026001810184601f8401873c8082015160ff16610965576001853b038060281c3d3d3e808403818511028203858211029150505b8082525f81602084010152806040830101604052509392505050565b80516001600160a01b0381168114610997575f80fd5b919050565b80516001600160801b0381168114610997575f80fd5b634e487b7160e01b5f52604160045260245ffd5b60405161016081016001600160401b03811182821017156109e9576109e96109b2565b60405290565b80516001600160401b0381168114610997575f80fd5b5f82601f830112610a14575f80fd5b81516001600160401b03811115610a2d57610a2d6109b2565b604051601f8201601f19908116603f011681016001600160401b0381118282101715610a5b57610a5b6109b2565b604052818152838201602001851015610a72575f80fd5b8160208501602083015e5f918101602001919091529392505050565b5f805f60608486031215610aa0575f80fd5b610aa984610981565b9250610ab76020850161099c565b60408501519092506001600160401b03811115610ad2575f80fd5b84016101608187031215610ae4575f80fd5b610aec6109c6565b610af582610981565b8152610b0360208301610981565b6020820152610b1460408301610981565b6040820152610b25606083016109ef565b6060820152610b36608083016109ef565b6080820152610b4760a083016109ef565b60a082015260c08281015190820152610b6260e08301610981565b60e08201526101008281015190820152610b7f610120830161099c565b6101208201526101408201516001600160401b03811115610b9e575f80fd5b610baa88828501610a05565b6101408301525080925050509250925092565b634e487b7160e01b5f52601160045260245ffd5b808201808211156107fe576107fe610bbd565b818103818111156107fe576107fe610bbd565b634e487b7160e01b5f52601260045260245ffd5b5f82610c1957610c19610bf7565b500690565b64ffffffffff8181168382160290811690818114610c3e57610c3e610bbd565b5092915050565b6001600160401b0381811683821601908111156107fe576107fe610bbd565b805160208201516001600160c01b0319811691906008821015610c9b576001600160c01b0319600883900360031b81901b82161692505b5050919050565b80820281158282048414176107fe576107fe610bbd565b5f6001600160a01b03831680610cd157610cd1610bf7565b6001600160a01b03929092169190910492915050565b6001815b6001841115610d2257808504811115610d0657610d06610bbd565b6001841615610d1457908102905b60019390931c928002610ceb565b935093915050565b5f82610d38575060016107fe565b81610d4457505f6107fe565b8160018114610d5a5760028114610d6457610d80565b60019150506107fe565b60ff841115610d7557610d75610bbd565b50506001821b6107fe565b5060208310610133831016604e8410600b8410161715610da3575081810a6107fe565b610daf5f198484610ce7565b805f1904821115610dc257610dc2610bbd565b029392505050565b5f610dd860ff841683610d2a565b9392505050565b5f82610ded57610ded610bf7565b500490565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e051610200516102205161024051610260516144ec610ffb5f395f81816106760152612bf601525f8181610494015281816115c6015261206a01525f81816107e801528181612ac60152612b1b01525f8181610e500152818161166e0152818161189501528181611a3801528181611be101528181611e26015261211201525f81816104c6015281816128ac01526128d301525f8181610aa0015281816127a801526127cf01525f6118dd01525f81816103c001528181610bfa01528181610d14015281816119280152818161354c015281816135b601528181613628015261385b01525f8181610a6e01528181610c2e015281816116f5015281816122cc015261278601525f8181610a3c01528181611f65015281816120160152818161246d015261288a01525f81816109370152818161250f015261380601525f818161073f01528181610acd01526134ee01525f81816103f80152613cac01525f613c2801525f81816102d301528181610e77015281816113c30152818161162d015281816117b00152818161181601528181611957015281816119bd01528181611aa301528181611e55015281816120d1015281816123340152818161296f015261375b01525f81816105ce015281816129950152613d1201526144ec5ff3fe6080604052600436106102c1575f3560e01c80638e4deb1711610170578063b363e953116100d1578063d2fc583a11610087578063e5a6b10f11610062578063e5a6b10f14610a2e578063fc0c546a14610a60578063fd63755714610a92575f80fd5b8063d2fc583a1461095b578063dc2690491461096f578063e25fe17514610982575f80fd5b8063c2c4c5c1116100b7578063c2c4c5c114610900578063cab8bedc14610914578063d0c93a7c14610929575f80fd5b8063b363e953146108cd578063b8f163d6146108e1575f80fd5b8063a52c872811610126578063ad7473e51161010c578063ad7473e5146107c2578063ae91fa33146107d7578063b122db601461080a575f80fd5b8063a52c87281461079b578063a9176e45146107ae575f80fd5b80639363c812116101565780639363c81214610731578063998ba4fc146107635780639e5f260214610777575f80fd5b80638e4deb171461069a578063907c0f92146106b9575f80fd5b806337dfbc4b1161022557806348cd4cb1116101db57806360d3ded7116101b657806360d3ded7146106405780637c121574146106545780638134f02714610668575f80fd5b806348cd4cb1146105c0578063534cb30d146105f25780635dd13ca71461062c575f80fd5b80633e9d91741161020b5780633e9d9174146104ea5780634423c5f1146104fe57806346e04a2f146105a1575f80fd5b806337dfbc4b146104865780633b6fd2cf146104b8575f80fd5b806318160ddd1161027a57806332a0f2d71161026057806332a0f2d71461043d578063331f2f651461045157806336dec5f214610467575f80fd5b806318160ddd1461039e5780632f5f3b3c146103ea575f80fd5b8063111a977d116102aa578063111a977d1461035257806311ea09d01461036d578063140fe8ee1461038b575f80fd5b8063083c6323146102c55780630e9a83cd14610311575b5f80fd5b3480156102d0575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405167ffffffffffffffff90911681526020015b60405180910390f35b34801561031c575f80fd5b506103447fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81565b604051908152602001610308565b34801561035d575f80fd5b506102f367ffffffffffffffff81565b348015610378575f80fd5b5060035467ffffffffffffffff166102f3565b610344610399366004613fc5565b610ac4565b3480156103a9575f80fd5b506040516fffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610308565b3480156103f5575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610308565b348015610448575f80fd5b50610344610afd565b34801561045c575f80fd5b50610465610bbf565b005b348015610472575f80fd5b50610465610481366004614046565b610d76565b348015610491575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000006102f3565b3480156104c3575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610418565b3480156104f5575f80fd5b506103446114c6565b348015610509575f80fd5b5061051d61051836600461407f565b6114f6565b60405161030891905f60e08201905067ffffffffffffffff835116825262ffffff602084015116602083015267ffffffffffffffff60408401511660408301526060830151606083015273ffffffffffffffffffffffffffffffffffffffff608084015116608083015260a083015160a083015260c083015160c083015292915050565b3480156105ac575f80fd5b506104656105bb36600461407f565b6115c4565b3480156105cb575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000006102f3565b3480156105fd575f80fd5b5061061161060c36600461407f565b611773565b60408051825181526020928301519281019290925201610308565b348015610637575f80fd5b506104656117ae565b34801561064b575f80fd5b50600754610344565b34801561065f575f80fd5b50610465611955565b348015610673575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610418565b3480156106a5575f80fd5b506104656106b436600461407f565b611aa1565b3480156106c4575f80fd5b506106cd611d29565b60405161030891905f60c08201905082518252602083015160208301526040830151604083015262ffffff606084015116606083015267ffffffffffffffff608084015116608083015267ffffffffffffffff60a08401511660a083015292915050565b34801561073c575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610344565b34801561076e575f80fd5b50610344611e14565b348015610782575f80fd5b5061078b611e1d565b6040519015158152602001610308565b6103446107a9366004614096565b611e4a565b3480156107b9575f80fd5b50600c54610344565b3480156107cd575f80fd5b5061034460095481565b3480156107e2575f80fd5b506103447f000000000000000000000000000000000000000000000000000000000000000081565b348015610815575f80fd5b506106cd610824366004614109565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b3480156108d8575f80fd5b50600b54610344565b3480156108ec575f80fd5b506104656108fb366004614122565b612068565b34801561090b575f80fd5b506106cd6122f9565b34801561091f575f80fd5b5061034460085481565b348015610934575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610344565b348015610966575f80fd5b50600a54610344565b34801561097a575f80fd5b505f54610344565b34801561098d575f80fd5b506109f5604080516060810182525f8082526020820181905291810191909152506040805160608101825260055462ffffff8116825267ffffffffffffffff63010000008204811660208401526b010000000000000000000000909104169181019190915290565b60408051825162ffffff16815260208084015167ffffffffffffffff908116918301919091529282015190921690820152606001610308565b348015610a39575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610418565b348015610a6b575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610418565b348015610a9d575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610418565b5f610af38686867f00000000000000000000000000000000000000000000000000000000000000008787611e4a565b9695505050505050565b6003545f90610bb99067ffffffffffffffff166040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b51919050565b600d5460ff1615610bcc57565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000006fffffffffffffffffffffffffffffffff16907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610c88573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cac91906141a3565b1015610ce4576040517f268128ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040517f00000000000000000000000000000000000000000000000000000000000000006fffffffffffffffffffffffffffffffff1681527f17cca138a663106b4c25a247e2d9238888fe37188d83b7bb7287bc1c0a4df82a9060200160405180910390a1565b5f610d7f6122f9565b90505f610d8b85612375565b6040805160e081018252825467ffffffffffffffff808216835262ffffff6801000000000000000083041660208401526b0100000000000000000000009091041691810182905260018301546060820152600283015473ffffffffffffffffffffffffffffffffffffffff166080820152600383015460a082015260049092015460c083015290915015610e4b576040517f3458822100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a547f00000000000000000000000000000000000000000000000000000000000000001115610eea577f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff164310610eb857610eb1855f806123c4565b5050505050565b6040517f8af474ff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606081015181515f610f9f876040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b905082815f01511015806110635750826110608260a001516040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b51105b8061108157508167ffffffffffffffff168767ffffffffffffffff16105b156110b8576040517f6a00945500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611166836040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b82519091505f908190156111855761117f8484896124ef565b90925090505b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915267ffffffffffffffff8a16156113c1576040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152438b67ffffffffffffffff160361120f5750886112bf565b6112bc8b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b90505b61137081608001516040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b915087815f01511115806113845750815188105b156113bb576040517f516cb4ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50611462565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff16431015611425576040517fe08d8a4f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50875188908714611462576040517f0ba9845700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80518790036114ad575f61147588612509565b6001015490505f8061148c8b84866020015161257c565b909250905061149b82876141e7565b95506114a781866141e7565b94505050505b6114b88c84846123c4565b505050505050505050505050565b5f6114f16114ec6c01000000000000000000000000600b5461258990919063ffffffff16565b61259d565b905090565b6040805160e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915261153782612375565b6040805160e081018252825467ffffffffffffffff808216835262ffffff6801000000000000000083041660208401526b010000000000000000000000909104169181019190915260018201546060820152600282015473ffffffffffffffffffffffffffffffffffffffff166080820152600382015460a082015260049091015460c082015292915050565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff16431015611628576040517f6247a84e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003547f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff908116911614611669576116676122f9565b505b600a547f000000000000000000000000000000000000000000000000000000000000000011156116c5576040517fd66173a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f806116d0836125ab565b9092509050801561176e5761171c73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016838361263e565b8173ffffffffffffffffffffffffffffffffffffffff16837f880f2ef2613b092f1a0a819f294155c98667eb294b7e6bf7a3810278142c1a1c8360405161176591815260200190565b60405180910390a35b505050565b604080518082019091525f808252602082015261178f82612509565b6040805180820190915281548152600190910154602082015292915050565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff16431015611811576040517e175ba800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003547f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff908116911614611852576118506122f9565b505b6009541561188c576040517f8fd6c3f900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6118b9600a547f0000000000000000000000000000000000000000000000000000000000000000111590565b156119145761190d6114ec6c01000000000000000000000000611907600b546119017f0000000000000000000000000000000000000000000000000000000000000000612749565b90612757565b90612589565b9050611949565b506fffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165b61195281612762565b50565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff164310156119b8576040517e175ba800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003547f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff9081169116146119f9576119f76122f9565b505b60085415611a33576040517f76ae8ed700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a547f00000000000000000000000000000000000000000000000000000000000000001115611a8f576040517fd66173a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a9f611a9a612840565b612866565b565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff16431015611b04576040517e175ba800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611b0e82612375565b6040805160e081018252825467ffffffffffffffff808216835262ffffff6801000000000000000083041660208401526b0100000000000000000000009091041691810182905260018301546060820152600283015473ffffffffffffffffffffffffffffffffffffffff166080820152600383015460a082015260049092015460c083015290915015611bce576040517f3458822100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611bd7612939565b9050611c05600a547f0000000000000000000000000000000000000000000000000000000000000000111590565b611c145761176e835f806123c4565b8051606083015111611c52576040517f0ba9845700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611d03835f01516040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b90505f80611d128484876124ef565b91509150611d218683836123c4565b505050505050565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091526003546114f19067ffffffffffffffff166040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b5f6114f1612840565b5f6114f1600a547f0000000000000000000000000000000000000000000000000000000000000000111590565b5f611e53612993565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff164310611eb6576040517f5f0ae8b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b856fffffffffffffffffffffffffffffffff165f03611f01576040517fc873160800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516611f4e576040517f4076a98c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016611fd957856fffffffffffffffffffffffffffffffff163414611fd4576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61204f565b3415612011576040517fe3e0a9bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61204f7f00000000000000000000000000000000000000000000000000000000000000003330896fffffffffffffffffffffffffffffffff16612a33565b61205d878787878787612ac3565b979650505050505050565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff164310156120cc576040517f6247a84e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003547f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff90811691161461210d5761210b6122f9565b505b600a547f00000000000000000000000000000000000000000000000000000000000000001115612169576040517fd66173a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805b828110156122ab575f8061219786868581811061218b5761218b6141fa565b905060200201356125ab565b915091508673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614612225576040517f1515875c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8089166004830152831660248201526044015b60405180910390fd5b61222f81856141e7565b935080156122a1578173ffffffffffffffffffffffffffffffffffffffff16868685818110612260576122606141fa565b905060200201357f880f2ef2613b092f1a0a819f294155c98667eb294b7e6bf7a3810278142c1a1c8360405161229891815260200190565b60405180910390a35b505060010161216c565b5080156122f3576122f373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016858361263e565b50505050565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152612332612993565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1643111561236c576114f1612939565b6114f143612d90565b5f805482106123b3576040517f9076e8b90000000000000000000000000000000000000000000000000000000081526004810183905260240161221c565b505f90815260016020526040902090565b5f6123ce84612375565b6002810154600382015491925073ffffffffffffffffffffffffffffffffffffffff16905f90606090612402908690614227565b6004850187905584547fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff166b0100000000000000000000004367ffffffffffffffff1602178555901c905080156124945761249473ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016838361263e565b604080518681526020810183905273ffffffffffffffffffffffffffffffffffffffff84169188917f054fe6469466a0b4d2a6ae4b100e5f9c494c958f04b4000f44d470088dd97930910160405180910390a3505050505050565b5f806124fc858585612f22565b915091505b935093915050565b5f6125347f000000000000000000000000000000000000000000000000000000000000000083614267565b1561256b576040517fd76fb50f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f90815260066020526040902090565b5f806124fc858585612f61565b5f612594828461427a565b90505b92915050565b5f612597629896808361427a565b5f805f6125b784612375565b80549091506b010000000000000000000000900467ffffffffffffffff165f0361260d576040517f7e13888200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002810154600490910180545f90915573ffffffffffffffffffffffffffffffffffffffff90911694909350915050565b5f73ffffffffffffffffffffffffffffffffffffffff84166126a0575f805f8085875af190508061269b576040517ff4b3b1bc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122f3565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015282602482015260205f6044835f895af13d15601f3d1160015f511416171691505f81525f60208201525f604082015250806122f3576040517ff27f64e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f612597629896808361428d565b5f6125948284614227565b4360095580156127cd576127cd73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000008361263e565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff167fdd9a81eb1b5197489c3ccfdab7b542e2e6dbdcf4120324e2688fab56fd23f98b8260405161283591815260200190565b60405180910390a250565b5f6114f16114ec6c01000000000000000000000000600a5461258990919063ffffffff16565b4360085580156128d1576128d173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000008361263e565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff167f053dfa7183794b221b03c5109dfb5a07b67d719cb3e98262d48bc66b2a132ad38260405161283591815260200190565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091526114f17f0000000000000000000000000000000000000000000000000000000000000000612d90565b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff164310156129f7576040517feffaea8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5460ff16611a9f576040517f8d6b8a8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518460601b60601c60748201528160548201528260348201528360601b60208201526f36c7851600000000000000000000000081526e22d473030f116ddee9f6b43ac78ba36001461480612a895750803b15155b80873b15105f386084601087015f875af116612aba57677939f4248757f0fd5f5260048460a01c151560021b601801fd5b50505050505050565b5f7f0000000000000000000000000000000000000000000000000000000000000000871115612b47576040517fae9d6dc2000000000000000000000000000000000000000000000000000000008152600481018890527f0000000000000000000000000000000000000000000000000000000000000000602482015260440161221c565b5f612b506122f9565b9050612b5b81612fce565b62ffffff165f03612b98576040517f5cd5436100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80518811612bd2576040517f5f259e5200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612bdc8589612fe2565b612c2173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016898989338989613179565b6040805160e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c08101919091525f606060ff16896fffffffffffffffffffffffffffffffff16901b9050612c8681898c8660600151613280565b945091505f612c9483613415565b9050612ca08b82613484565b80600c5f828254612cb191906141e7565b90915550612ce49050629896807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61427a565b600c5410612d1e576040517fa37fb9e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518c81526fffffffffffffffffffffffffffffffff8c16602082015273ffffffffffffffffffffffffffffffffffffffff8b169187917f650baad5cd8ca09b8f580be220fa04ce2ba905a041f764b6a3fe2c848eb70540910160405180910390a3505050509695505050505050565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915260035467ffffffffffffffff908116908316819003612de957612de2611d29565b9392505050565b612df1611d29565b91505f612dfd836134e8565b83519091508114612e58578083525f6020808501919091526040805167ffffffffffffffff871681529182018390527f30adbe996d7a69a21fdebcc1f8a46270bf6c22d505a7d872c1ab4767aa707609910160405180910390a15b5f80612e6486856136b0565b915091505f612e91836020015167ffffffffffffffff168667ffffffffffffffff16808218908210021890565b612e9b90886142a4565b835162ffffff16810292909201919050612eb586836137c1565b9550612ec18688613944565b85516060808801516040805167ffffffffffffffff8c168152602081019490945262ffffff909116908301527ff1e4b6d7d0d7c5deb6393a39862d66a2f2ecb034f3283a8a597f9bf0c36f76fa910160405180910390a15050505050919050565b5f80612f558385604001518760400151612f3c9190614227565b86606001518860600151612f5091906142c4565b613a9d565b90969095509350505050565b5f80835f03612f7457505f905080612501565b5f612f7e86613afe565b612f8d9062ffffff168661428d565b60a0870151909150612fa0908583613b12565b606087015160a088015191935090612fb9908684613b3e565b612fc3919061427a565b925050935093915050565b5f81606001516298968061259791906142c4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810361303b576040517f7538510200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61304582612509565b80549091501561305457505050565b81831061308d576040517fa16c453500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61309784612509565b5490505f8190036130d4576040517fa16c453500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828110156130f0578093506130e881612509565b5490506130d4565b808255826130fd85612509565b5560075481036131405760078390556040518381527fb9a86892440ed5515518351623ecfc523d283b21e92f1505e533ef26137be5b09060200160405180910390a15b6040518381527f7fdd20e2dbf90ff60a7d9be5ad62f1ec6d9d9cba8b36174a3839cafd059f09589060200160405180910390a150505050565b73ffffffffffffffffffffffffffffffffffffffff871615612aba576040517f22c44b5f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8816906322c44b5f906131f1908990899089908990899089906004016142df565b5f604051808303815f87803b158015613208575f80fd5b505af1925050508015613219575060015b612aba573d808015613246576040519150601f19603f3d011682016040523d82523d5f602084013e61324b565b606091505b50806040517f5d73cdc500000000000000000000000000000000000000000000000000000000815260040161221c9190614385565b6040805160e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152506040805160e08101825267ffffffffffffffff438116825262ffffff80851660208085019182525f858701818152606087018a815273ffffffffffffffffffffffffffffffffffffffff808d1660808a0190815260a08a018f815260c08b0186815286548088526001988990529c87208c5181549a5197518d166b010000000000000000000000027fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff98909c1668010000000000000000027fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000909b169c169b909b1798909817949094169790971788559051938701939093559351600286018054919093167fffffffffffffffffffffffff000000000000000000000000000000000000000090911617909155915160038401555160049092019190915580549192919080613407836143d8565b919050555094509492505050565b5f8061342083613afe565b90508062ffffff165f03613460576040517f15604cc500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60a083015162ffffff82169061347a90629896809061428d565b612de2919061427a565b5f61348e83612509565b80549091505f036134cb576040517f997768ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81816001015f8282546134de91906141e7565b9091555050505050565b80515f907f00000000000000000000000000000000000000000000000000000000000000008115021761351a83612fce565b62ffffff165f0361352b5792915050565b600c546007545f919082613571836fffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016613beb565b90505b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141580156135df57506135db826fffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001661428d565b8310155b806135e957508181145b15613659575f6135f883612509565b905080600101548461360a9190614227565b8154939650935061364d846fffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016613beb565b91506001945050613574565b831561369d57600c83905560078290556040518281527fb9a86892440ed5515518351623ecfc523d283b21e92f1505e533ef26137be5b09060200160405180910390a15b84811015610af357509295945050505050565b60408051606080820183525f80835260208084018290529284018190528351918201845260055462ffffff811680845267ffffffffffffffff6301000000830481169585018690526b0100000000000000000000009092048216958401869052929491939086168082189082100218915b8167ffffffffffffffff168767ffffffffffffffff1611156137b7575f61374884846142a4565b90508162ffffff168102850194508293507f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff168367ffffffffffffffff160361379a57506137b7565b6137a2613c0a565b9550855f015191508560400151925050613721565b5050509250929050565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091528251600c5462ffffff84169080820261382b7f000000000000000000000000000000000000000000000000000000000000000085614267565b5f036138c1575f61383b85612509565b60010154905080156138bf57816fffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016860285025f61388a8383614227565b90508387028082188183110282186138a285826141e7565b9650808d602001516138b491906141e7565b60208e015250505050505b505b5f6138da826c0100000000000000000000000087613b12565b905080600b546138ea91906141e7565b600b55600a546138fb9083906141e7565b600a5560608801805188919061391290839061440f565b62ffffff169052506139248786613e97565b8860400181815161393591906141e7565b90525096979650505050505050565b60035467ffffffffffffffff908116908216811061398e576040517fca31f40d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff9081166080840181815260a085018381525f928352600260208181526040808620600390810180549989166b0100000000000000000000008181027fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff9c8d1617909255808952978390208b518155938b01516001850155918a0151938301939093556060909801519082018054945193518716909802929095166301000000027fffffffffffffffffffffffffffffffffffffffffff000000000000000000000090931662ffffff90951694909417919091179093169290921790925581547fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016179055565b5f805f613aa986613afe565b60a0870151909150613ac49062ffffff808716908416613b12565b9150612fc385613af262ffffff8416780100000000000000000000000000000000000000000000000061428d565b60a08901519190613b3e565b5f81602001516298968061259791906142c4565b5f613b1e848484613b3e565b90508183850915612de25760010180612de25763ae47f7025f526004601cfd5b82820281838583041485151702613be4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8385098181108201900382848609835f038416828511613b975763ae47f7025f526004601cfd5b93849004938382119092035f839003839004600101029203041760026003830281188084028203028084028203028084028203028084028203028084028203028084029091030202612de2565b0492915050565b5f81613bfe576365244e4e5f526004601cfd5b50808206151591040190565b604080516060810182525f80825260208201819052918101919091527f000000000000000000000000000000000000000000000000000000000000000060045410613c81576040517f5f0ae8b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004545f90613cd390613c956008826141e7565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169190613ed1565b613cdc9061442a565b60055490915060e882901c9060c083901c906b010000000000000000000000900467ffffffffffffffff165f819003613d3257507f00000000000000000000000000000000000000000000000000000000000000005b5f613d4464ffffffffff841683614496565b6040805160608101825262ffffff871680825267ffffffffffffffff8681166020840181905290851692909301829052600580547fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000169091176301000000909302929092177fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff166b010000000000000000000000909102179055600480549192506008915f90613df59084906141e7565b90915550506040805167ffffffffffffffff84811682528316602082015262ffffff86168183015290517f6863f2b489f9186bf89231dc73aa0e9836f536b9ddb0f708f74260ed3160f2979181900360600190a150506040805160608101825260055462ffffff8116825267ffffffffffffffff63010000008204811660208401526b0100000000000000000000009091041691810191909152949350505050565b5f815f03613ea657505f612597565b612594827affffff00000000000000000000000000000000000000000000000060c086901b1661427a565b60405161ffff8210613ee35761ffff91505b818310838303026001810184601f8401873c8082015160ff16613f1d576001853b038060281c3d3d3e808403818511028203858211029150505b8082525f81602084010152806040830101604052509392505050565b80356fffffffffffffffffffffffffffffffff81168114613f58575f80fd5b919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114613f58575f80fd5b5f8083601f840112613f90575f80fd5b50813567ffffffffffffffff811115613fa7575f80fd5b602083019150836020828501011115613fbe575f80fd5b9250929050565b5f805f805f60808688031215613fd9575f80fd5b85359450613fe960208701613f39565b9350613ff760408701613f5d565b9250606086013567ffffffffffffffff811115614012575f80fd5b61401e88828901613f80565b969995985093965092949392505050565b803567ffffffffffffffff81168114613f58575f80fd5b5f805f60608486031215614058575f80fd5b833592506140686020850161402f565b91506140766040850161402f565b90509250925092565b5f6020828403121561408f575f80fd5b5035919050565b5f805f805f8060a087890312156140ab575f80fd5b863595506140bb60208801613f39565b94506140c960408801613f5d565b935060608701359250608087013567ffffffffffffffff8111156140eb575f80fd5b6140f789828a01613f80565b979a9699509497509295939492505050565b5f60208284031215614119575f80fd5b6125948261402f565b5f805f60408486031215614134575f80fd5b61413d84613f5d565b9250602084013567ffffffffffffffff811115614158575f80fd5b8401601f81018613614168575f80fd5b803567ffffffffffffffff81111561417e575f80fd5b8660208260051b8401011115614192575f80fd5b939660209190910195509293505050565b5f602082840312156141b3575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115612597576125976141ba565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b81810381811115612597576125976141ba565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f826142755761427561423a565b500690565b5f826142885761428861423a565b500490565b8082028115828204841417612597576125976141ba565b67ffffffffffffffff8281168282160390811115612597576125976141ba565b62ffffff8281168282160390811115612597576125976141ba565b8681526fffffffffffffffffffffffffffffffff8616602082015273ffffffffffffffffffffffffffffffffffffffff8516604082015273ffffffffffffffffffffffffffffffffffffffff8416606082015260a060808201528160a0820152818360c08301375f81830160c090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010195945050505050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614408576144086141ba565b5060010190565b62ffffff8181168382160190811115612597576125976141ba565b805160208201517fffffffffffffffff00000000000000000000000000000000000000000000000081169190600882101561448f577fffffffffffffffff000000000000000000000000000000000000000000000000808360080360031b1b82161692505b5050919050565b67ffffffffffffffff8181168382160190811115612597576125976141ba56fea26469706673582212201e840418aee3693d42978f5f484854c78e142f7467536258226a973196b0f86064736f6c634300081a00330000000000000000000000004b00c30ceba3f188407c6e6741cc5b43561f1f6e000000000000000000000000000000000000000004ffa5fe0ab05ffbeb0000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013620833364653fa125ccdd7cf54b9e4a22ab6d9000000000000000000000000d53006d1e3110fd319a79aeec4c527a0d265e08000000000000000000000000000000000000000000000000000000000016b049500000000000000000000000000000000000000000000000000000000016d874c00000000000000000000000000000000000000000000000000000000016d874c000000000000000000000000000000000000000000000198b87463d2504850630000000000000000000000002dd6e0e331de9743635590f6c8bc5038374cac9d000000000000000000000000000000000000000000009fa80d76fe275c3f66ac000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000040000000000001f61600015b0000001c200000000000000e1000008a0000000e1000008a0000001c2000008a0000001c2000008a0000001c203d66c00000000001

Deployed Bytecode

0x6080604052600436106102c1575f3560e01c80638e4deb1711610170578063b363e953116100d1578063d2fc583a11610087578063e5a6b10f11610062578063e5a6b10f14610a2e578063fc0c546a14610a60578063fd63755714610a92575f80fd5b8063d2fc583a1461095b578063dc2690491461096f578063e25fe17514610982575f80fd5b8063c2c4c5c1116100b7578063c2c4c5c114610900578063cab8bedc14610914578063d0c93a7c14610929575f80fd5b8063b363e953146108cd578063b8f163d6146108e1575f80fd5b8063a52c872811610126578063ad7473e51161010c578063ad7473e5146107c2578063ae91fa33146107d7578063b122db601461080a575f80fd5b8063a52c87281461079b578063a9176e45146107ae575f80fd5b80639363c812116101565780639363c81214610731578063998ba4fc146107635780639e5f260214610777575f80fd5b80638e4deb171461069a578063907c0f92146106b9575f80fd5b806337dfbc4b1161022557806348cd4cb1116101db57806360d3ded7116101b657806360d3ded7146106405780637c121574146106545780638134f02714610668575f80fd5b806348cd4cb1146105c0578063534cb30d146105f25780635dd13ca71461062c575f80fd5b80633e9d91741161020b5780633e9d9174146104ea5780634423c5f1146104fe57806346e04a2f146105a1575f80fd5b806337dfbc4b146104865780633b6fd2cf146104b8575f80fd5b806318160ddd1161027a57806332a0f2d71161026057806332a0f2d71461043d578063331f2f651461045157806336dec5f214610467575f80fd5b806318160ddd1461039e5780632f5f3b3c146103ea575f80fd5b8063111a977d116102aa578063111a977d1461035257806311ea09d01461036d578063140fe8ee1461038b575f80fd5b8063083c6323146102c55780630e9a83cd14610311575b5f80fd5b3480156102d0575f80fd5b507f00000000000000000000000000000000000000000000000000000000016d874c5b60405167ffffffffffffffff90911681526020015b60405180910390f35b34801561031c575f80fd5b506103447fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81565b604051908152602001610308565b34801561035d575f80fd5b506102f367ffffffffffffffff81565b348015610378575f80fd5b5060035467ffffffffffffffff166102f3565b610344610399366004613fc5565b610ac4565b3480156103a9575f80fd5b506040516fffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000004ffa5fe0ab05ffbeb000000168152602001610308565b3480156103f5575f80fd5b507f000000000000000000000000ff71bdd5419bae0208be88068abf169303deb7ca5b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610308565b348015610448575f80fd5b50610344610afd565b34801561045c575f80fd5b50610465610bbf565b005b348015610472575f80fd5b50610465610481366004614046565b610d76565b348015610491575f80fd5b507f00000000000000000000000000000000000000000000000000000000016d874c6102f3565b3480156104c3575f80fd5b507f000000000000000000000000d53006d1e3110fd319a79aeec4c527a0d265e080610418565b3480156104f5575f80fd5b506103446114c6565b348015610509575f80fd5b5061051d61051836600461407f565b6114f6565b60405161030891905f60e08201905067ffffffffffffffff835116825262ffffff602084015116602083015267ffffffffffffffff60408401511660408301526060830151606083015273ffffffffffffffffffffffffffffffffffffffff608084015116608083015260a083015160a083015260c083015160c083015292915050565b3480156105ac575f80fd5b506104656105bb36600461407f565b6115c4565b3480156105cb575f80fd5b507f00000000000000000000000000000000000000000000000000000000016b04956102f3565b3480156105fd575f80fd5b5061061161060c36600461407f565b611773565b60408051825181526020928301519281019290925201610308565b348015610637575f80fd5b506104656117ae565b34801561064b575f80fd5b50600754610344565b34801561065f575f80fd5b50610465611955565b348015610673575f80fd5b507f0000000000000000000000002dd6e0e331de9743635590f6c8bc5038374cac9d610418565b3480156106a5575f80fd5b506104656106b436600461407f565b611aa1565b3480156106c4575f80fd5b506106cd611d29565b60405161030891905f60c08201905082518252602083015160208301526040830151604083015262ffffff606084015116606083015267ffffffffffffffff608084015116608083015267ffffffffffffffff60a08401511660a083015292915050565b34801561073c575f80fd5b507f000000000000000000000000000000000000000000009fa80d76fe275c3f66ac610344565b34801561076e575f80fd5b50610344611e14565b348015610782575f80fd5b5061078b611e1d565b6040519015158152602001610308565b6103446107a9366004614096565b611e4a565b3480156107b9575f80fd5b50600c54610344565b3480156107cd575f80fd5b5061034460095481565b3480156107e2575f80fd5b506103447f00000000000000000000000000000000a3ee1769a11b5bd37830553b97e8896481565b348015610815575f80fd5b506106cd610824366004614109565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b3480156108d8575f80fd5b50600b54610344565b3480156108ec575f80fd5b506104656108fb366004614122565b612068565b34801561090b575f80fd5b506106cd6122f9565b34801561091f575f80fd5b5061034460085481565b348015610934575f80fd5b507f000000000000000000000000000000000000000000000198b87463d250485063610344565b348015610966575f80fd5b50600a54610344565b34801561097a575f80fd5b505f54610344565b34801561098d575f80fd5b506109f5604080516060810182525f8082526020820181905291810191909152506040805160608101825260055462ffffff8116825267ffffffffffffffff63010000008204811660208401526b010000000000000000000000909104169181019190915290565b60408051825162ffffff16815260208084015167ffffffffffffffff908116918301919091529282015190921690820152606001610308565b348015610a39575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610418565b348015610a6b575f80fd5b507f0000000000000000000000004b00c30ceba3f188407c6e6741cc5b43561f1f6e610418565b348015610a9d575f80fd5b507f00000000000000000000000013620833364653fa125ccdd7cf54b9e4a22ab6d9610418565b5f610af38686867f000000000000000000000000000000000000000000009fa80d76fe275c3f66ac8787611e4a565b9695505050505050565b6003545f90610bb99067ffffffffffffffff166040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b51919050565b600d5460ff1615610bcc57565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000004ffa5fe0ab05ffbeb0000006fffffffffffffffffffffffffffffffff16907f0000000000000000000000004b00c30ceba3f188407c6e6741cc5b43561f1f6e73ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610c88573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cac91906141a3565b1015610ce4576040517f268128ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040517f000000000000000000000000000000000000000004ffa5fe0ab05ffbeb0000006fffffffffffffffffffffffffffffffff1681527f17cca138a663106b4c25a247e2d9238888fe37188d83b7bb7287bc1c0a4df82a9060200160405180910390a1565b5f610d7f6122f9565b90505f610d8b85612375565b6040805160e081018252825467ffffffffffffffff808216835262ffffff6801000000000000000083041660208401526b0100000000000000000000009091041691810182905260018301546060820152600283015473ffffffffffffffffffffffffffffffffffffffff166080820152600383015460a082015260049092015460c083015290915015610e4b576040517f3458822100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a547f00000000000000000000000000000000000000000000000000000000000000001115610eea577f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff164310610eb857610eb1855f806123c4565b5050505050565b6040517f8af474ff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606081015181515f610f9f876040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b905082815f01511015806110635750826110608260a001516040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b51105b8061108157508167ffffffffffffffff168767ffffffffffffffff16105b156110b8576040517f6a00945500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611166836040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b82519091505f908190156111855761117f8484896124ef565b90925090505b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915267ffffffffffffffff8a16156113c1576040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152438b67ffffffffffffffff160361120f5750886112bf565b6112bc8b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b90505b61137081608001516040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b915087815f01511115806113845750815188105b156113bb576040517f516cb4ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50611462565b7f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff16431015611425576040517fe08d8a4f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50875188908714611462576040517f0ba9845700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80518790036114ad575f61147588612509565b6001015490505f8061148c8b84866020015161257c565b909250905061149b82876141e7565b95506114a781866141e7565b94505050505b6114b88c84846123c4565b505050505050505050505050565b5f6114f16114ec6c01000000000000000000000000600b5461258990919063ffffffff16565b61259d565b905090565b6040805160e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915261153782612375565b6040805160e081018252825467ffffffffffffffff808216835262ffffff6801000000000000000083041660208401526b010000000000000000000000909104169181019190915260018201546060820152600282015473ffffffffffffffffffffffffffffffffffffffff166080820152600382015460a082015260049091015460c082015292915050565b7f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff16431015611628576040517f6247a84e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003547f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff908116911614611669576116676122f9565b505b600a547f000000000000000000000000000000000000000000000000000000000000000011156116c5576040517fd66173a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f806116d0836125ab565b9092509050801561176e5761171c73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004b00c30ceba3f188407c6e6741cc5b43561f1f6e16838361263e565b8173ffffffffffffffffffffffffffffffffffffffff16837f880f2ef2613b092f1a0a819f294155c98667eb294b7e6bf7a3810278142c1a1c8360405161176591815260200190565b60405180910390a35b505050565b604080518082019091525f808252602082015261178f82612509565b6040805180820190915281548152600190910154602082015292915050565b7f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff16431015611811576040517e175ba800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003547f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff908116911614611852576118506122f9565b505b6009541561188c576040517f8fd6c3f900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6118b9600a547f0000000000000000000000000000000000000000000000000000000000000000111590565b156119145761190d6114ec6c01000000000000000000000000611907600b546119017f000000000000000004ffa5fe0ab05ffbeb000000000000000000000000000000612749565b90612757565b90612589565b9050611949565b506fffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000004ffa5fe0ab05ffbeb000000165b61195281612762565b50565b7f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff164310156119b8576040517e175ba800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003547f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff9081169116146119f9576119f76122f9565b505b60085415611a33576040517f76ae8ed700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a547f00000000000000000000000000000000000000000000000000000000000000001115611a8f576040517fd66173a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611a9f611a9a612840565b612866565b565b7f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff16431015611b04576040517e175ba800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611b0e82612375565b6040805160e081018252825467ffffffffffffffff808216835262ffffff6801000000000000000083041660208401526b0100000000000000000000009091041691810182905260018301546060820152600283015473ffffffffffffffffffffffffffffffffffffffff166080820152600383015460a082015260049092015460c083015290915015611bce576040517f3458822100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611bd7612939565b9050611c05600a547f0000000000000000000000000000000000000000000000000000000000000000111590565b611c145761176e835f806123c4565b8051606083015111611c52576040517f0ba9845700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f611d03835f01516040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b90505f80611d128484876124ef565b91509150611d218683836123c4565b505050505050565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091526003546114f19067ffffffffffffffff166040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff9081165f90815260026020818152604092839020835160c0810185528154815260018201549281019290925291820154928101929092526003015462ffffff8116606083015263010000008104831660808301526b010000000000000000000000900490911660a082015290565b5f6114f1612840565b5f6114f1600a547f0000000000000000000000000000000000000000000000000000000000000000111590565b5f611e53612993565b7f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff164310611eb6576040517f5f0ae8b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b856fffffffffffffffffffffffffffffffff165f03611f01576040517fc873160800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516611f4e576040517f4076a98c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016611fd957856fffffffffffffffffffffffffffffffff163414611fd4576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61204f565b3415612011576040517fe3e0a9bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61204f7f00000000000000000000000000000000000000000000000000000000000000003330896fffffffffffffffffffffffffffffffff16612a33565b61205d878787878787612ac3565b979650505050505050565b7f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff164310156120cc576040517f6247a84e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003547f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff90811691161461210d5761210b6122f9565b505b600a547f00000000000000000000000000000000000000000000000000000000000000001115612169576040517fd66173a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805b828110156122ab575f8061219786868581811061218b5761218b6141fa565b905060200201356125ab565b915091508673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614612225576040517f1515875c00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8089166004830152831660248201526044015b60405180910390fd5b61222f81856141e7565b935080156122a1578173ffffffffffffffffffffffffffffffffffffffff16868685818110612260576122606141fa565b905060200201357f880f2ef2613b092f1a0a819f294155c98667eb294b7e6bf7a3810278142c1a1c8360405161229891815260200190565b60405180910390a35b505060010161216c565b5080156122f3576122f373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004b00c30ceba3f188407c6e6741cc5b43561f1f6e16858361263e565b50505050565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152612332612993565b7f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff1643111561236c576114f1612939565b6114f143612d90565b5f805482106123b3576040517f9076e8b90000000000000000000000000000000000000000000000000000000081526004810183905260240161221c565b505f90815260016020526040902090565b5f6123ce84612375565b6002810154600382015491925073ffffffffffffffffffffffffffffffffffffffff16905f90606090612402908690614227565b6004850187905584547fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff166b0100000000000000000000004367ffffffffffffffff1602178555901c905080156124945761249473ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016838361263e565b604080518681526020810183905273ffffffffffffffffffffffffffffffffffffffff84169188917f054fe6469466a0b4d2a6ae4b100e5f9c494c958f04b4000f44d470088dd97930910160405180910390a3505050505050565b5f806124fc858585612f22565b915091505b935093915050565b5f6125347f000000000000000000000000000000000000000000000198b87463d25048506383614267565b1561256b576040517fd76fb50f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505f90815260066020526040902090565b5f806124fc858585612f61565b5f612594828461427a565b90505b92915050565b5f612597629896808361427a565b5f805f6125b784612375565b80549091506b010000000000000000000000900467ffffffffffffffff165f0361260d576040517f7e13888200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002810154600490910180545f90915573ffffffffffffffffffffffffffffffffffffffff90911694909350915050565b5f73ffffffffffffffffffffffffffffffffffffffff84166126a0575f805f8085875af190508061269b576040517ff4b3b1bc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6122f3565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015282602482015260205f6044835f895af13d15601f3d1160015f511416171691505f81525f60208201525f604082015250806122f3576040517ff27f64e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f612597629896808361428d565b5f6125948284614227565b4360095580156127cd576127cd73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004b00c30ceba3f188407c6e6741cc5b43561f1f6e167f00000000000000000000000013620833364653fa125ccdd7cf54b9e4a22ab6d98361263e565b7f00000000000000000000000013620833364653fa125ccdd7cf54b9e4a22ab6d973ffffffffffffffffffffffffffffffffffffffff167fdd9a81eb1b5197489c3ccfdab7b542e2e6dbdcf4120324e2688fab56fd23f98b8260405161283591815260200190565b60405180910390a250565b5f6114f16114ec6c01000000000000000000000000600a5461258990919063ffffffff16565b4360085580156128d1576128d173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000d53006d1e3110fd319a79aeec4c527a0d265e0808361263e565b7f000000000000000000000000d53006d1e3110fd319a79aeec4c527a0d265e08073ffffffffffffffffffffffffffffffffffffffff167f053dfa7183794b221b03c5109dfb5a07b67d719cb3e98262d48bc66b2a132ad38260405161283591815260200190565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091526114f17f00000000000000000000000000000000000000000000000000000000016d874c612d90565b7f00000000000000000000000000000000000000000000000000000000016b049567ffffffffffffffff164310156129f7576040517feffaea8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5460ff16611a9f576040517f8d6b8a8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518460601b60601c60748201528160548201528260348201528360601b60208201526f36c7851600000000000000000000000081526e22d473030f116ddee9f6b43ac78ba36001461480612a895750803b15155b80873b15105f386084601087015f875af116612aba57677939f4248757f0fd5f5260048460a01c151560021b601801fd5b50505050505050565b5f7f00000000000000000000000000000000a3ee1769a11b5bd37830553b97e88964871115612b47576040517fae9d6dc2000000000000000000000000000000000000000000000000000000008152600481018890527f00000000000000000000000000000000a3ee1769a11b5bd37830553b97e88964602482015260440161221c565b5f612b506122f9565b9050612b5b81612fce565b62ffffff165f03612b98576040517f5cd5436100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80518811612bd2576040517f5f259e5200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612bdc8589612fe2565b612c2173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002dd6e0e331de9743635590f6c8bc5038374cac9d16898989338989613179565b6040805160e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c08101919091525f606060ff16896fffffffffffffffffffffffffffffffff16901b9050612c8681898c8660600151613280565b945091505f612c9483613415565b9050612ca08b82613484565b80600c5f828254612cb191906141e7565b90915550612ce49050629896807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61427a565b600c5410612d1e576040517fa37fb9e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518c81526fffffffffffffffffffffffffffffffff8c16602082015273ffffffffffffffffffffffffffffffffffffffff8b169187917f650baad5cd8ca09b8f580be220fa04ce2ba905a041f764b6a3fe2c848eb70540910160405180910390a3505050509695505050505050565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a081019190915260035467ffffffffffffffff908116908316819003612de957612de2611d29565b9392505050565b612df1611d29565b91505f612dfd836134e8565b83519091508114612e58578083525f6020808501919091526040805167ffffffffffffffff871681529182018390527f30adbe996d7a69a21fdebcc1f8a46270bf6c22d505a7d872c1ab4767aa707609910160405180910390a15b5f80612e6486856136b0565b915091505f612e91836020015167ffffffffffffffff168667ffffffffffffffff16808218908210021890565b612e9b90886142a4565b835162ffffff16810292909201919050612eb586836137c1565b9550612ec18688613944565b85516060808801516040805167ffffffffffffffff8c168152602081019490945262ffffff909116908301527ff1e4b6d7d0d7c5deb6393a39862d66a2f2ecb034f3283a8a597f9bf0c36f76fa910160405180910390a15050505050919050565b5f80612f558385604001518760400151612f3c9190614227565b86606001518860600151612f5091906142c4565b613a9d565b90969095509350505050565b5f80835f03612f7457505f905080612501565b5f612f7e86613afe565b612f8d9062ffffff168661428d565b60a0870151909150612fa0908583613b12565b606087015160a088015191935090612fb9908684613b3e565b612fc3919061427a565b925050935093915050565b5f81606001516298968061259791906142c4565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810361303b576040517f7538510200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61304582612509565b80549091501561305457505050565b81831061308d576040517fa16c453500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61309784612509565b5490505f8190036130d4576040517fa16c453500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828110156130f0578093506130e881612509565b5490506130d4565b808255826130fd85612509565b5560075481036131405760078390556040518381527fb9a86892440ed5515518351623ecfc523d283b21e92f1505e533ef26137be5b09060200160405180910390a15b6040518381527f7fdd20e2dbf90ff60a7d9be5ad62f1ec6d9d9cba8b36174a3839cafd059f09589060200160405180910390a150505050565b73ffffffffffffffffffffffffffffffffffffffff871615612aba576040517f22c44b5f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8816906322c44b5f906131f1908990899089908990899089906004016142df565b5f604051808303815f87803b158015613208575f80fd5b505af1925050508015613219575060015b612aba573d808015613246576040519150601f19603f3d011682016040523d82523d5f602084013e61324b565b606091505b50806040517f5d73cdc500000000000000000000000000000000000000000000000000000000815260040161221c9190614385565b6040805160e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152506040805160e08101825267ffffffffffffffff438116825262ffffff80851660208085019182525f858701818152606087018a815273ffffffffffffffffffffffffffffffffffffffff808d1660808a0190815260a08a018f815260c08b0186815286548088526001988990529c87208c5181549a5197518d166b010000000000000000000000027fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff98909c1668010000000000000000027fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000909b169c169b909b1798909817949094169790971788559051938701939093559351600286018054919093167fffffffffffffffffffffffff000000000000000000000000000000000000000090911617909155915160038401555160049092019190915580549192919080613407836143d8565b919050555094509492505050565b5f8061342083613afe565b90508062ffffff165f03613460576040517f15604cc500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60a083015162ffffff82169061347a90629896809061428d565b612de2919061427a565b5f61348e83612509565b80549091505f036134cb576040517f997768ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81816001015f8282546134de91906141e7565b9091555050505050565b80515f907f000000000000000000000000000000000000000000009fa80d76fe275c3f66ac8115021761351a83612fce565b62ffffff165f0361352b5792915050565b600c546007545f919082613571836fffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000004ffa5fe0ab05ffbeb00000016613beb565b90505b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141580156135df57506135db826fffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000004ffa5fe0ab05ffbeb0000001661428d565b8310155b806135e957508181145b15613659575f6135f883612509565b905080600101548461360a9190614227565b8154939650935061364d846fffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000004ffa5fe0ab05ffbeb00000016613beb565b91506001945050613574565b831561369d57600c83905560078290556040518281527fb9a86892440ed5515518351623ecfc523d283b21e92f1505e533ef26137be5b09060200160405180910390a15b84811015610af357509295945050505050565b60408051606080820183525f80835260208084018290529284018190528351918201845260055462ffffff811680845267ffffffffffffffff6301000000830481169585018690526b0100000000000000000000009092048216958401869052929491939086168082189082100218915b8167ffffffffffffffff168767ffffffffffffffff1611156137b7575f61374884846142a4565b90508162ffffff168102850194508293507f00000000000000000000000000000000000000000000000000000000016d874c67ffffffffffffffff168367ffffffffffffffff160361379a57506137b7565b6137a2613c0a565b9550855f015191508560400151925050613721565b5050509250929050565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a08101919091528251600c5462ffffff84169080820261382b7f000000000000000000000000000000000000000000000198b87463d25048506385614267565b5f036138c1575f61383b85612509565b60010154905080156138bf57816fffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000004ffa5fe0ab05ffbeb00000016860285025f61388a8383614227565b90508387028082188183110282186138a285826141e7565b9650808d602001516138b491906141e7565b60208e015250505050505b505b5f6138da826c0100000000000000000000000087613b12565b905080600b546138ea91906141e7565b600b55600a546138fb9083906141e7565b600a5560608801805188919061391290839061440f565b62ffffff169052506139248786613e97565b8860400181815161393591906141e7565b90525096979650505050505050565b60035467ffffffffffffffff908116908216811061398e576040517fca31f40d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff9081166080840181815260a085018381525f928352600260208181526040808620600390810180549989166b0100000000000000000000008181027fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff9c8d1617909255808952978390208b518155938b01516001850155918a0151938301939093556060909801519082018054945193518716909802929095166301000000027fffffffffffffffffffffffffffffffffffffffffff000000000000000000000090931662ffffff90951694909417919091179093169290921790925581547fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016179055565b5f805f613aa986613afe565b60a0870151909150613ac49062ffffff808716908416613b12565b9150612fc385613af262ffffff8416780100000000000000000000000000000000000000000000000061428d565b60a08901519190613b3e565b5f81602001516298968061259791906142c4565b5f613b1e848484613b3e565b90508183850915612de25760010180612de25763ae47f7025f526004601cfd5b82820281838583041485151702613be4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8385098181108201900382848609835f038416828511613b975763ae47f7025f526004601cfd5b93849004938382119092035f839003839004600101029203041760026003830281188084028203028084028203028084028203028084028203028084028203028084029091030202612de2565b0492915050565b5f81613bfe576365244e4e5f526004601cfd5b50808206151591040190565b604080516060810182525f80825260208201819052918101919091527f000000000000000000000000000000000000000000000000000000000000004060045410613c81576040517f5f0ae8b500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004545f90613cd390613c956008826141e7565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ff71bdd5419bae0208be88068abf169303deb7ca169190613ed1565b613cdc9061442a565b60055490915060e882901c9060c083901c906b010000000000000000000000900467ffffffffffffffff165f819003613d3257507f00000000000000000000000000000000000000000000000000000000016b04955b5f613d4464ffffffffff841683614496565b6040805160608101825262ffffff871680825267ffffffffffffffff8681166020840181905290851692909301829052600580547fffffffffffffffffffffffffffffffffffffffffff0000000000000000000000169091176301000000909302929092177fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff166b010000000000000000000000909102179055600480549192506008915f90613df59084906141e7565b90915550506040805167ffffffffffffffff84811682528316602082015262ffffff86168183015290517f6863f2b489f9186bf89231dc73aa0e9836f536b9ddb0f708f74260ed3160f2979181900360600190a150506040805160608101825260055462ffffff8116825267ffffffffffffffff63010000008204811660208401526b0100000000000000000000009091041691810191909152949350505050565b5f815f03613ea657505f612597565b612594827affffff00000000000000000000000000000000000000000000000060c086901b1661427a565b60405161ffff8210613ee35761ffff91505b818310838303026001810184601f8401873c8082015160ff16613f1d576001853b038060281c3d3d3e808403818511028203858211029150505b8082525f81602084010152806040830101604052509392505050565b80356fffffffffffffffffffffffffffffffff81168114613f58575f80fd5b919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114613f58575f80fd5b5f8083601f840112613f90575f80fd5b50813567ffffffffffffffff811115613fa7575f80fd5b602083019150836020828501011115613fbe575f80fd5b9250929050565b5f805f805f60808688031215613fd9575f80fd5b85359450613fe960208701613f39565b9350613ff760408701613f5d565b9250606086013567ffffffffffffffff811115614012575f80fd5b61401e88828901613f80565b969995985093965092949392505050565b803567ffffffffffffffff81168114613f58575f80fd5b5f805f60608486031215614058575f80fd5b833592506140686020850161402f565b91506140766040850161402f565b90509250925092565b5f6020828403121561408f575f80fd5b5035919050565b5f805f805f8060a087890312156140ab575f80fd5b863595506140bb60208801613f39565b94506140c960408801613f5d565b935060608701359250608087013567ffffffffffffffff8111156140eb575f80fd5b6140f789828a01613f80565b979a9699509497509295939492505050565b5f60208284031215614119575f80fd5b6125948261402f565b5f805f60408486031215614134575f80fd5b61413d84613f5d565b9250602084013567ffffffffffffffff811115614158575f80fd5b8401601f81018613614168575f80fd5b803567ffffffffffffffff81111561417e575f80fd5b8660208260051b8401011115614192575f80fd5b939660209190910195509293505050565b5f602082840312156141b3575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115612597576125976141ba565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b81810381811115612597576125976141ba565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f826142755761427561423a565b500690565b5f826142885761428861423a565b500490565b8082028115828204841417612597576125976141ba565b67ffffffffffffffff8281168282160390811115612597576125976141ba565b62ffffff8281168282160390811115612597576125976141ba565b8681526fffffffffffffffffffffffffffffffff8616602082015273ffffffffffffffffffffffffffffffffffffffff8516604082015273ffffffffffffffffffffffffffffffffffffffff8416606082015260a060808201528160a0820152818360c08301375f81830160c090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010195945050505050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614408576144086141ba565b5060010190565b62ffffff8181168382160190811115612597576125976141ba565b805160208201517fffffffffffffffff00000000000000000000000000000000000000000000000081169190600882101561448f577fffffffffffffffff000000000000000000000000000000000000000000000000808360080360031b1b82161692505b5050919050565b67ffffffffffffffff8181168382160190811115612597576125976141ba56fea26469706673582212201e840418aee3693d42978f5f484854c78e142f7467536258226a973196b0f86064736f6c634300081a0033

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

0000000000000000000000004b00c30ceba3f188407c6e6741cc5b43561f1f6e000000000000000000000000000000000000000004ffa5fe0ab05ffbeb0000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013620833364653fa125ccdd7cf54b9e4a22ab6d9000000000000000000000000d53006d1e3110fd319a79aeec4c527a0d265e08000000000000000000000000000000000000000000000000000000000016b049500000000000000000000000000000000000000000000000000000000016d874c00000000000000000000000000000000000000000000000000000000016d874c000000000000000000000000000000000000000000000198b87463d2504850630000000000000000000000002dd6e0e331de9743635590f6c8bc5038374cac9d000000000000000000000000000000000000000000009fa80d76fe275c3f66ac000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000040000000000001f61600015b0000001c200000000000000e1000008a0000000e1000008a0000001c2000008a0000001c2000008a0000001c203d66c00000000001

-----Decoded View---------------
Arg [0] : _token (address): 0x4B00C30cEBA3F188407C6e6741cc5b43561f1F6e
Arg [1] : _totalSupply (uint128): 1547000000000000000000000000
Arg [2] : _parameters (tuple):
Arg [1] : currency (address): 0x0000000000000000000000000000000000000000
Arg [2] : tokensRecipient (address): 0x13620833364653fa125cCDD7Cf54b9e4A22AB6d9
Arg [3] : fundsRecipient (address): 0xd53006d1e3110fD319a79AEEc4c527a0d265E080
Arg [4] : startBlock (uint64): 23790741
Arg [5] : endBlock (uint64): 23955276
Arg [6] : claimBlock (uint64): 23955276
Arg [7] : tickSpacing (uint256): 7539562940228715434083
Arg [8] : validationHook (address): 0x2DD6e0E331DE9743635590F6c8BC5038374CAc9D
Arg [9] : floorPrice (uint256): 753956294022871543408300
Arg [10] : requiredCurrencyRaised (uint128): 0
Arg [11] : auctionStepsData (bytes): 0x000000000001f61600015b0000001c200000000000000e1000008a0000000e1000008a0000001c2000008a0000001c2000008a0000001c203d66c00000000001


-----Encoded View---------------
17 Constructor Arguments found :
Arg [0] : 0000000000000000000000004b00c30ceba3f188407c6e6741cc5b43561f1f6e
Arg [1] : 000000000000000000000000000000000000000004ffa5fe0ab05ffbeb000000
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 00000000000000000000000013620833364653fa125ccdd7cf54b9e4a22ab6d9
Arg [5] : 000000000000000000000000d53006d1e3110fd319a79aeec4c527a0d265e080
Arg [6] : 00000000000000000000000000000000000000000000000000000000016b0495
Arg [7] : 00000000000000000000000000000000000000000000000000000000016d874c
Arg [8] : 00000000000000000000000000000000000000000000000000000000016d874c
Arg [9] : 000000000000000000000000000000000000000000000198b87463d250485063
Arg [10] : 0000000000000000000000002dd6e0e331de9743635590f6c8bc5038374cac9d
Arg [11] : 000000000000000000000000000000000000000000009fa80d76fe275c3f66ac
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [15] : 000000000001f61600015b0000001c200000000000000e1000008a0000000e10
Arg [16] : 00008a0000001c2000008a0000001c2000008a0000001c203d66c00000000001


Deployed Bytecode Sourcemap

1652:35446:5:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3935:84:6;;;;;;;;;;-1:-1:-1;4003:9:6;3935:84;;;188:18:28;176:31;;;158:50;;146:2;131:18;3935:84:6;;;;;;;;952:56:7;;;;;;;;;;;;991:17;952:56;;;;;365:25:28;;;353:2;338:18;952:56:7;219:177:28;721:58:4;;;;;;;;;;;;763:16;721:58;;4202:110;;;;;;;;;;-1:-1:-1;4283:22:4;;;;4202:110;;26104:237:5;;;;;;:::i;:::-;;:::i;3596:91:8:-;;;;;;;;;;-1:-1:-1;3596:91:8;;2000:34:28;3668:12:8;1988:47:28;1970:66;;1958:2;1943:18;3596:91:8;1824:218:28;4058:84:6;;;;;;;;;;-1:-1:-1;4126:9:6;4058:84;;;2223:42:28;2211:55;;;2193:74;;2181:2;2166:18;4058:84:6;2047:226:28;1217:133:4;;;;;;;;;;;;;:::i;5665:452:5:-;;;;;;;;;;;;;:::i;:::-;;27379:5636;;;;;;;;;;-1:-1:-1;27379:5636:5;;;;;:::i;:::-;;:::i;36140:88::-;;;;;;;;;;-1:-1:-1;36210:11:5;36140:88;;3882:97:8;;;;;;;;;;-1:-1:-1;3957:15:8;3882:97;;36947:149:5;;;;;;;;;;;;;:::i;1787:102:3:-;;;;;;;;;;-1:-1:-1;1787:102:3;;;;;:::i;:::-;;:::i;:::-;;;;;;3194:4:28;3236:3;3225:9;3221:19;3213:27;;3286:18;3277:6;3271:13;3267:38;3256:9;3249:57;3374:8;3366:4;3358:6;3354:17;3348:24;3344:39;3337:4;3326:9;3322:20;3315:69;3452:18;3444:4;3436:6;3432:17;3426:24;3422:49;3415:4;3404:9;3400:20;3393:79;3528:4;3520:6;3516:17;3510:24;3503:4;3492:9;3488:20;3481:54;3603:42;3595:4;3587:6;3583:17;3577:24;3573:73;3566:4;3555:9;3551:20;3544:103;3703:4;3695:6;3691:17;3685:24;3678:4;3667:9;3663:20;3656:54;3766:4;3758:6;3754:17;3748:24;3741:4;3730:9;3726:20;3719:54;3060:719;;;;;33068:478:5;;;;;;;;;;-1:-1:-1;33068:478:5;;;;;:::i;:::-;;:::i;3808:88:6:-;;;;;;;;;;-1:-1:-1;3878:11:6;3808:88;;5481:105:7;;;;;;;;;;-1:-1:-1;5481:105:7;;;;;:::i;:::-;;:::i;:::-;;;;3992:13:28;;3974:32;;4062:4;4050:17;;;4044:24;4022:20;;;4015:54;;;;3947:18;5481:105:7;3784:291:28;35585:487:5;;;;;;;;;;;;;:::i;5335:107:7:-;;;;;;;;;;-1:-1:-1;5415:20:7;;5335:107;;35129:403:5;;;;;;;;;;;;;:::i;36281:105::-;;;;;;;;;;-1:-1:-1;36364:15:5;36281:105;;26394:932;;;;;;;;;;-1:-1:-1;26394:932:5;;;;;:::i;:::-;;:::i;1042:130:4:-;;;;;;;;;;;;;:::i;:::-;;;;;;4483:4:28;4525:3;4514:9;4510:19;4502:27;;4562:6;4556:13;4545:9;4538:32;4626:4;4618:6;4614:17;4608:24;4601:4;4590:9;4586:20;4579:54;4689:4;4681:6;4677:17;4671:24;4664:4;4653:9;4649:20;4642:54;4764:8;4756:4;4748:6;4744:17;4738:24;4734:39;4727:4;4716:9;4712:20;4705:69;4842:18;4834:4;4826:6;4822:17;4816:24;4812:49;4805:4;4794:9;4790:20;4783:79;4930:18;4922:4;4914:6;4910:17;4904:24;4900:49;4893:4;4882:9;4878:20;4871:79;4335:621;;;;;5077:89:7;;;;;;;;;;-1:-1:-1;5148:11:7;5077:89;;6692:99:5;;;;;;;;;;;;;:::i;6170:90::-;;;;;;;;;;;;;:::i;:::-;;;5126:14:28;;5119:22;5101:41;;5089:2;5074:18;6170:90:5;4961:187:28;25113:836:5;;;;;;:::i;:::-;;:::i;36601:135::-;;;;;;;;;;-1:-1:-1;36695:34:5;;36601:135;;1638:37:8;;;;;;;;;;;;;;;;2207:38:5;;;;;;;;;;;;;;;4357:133:4;;;;;;;;;;-1:-1:-1;4357:133:4;;;;;:::i;:::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4457:26:4;;;;;;;;:13;:26;;;;;;;;;4450:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4357:133;36789:105:5;;;;;;;;;;-1:-1:-1;36868:19:5;;36789:105;;33599:887;;;;;;;;;;-1:-1:-1;33599:887:5;;;;;:::i;:::-;;:::i;24724:252::-;;;;;;;;;;;;;:::i;1542:33:8:-;;;;;;;;;;;;;;;;5205:91:7;;;;;;;;;;-1:-1:-1;5277:12:7;5205:91;;36439:109:5;;;;;;;;;;-1:-1:-1;36520:21:5;;36439:109;;1661:88:3;;;;;;;;;;-1:-1:-1;1705:7:3;1731:11;1661:88;;3666::6;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;3735:12:6;;;;;;;;3742:5;3735:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3666:88;;;;;7265:13:28;;7280:8;7261:28;7243:47;;7350:4;7338:17;;;7332:24;7358:18;7328:49;;;7306:20;;;7299:79;;;;7426:17;;;7420:24;7416:49;;;7394:20;;;7387:79;7231:2;7216:18;3666:88:6;7039:433:28;3331:85:8;;;;;;;;;;-1:-1:-1;3401:8:8;3331:85;;3464:84;;;;;;;;;;-1:-1:-1;3536:5:8;3464:84;;3735:99;;;;;;;;;;-1:-1:-1;3811:16:8;3735:99;;26104:237:5;26247:7;26277:57;26287:8;26297:6;26305:5;26312:11;26325:8;;26277:9;:57::i;:::-;26270:64;26104:237;-1:-1:-1;;;;;;26104:237:5:o;1217:133:4:-;1306:22;;1265:7;;1291:38;;1306:22;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4457:26:4;;;;;;;;:13;:26;;;;;;;;;4450:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4357:133;1291:38;:52;;1217:133;-1:-1:-1;1217:133:4:o;5665:452:5:-;5821:16;;;;5817:29;;;5665:452::o;5817:29::-;5928:30;;;;;5952:4;5928:30;;;2193:74:28;5961:12:5;5928:45;;;:5;:15;;;;;2166:18:28;;5928:30:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:45;5924:111;;;5996:28;;;;;;;;;;;;;;5924:111;6044:16;:23;;;;6063:4;6044:23;;;6082:28;;6097:12;2000:34:28;1988:47;1970:66;;6082:28:5;;1958:2:28;1943:18;6082:28:5;;;;;;;5665:452::o;27379:5636::-;27610:40;27653:12;:10;:12::i;:::-;27610:55;;27676:14;27693;27701:5;27693:7;:14::i;:::-;27676:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;27721:20:5;27717:51;;27750:18;;;;;;;;;;;;;;27717:51;6559:21;;6600:31;-1:-1:-1;6544:88:5;27839:266;;27894:9;27878:25;;:12;:25;27874:160;;27994:25;28007:5;28014:1;28017;27994:12;:25::i;:::-;27987:32;;27379:5636;;;:::o;27874:160::-;28054:40;;;;;;;;;;;;;;27839:266;28137:12;;;;28182:14;;28115:19;28331:46;28346:30;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4457:26:4;;;;;;;;:13;:26;;;;;;;;;4450:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4357:133;28331:46:5;28285:92;;28785:11;28742:25;:39;;;:54;;:148;;;;28879:11;28816:46;28831:25;:30;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4457:26:4;;;;;;;;:13;:26;;;;;;;;;4450:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4357:133;28816:46:5;:60;:74;28742:148;:214;;;;28943:13;28910:46;;:30;:46;;;28742:214;28725:312;;;28988:38;;;;;;;;;;;;;;28725:312;29172:33;29208:29;29223:13;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4457:26:4;;;;;;;;:13;:26;;;;;;;;;4450:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4357:133;29208:29:5;29483:39;;29172:65;;-1:-1:-1;29316:20:5;;;;29483:43;29479:315;;29704:79;29735:25;29762:15;29779:3;29704:30;:79::i;:::-;29653:130;;-1:-1:-1;29653:130:5;-1:-1:-1;29479:315:5;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30095:16:5;;;;30091:1664;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30333:12:5;30318:11;:27;;;30314:192;;-1:-1:-1;30384:22:5;30314:192;;;30464:27;30479:11;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4457:26:4;;;;;;;;:13;:26;;;;;;;;;4450:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4357:133;30464:27:5;30445:46;;30314:192;30538:37;30553:16;:21;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4457:26:4;;;;;;;;:13;:26;;;;;;;;;4450:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4357:133;30538:37:5;30520:55;;30798:11;30764:16;:30;;;:45;;:92;;;-1:-1:-1;30813:29:5;;:43;-1:-1:-1;30764:92:5;30760:172;;;30883:34;;;;;;;;;;;;;;30760:172;30113:829;30091:1664;;;31179:9;31164:24;;:12;:24;31160:75;;;31197:38;;;;;;;;;;;;;;31160:75;-1:-1:-1;31644:29:5;;31505:22;;31644:44;;31640:105;;31715:15;;;;;;;;;;;;;;31640:105;32383:29;;:44;;;32379:568;;32443:21;32467;32476:11;32467:8;:21::i;:::-;:39;;;32443:63;;32521:27;32550:31;32585:137;32637:3;32642:13;32657:15;:51;;;32585:34;:137::i;:::-;32520:202;;-1:-1:-1;32520:202:5;-1:-1:-1;32844:35:5;32520:202;32844:35;;:::i;:::-;;-1:-1:-1;32893:43:5;32913:23;32893:43;;:::i;:::-;;;32429:518;;;32379:568;32957:51;32970:5;32977:12;32991:16;32957:12;:51::i;:::-;27494:5521;;;;;;;;;27379:5636;;;:::o;36947:149::-;36994:7;37020:69;:48;397:27:23;37020:19:5;;:30;;:48;;;;:::i;:::-;:67;:69::i;:::-;37013:76;;36947:149;:::o;1787:102:3:-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1868:14:3;1876:5;1868:7;:14::i;:::-;1861:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1787:102;-1:-1:-1;;1787:102:3:o;33068:478:5:-;4770:11;4755:26;;:12;:26;4751:53;;;4790:14;;;;;;;;;;;;;;4751:53;5526:22:::1;::::0;5552:9:::1;5526:35;::::0;;::::1;:22:::0;::::1;:35;5522:78;;5577:12;:10;:12::i;:::-;;5522:78;6559:21:::0;;6600:31;-1:-1:-1;6544:88:5;33241:42:::2;;33269:14;;;;;;;;;;;;;;33241:42;33295:13;33310:20:::0;33334:28:::2;33355:6;33334:20;:28::i;:::-;33294:68:::0;;-1:-1:-1;33294:68:5;-1:-1:-1;33377:16:5;;33373:167:::2;;33409:59;:38;33431:5;33409:38;33448:5:::0;33455:12;33409:38:::2;:59::i;:::-;33509:5;33487:42;;33501:6;33487:42;33516:12;33487:42;;;;365:25:28::0;;353:2;338:18;;219:177;33487:42:5::2;;;;;;;;33373:167;33163:383;;33068:478:::0;:::o;5481:105:7:-;-1:-1:-1;;;;;;;;;;;;;;;;;5564:15:7;5573:5;5564:8;:15::i;:::-;5557:22;;;;;;;;;;;;;;;;;;;;;;;5481:105;-1:-1:-1;;5481:105:7:o;35585:487:5:-;4550:9;4535:24;;:12;:24;4531:55;;;4568:18;;;;;;;;;;;;;;4531:55;5526:22:::1;::::0;5552:9:::1;5526:35;::::0;;::::1;:22:::0;::::1;:35;5522:78;;5577:12;:10;:12::i;:::-;;5522:78;35689:22:::2;::::0;:27;35685:59:::2;;35725:19;;;;;;;;;;;;;;35685:59;35754:20;35788:14;6559:21:::0;;6600:31;-1:-1:-1;6544:88:5;;6474:165;35788:14:::2;35784:240;;;35833:122;:84;397:27:23;35833:55:5;35868:19;;35833:30;:16;:28;:30::i;:::-;:34:::0;::::2;:55::i;:::-;:66:::0;::::2;:84::i;:122::-;35818:137;;35784:240;;;-1:-1:-1::0;35986:27:5::2;36001:12;35986:27;35784:240;36033:32;36052:12;36033:18;:32::i;:::-;35675:397;35585:487::o:0;35129:403::-;4550:9;4535:24;;:12;:24;4531:55;;;4568:18;;;;;;;;;;;;;;4531:55;5526:22:::1;::::0;5552:9:::1;5526:35;::::0;;::::1;:22:::0;::::1;:35;5522:78;;5577:12;:10;:12::i;:::-;;5522:78;35270:18:::2;::::0;:23;35266:57:::2;;35302:21;;;;;;;;;;;;;;35266:57;6559:21:::0;;6600:31;-1:-1:-1;6544:88:5;35440:42:::2;;35468:14;;;;;;;;;;;;;;35440:42;35492:33;35507:17;:15;:17::i;:::-;35492:14;:33::i;:::-;35129:403::o:0;26394:932::-;4550:9;4535:24;;:12;:24;4531:55;;;4568:18;;;;;;;;;;;;;;4531:55;26468:14:::1;26485;26493:5;26485:7;:14::i;:::-;26468:31;::::0;;::::1;::::0;::::1;::::0;;;;::::1;::::0;;::::1;::::0;;::::1;::::0;;::::1;;;::::0;::::1;::::0;;;;::::1;;::::0;;;;;;;;::::1;::::0;;;;;::::1;::::0;::::1;::::0;::::1;;::::0;;;;::::1;::::0;::::1;::::0;;;;;::::1;::::0;;::::1;::::0;;;;;;;-1:-1:-1;26513:20:5;26509:51:::1;;26542:18;;;;;;;;;;;;;;26509:51;26570:33;26606:21;:19;:21::i;:::-;26570:57;;26642:14;6559:21:::0;;6600:31;-1:-1:-1;6544:88:5;;6474:165;26642:14:::1;26637:161;;26762:25;26775:5;26782:1;26785::::0;26762:12:::1;:25::i;26637:161::-;26933:29:::0;;26917:12:::1;::::0;::::1;::::0;:45:::1;26913:73;;26971:15;;;;;;;;;;;;;;26913:73;27049:33;27085:30;27100:3;:14;;;-1:-1:-1::0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4457:26:4;;;;;;;;:13;:26;;;;;;;;;4450:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4357:133;27085:30:5::1;27049:66;;27126:20;27148:24:::0;27188:69:::1;27219:15;27236;27253:3;27188:30;:69::i;:::-;27125:132;;;;27268:51;27281:5;27288:12;27302:16;27268:12;:51::i;:::-;26458:868;;;;;26394:932:::0;:::o;1042:130:4:-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1142:22:4;;1127:38;;1142:22;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4457:26:4;;;;;;;;:13;:26;;;;;;;;;4450:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4357:133;6692:99:5;6741:7;6767:17;:15;:17::i;6170:90::-;6216:4;6239:14;6559:21;;6600:31;-1:-1:-1;6544:88:5;;6474:165;25113:836;25303:7;4994:20;:18;:20::i;:::-;25407:9:::1;25391:25;;:12;:25;25387:53;;25425:15;;;;;;;;;;;;;;25387:53;25454:6;:11;;25464:1;25454:11:::0;25450:43:::1;;25474:19;;;;;;;;;;;;;;25450:43;25507:19;::::0;::::1;25503:61;;25535:29;;;;;;;;;;;;;;25503:61;25578:22;:8;:22;25574:292;;25635:6;25622:19;;:9;:19;25618:47;;25650:15;;;;;;;;;;;;;;25618:47;25574:292;;;25700:9;:14:::0;25696:48:::1;;25723:21;;;;;;;;;;;;;;25696:48;25758:97;25810:8;25821:10;25841:4;25848:6;25758:97;;:35;:97::i;:::-;25882:60;25893:8;25903:6;25911:5;25918:13;25933:8;;25882:10;:60::i;:::-;25875:67:::0;25113:836;-1:-1:-1;;;;;;;25113:836:5:o;33599:887::-;4770:11;4755:26;;:12;:26;4751:53;;;4790:14;;;;;;;;;;;;;;4751:53;5526:22:::1;::::0;5552:9:::1;5526:35;::::0;;::::1;:22:::0;::::1;:35;5522:78;;5577:12;:10;:12::i;:::-;;5522:78;6559:21:::0;;6600:31;-1:-1:-1;6544:88:5;33833:42:::2;;33861:14;;;;;;;;;;;;;;33833:42;33886:20;33925:9:::0;33920:443:::2;33940:18:::0;;::::2;33920:443;;;33980:16;33998:23:::0;34025:32:::2;34046:7;;34054:1;34046:10;;;;;;;:::i;:::-;;;;;;;34025:20;:32::i;:::-;33979:78;;;;34088:6;34076:18;;:8;:18;;;34072:106;;34121:42;::::0;::::2;::::0;;9114::28;9102:55;;;34121:42:5::2;::::0;::::2;9084:74:28::0;9194:55;;9174:18;;;9167:83;9057:18;;34121:42:5::2;;;;;;;;34072:106;34192:31;34208:15:::0;34192:31;::::2;:::i;:::-;::::0;-1:-1:-1;34242:19:5;;34238:115:::2;;34312:8;34286:52;;34300:7;;34308:1;34300:10;;;;;;;:::i;:::-;;;;;;;34286:52;34322:15;34286:52;;;;365:25:28::0;;353:2;338:18;;219:177;34286:52:5::2;;;;;;;;34238:115;-1:-1:-1::0;;33960:3:5::2;;33920:443;;;-1:-1:-1::0;34377:16:5;;34373:107:::2;;34409:60;:38;34431:5;34409:38;34448:6:::0;34456:12;34409:38:::2;:60::i;:::-;33755:731;33599:887:::0;;;:::o;24724:252::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4994:20:5;:18;:20::i;:::-;24828:9:::1;24813:24;;:12;:24;24809:161;;;24860:21;:19;:21::i;24809:161::-;24919:40;24945:12;24919:18;:40::i;566:172:3:-:0;621:11;657;;648:5;:20;644:57;;677:24;;;;;;;;365:25:28;;;338:18;;677:24:3;219:177:28;644:57:3;-1:-1:-1;718:13:3;;;;:6;:13;;;;;;566:172::o;24162:509:5:-;24266:16;24285:14;24293:5;24285:7;:14::i;:::-;24326:10;;;;24365:14;;;;24266:33;;-1:-1:-1;24326:10:5;;;24309:14;;357:2:23;;24365:33:5;;24382:16;;24365:33;:::i;:::-;24437:17;;;:32;;;24479:39;;;;;24505:12;24479:39;;;;;;24364:62;;;-1:-1:-1;24533:10:5;;24529:74;;24559:33;:17;:8;:17;24577:6;24585;24559:17;:33::i;:::-;24618:46;;;9568:25:28;;;9624:2;9609:18;;9602:34;;;24618:46:5;;;;24628:5;;24618:46;;9541:18:28;24618:46:5;;;;;;;24256:415;;;24162:509;;;:::o;3049:321:4:-;3212:20;3234:24;3281:82;3335:5;3342:15;3359:3;3281:53;:82::i;:::-;3274:89;;;;3049:321;;;;;;;:::o;1823:256:7:-;1879:12;1983:20;1991:12;1983:5;:20;:::i;:::-;:25;1979:62;;2017:24;;;;;;;;;;;;;;1979:62;-1:-1:-1;2058:14:7;;;;:7;:14;;;;;;1823:256::o;3768:389:4:-;3949:20;3971:24;4014:136;4085:3;4090:13;4105:35;4014:57;:136::i;620:116:27:-;676:7;711:21;731:1;726;711:21;:::i;:::-;691:42;;620:116;;;;;:::o;1314:125::-;1380:7;1406:26;250:3:21;1421:5:27;1406:26;:::i;34698:378:5:-;34761:13;34776:20;34808:16;34827:14;34835:5;34827:7;:14::i;:::-;34855:16;;;;-1:-1:-1;34855:16:5;;;;;;:21;34851:48;;34885:14;;;;;;;;;;;;;;34851:48;34947:10;;;;34982:17;;;;;;34947:10;35048:21;;;34947:10;;;;;34982:17;;-1:-1:-1;34698:378:5;-1:-1:-1;;34698:378:5:o;779:2536:22:-;1050:12;1076:22;;;1072:2237;;1263:1;1260;1257;1254;1246:6;1242:2;1235:5;1230:35;1219:46;;1345:7;1340:76;;1379:22;;;;;;;;;;;;;;1340:76;1072:2237;;;1560:4;1554:11;1696:66;1691:3;1684:79;1808:42;1804:2;1800:51;1796:1;1791:3;1787:11;1780:72;1928:6;1923:2;1918:3;1914:12;1907:28;2805:2;2802:1;2798:2;2793:3;2790:1;2780:8;2773:5;2768:40;2317:16;2310:24;2304:2;2286:16;2283:24;2279:1;2275;2269:8;2266:15;2262:46;2259:76;2047:779;2036:790;;2904:1;2899:3;2892:14;3005:1;2998:4;2993:3;2989:14;2982:25;3108:1;3101:4;3096:3;3092:14;3085:25;;3229:7;3224:75;;3263:21;;;;;;;;;;;;;;1106:116:27;1165:7;1204:10;250:3:21;1204:5:27;:10;:::i;445:125::-;494:7;529:37;564:1;544;529:37;:::i;2995:273:8:-;3083:12;3058:22;:37;3109:10;;3105:105;;3135:64;:38;3157:5;3135:38;3174:16;3192:6;3135:38;:64::i;:::-;3236:16;3224:37;;;3254:6;3224:37;;;;365:25:28;;353:2;338:18;;219:177;3224:37:8;;;;;;;;2995:273;:::o;6902:154:5:-;6952:7;6978:71;:50;397:27:23;6978:21:5;;:32;;:50;;;;:::i;2745:244:8:-;2825:12;2804:18;:33;2851:10;;2847:83;;2877:42;:17;:8;:17;2895:15;2912:6;2877:17;:42::i;:::-;2958:15;2944:38;;;2975:6;2944:38;;;;365:25:28;;353:2;338:18;;219:177;20955:121:5;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21040:29:5;21059:9;21040:18;:29::i;5196:178::-;5269:11;5254:26;;:12;:26;5250:58;;;5289:19;;;;;;;;;;;;;;5250:58;5323:16;;;;5318:49;;5348:19;;;;;;;;;;;;;;21987:1125:2;22185:4;22179:11;22240:5;22236:2;22232:14;22228:2;22224:23;22217:4;22214:1;22210:12;22203:45;22282:6;22275:4;22272:1;22268:12;22261:28;22323:2;22316:4;22313:1;22309:12;22302:24;22368:4;22364:2;22360:13;22353:4;22350:1;22346:12;22339:35;22461:34;22458:1;22451:45;22518:7;22566:1;22555:9;22552:16;22591:6;22581:62;;-1:-1:-1;22625:14:2;;22618:22;22611:30;22581:62;22816:6;22807:5;22795:18;22788:26;22785:38;22758:4;22746:10;22740:4;22733;22730:1;22726:12;22723:1;22720;22713:5;22708:55;22683:196;22656:440;;22925:18;22919:4;22912:32;23077:4;23064:6;23059:3;23055:16;23048:24;23041:32;23038:1;23034:40;23028:4;23024:51;23017:65;22656:440;;;;21987:1125;;;;:::o;21488:2260:5:-;21639:13;21770;21759:8;:24;21755:84;;;21792:47;;;;;;;;9568:25:28;;;21825:13:5;9609:18:28;;;9602:34;9541:18;;21792:47:5;9394:248:28;21755:84:5;21913:29;21945:12;:10;:12::i;:::-;21913:44;;22028:35;:11;:33;:35::i;:::-;:40;;22067:1;22028:40;22024:69;;22077:16;;;;;;;;;;;;;;22024:69;22197:25;;22185:37;;22181:79;;22231:29;;;;;;;;;;;;;;22181:79;22365:48;22389:13;22404:8;22365:23;:48::i;:::-;22506:77;:30;:15;:30;22537:8;22547:6;22555:5;22562:10;22574:8;;22506:30;:77::i;:::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22618:17:5;357:2:23;22638:42:5;;22646:6;22638:15;;:42;;22618:62;;22705:65;22716:9;22727:5;22734:8;22744:11;:25;;;22705:10;:65::i;:::-;22690:80;-1:-1:-1;22690:80:5;-1:-1:-1;22951:29:5;22983:23;22690:80;22983:21;:23::i;:::-;22951:55;;23079:50;23097:8;23107:21;23079:17;:50::i;:::-;23347:21;23309:34;;:59;;;;;;;:::i;:::-;;;;-1:-1:-1;344:23:21;;-1:-1:-1;364:3:21;344:17;:23;:::i;:::-;23558:34:5;;:65;23554:128;;23646:25;;;;;;;;;;;;;;23554:128;23697:44;;;10425:25:28;;;10498:34;10486:47;;10481:2;10466:18;;10459:75;23697:44:5;;;;23710:5;;23697:44;;10398:18:28;23697:44:5;;;;;;;21658:2090;;;;21488:2260;;;;;;;;:::o;18664:2006::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18802:22:5;;;;;;;18838:36;;;;;18834:67;;18883:18;:16;:18::i;:::-;18876:25;18664:2006;-1:-1:-1;;;18664:2006:5:o;18834:67::-;18926:18;:16;:18::i;:::-;18912:32;;18954:21;18978:50;19016:11;18978:37;:50::i;:::-;19059:25;;18954:74;;-1:-1:-1;19042:42:5;;19038:402;;19142:41;;;:25;19297:47;;;;:65;;;;19381:48;;;10748:18:28;10736:31;;10718:50;;10784:18;;;10777:34;;;19381:48:5;;10691:18:28;19381:48:5;;;;;;;19038:402;19580:23;19605:15;19624:64;19653:11;19666:21;19624:28;:64::i;:::-;19579:109;;;;20048:17;20089:61;20111:4;:15;;;20089:61;;20128:21;20089:61;;49139:9:0;;;49150:8;;-1:-1:-1;49135:24:0;49128:32;;48978:198;20089:61:5;20068:83;;:11;:83;:::i;:::-;20217:8;;20204:21;;;;20185:41;;;;;20048:103;-1:-1:-1;20370:49:5;20397:11;20185:41;20370:26;:49::i;:::-;20356:63;;20520:43;20538:11;20551;20520:17;:43::i;:::-;20610:25;;20637;;;;;20579:84;;;11250:18:28;11238:31;;11220:50;;11301:2;11286:18;;11279:34;;;;11361:8;11349:21;;;11329:18;;;11322:49;20579:84:5;;11193:18:28;20579:84:5;;;;;;;18761:1909;;;;;18664:2006;;;:::o;1142:452:19:-;1304:20;1326:24;1401:186;1428:3;1475:15;:37;;;1445:5;:27;;;:67;;;;:::i;:::-;1548:15;:29;;;1526:5;:19;;;:51;;;;:::i;:::-;1401:13;:186::i;:::-;1366:221;;;;-1:-1:-1;1142:452:19;-1:-1:-1;;;;1142:452:19:o;1992:1070::-;2172:20;2194:24;2234:13;2251:1;2234:18;2230:37;;-1:-1:-1;2262:1:19;;-1:-1:-1;2262:1:19;2254:13;;2230:37;2547:19;2585:42;:3;:40;:42::i;:::-;2569:58;;;;:13;:58;:::i;:::-;2656:13;;;;2547:80;;-1:-1:-1;2656:92:19;;2698:35;2547:80;2656:26;:92::i;:::-;3043:12;;;;2950:13;;;;2637:111;;-1:-1:-1;3043:12:19;2950:90;;2990:35;3028:11;2950:24;:90::i;:::-;:105;;;;:::i;:::-;2923:132;;2220:842;1992:1070;;;;;;:::o;922:161:20:-;1007:6;1051:11;:25;;;250:3:21;1032:44:20;;;;:::i;2650:1961:7:-;991:17;2740:5;:21;2736:52;;2770:18;;;;;;;;;;;;;;2736:52;2893:21;2917:15;2926:5;2917:8;:15::i;:::-;3005:13;;2893:39;;-1:-1:-1;3005:18:7;3001:31;;3025:7;2650:1961;;:::o;3001:31::-;3260:5;3247:9;:18;3243:57;;3274:26;;;;;;;;;;;;;;3243:57;3310:17;3330:19;3339:9;3330:8;:19::i;:::-;:24;;-1:-1:-1;3330:24:7;3472:14;;;3468:53;;3495:26;;;;;;;;;;;;;;3468:53;3993:5;3981:9;:17;3974:122;;;4026:9;4014:21;;4061:19;4070:9;4061:8;:19::i;:::-;:24;;-1:-1:-1;3974:122:7;;;4144:25;;;4206:5;4179:19;4188:9;4179:8;:19::i;:::-;:32;4445:20;;4432:33;;4428:139;;4481:20;:28;;;4528;;365:25:28;;;4528:28:7;;353:2:28;338:18;4528:28:7;;;;;;;4428:139;4582:22;;365:25:28;;;4582:22:7;;353:2:28;338:18;4582:22:7;;;;;;;2726:1885;;2650:1961;;:::o;528:429:26:-;741:27;;;737:40;770:7;737:40;791:56;;;;;:13;;;;;;:56;;805:8;;815:6;;823:5;;830:6;;838:8;;;;791:56;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;787:164;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;933:6;908:32;;;;;;;;;;;:::i;1078:529:3:-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1259:259:3;;;;;;;;;1296:12;1259:259;;;;;;;;;;;;;;;1224:13;1259:259;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1537:11;;1558:13;;;:6;:13;;;;;;;:19;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1587:13;;1259:259;;1537:11;1587:13;1224;1587;;;:::i;:::-;;;;;;1078:529;;;;;;;:::o;1708:307:18:-;1774:7;1793:28;1824:42;:3;:40;:42::i;:::-;1793:73;;1880:21;:26;;1905:1;1880:26;1876:59;;1915:20;;;;;;;;;;;;;;1876:59;1952:13;;;;:56;;;;:32;;250:3:21;;1952:32:18;:::i;:::-;:56;;;;:::i;4769:254:7:-;4857:18;4878:15;4887:5;4878:8;:15::i;:::-;4907:10;;4857:36;;-1:-1:-1;4907:10:7;:15;4903:59;;4931:31;;;;;;;;;;;;;;4903:59;4999:17;4972:5;:23;;;:44;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;4769:254:7:o;14267:3809:5:-;14556:25;;14363:7;;14591:11;32048:9:0;;32041:17;32035:24;14777:35:5;14556:25;14777:33;:35::i;:::-;:40;;14816:1;14777:40;14773:98;;14840:20;14267:3809;-1:-1:-1;;14267:3809:5:o;14773:98::-;15019:34;;15094:20;;14939:25;;15019:34;14939:25;15915:54;15019:34;15915:54;15956:12;15915:54;:40;:54::i;:::-;15891:78;;15979:1209;991:17:7;16135:20:5;:36;;:133;;;;-1:-1:-1;16233:35:5;16248:20;16233:35;:12;:35;;:::i;:::-;16195:34;:73;;16135:133;16134:450;;;;16564:20;16547:13;:37;16134:450;15979:1209;;;16609:28;16640:30;16649:20;16640:8;:30::i;:::-;16609:61;;16809:15;:33;;;16771:71;;;;;:::i;:::-;17032:20;;16935;;-1:-1:-1;16771:71:5;-1:-1:-1;17082:54:5;16771:71;17082:54;17123:12;17082:54;:40;:54::i;:::-;17066:70;;17173:4;17150:27;;16595:593;15979:1209;;;17281:20;17277:241;;;17317:34;:71;;;17402:20;:43;;;17464;;365:25:28;;;17464:43:5;;353:2:28;338:18;17464:43:5;;;;;;;17277:241;17878:20;17862:13;:36;17858:212;;;-1:-1:-1;17921:20:5;;14267:3809;-1:-1:-1;;;;;14267:3809:5:o;13034:888::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;13394:12:5;;;;;;;13401:5;13394:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;13438:62:5;;;49139:9:0;;;-1:-1:-1;;;49135:24:0;49128:32;;13579:337:5;13601:3;13586:18;;:12;:18;;;13579:337;;;13620:17;13640:11;13646:5;13640:3;:11;:::i;:::-;13620:31;;13725:3;13712:16;;:10;:16;13693:36;;;;13765:3;13757:11;;13793:9;13786:16;;:3;:16;;;13782:27;;13804:5;;;13782:27;13830:14;:12;:14::i;:::-;13823:21;;13864:4;:8;;;13858:14;;13892:4;:13;;;13886:19;;13606:310;13579:337;;;13204:718;;;13034:888;;;;;:::o;7496:5066::-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8741:25:5;;8845:34;;8796:17;;;;9154:23;;;9492;9503:12;8741:25;9492:23;:::i;:::-;9519:1;9492:28;9488:2139;;9536:24;9563:18;9572:8;9563;:18::i;:::-;:36;;;;-1:-1:-1;9617:20:5;;9613:2004;;9700:22;10151:21;10159:12;10151:21;:32;;10150:46;;9657:40;10363:61;9700:22;10150:46;10363:61;:::i;:::-;10331:93;-1:-1:-1;10667:28:5;;;48635:9:0;;;-1:-1:-1;;;48631:24:0;48624:32;;11219:62:5;11249:32;48624::0;11219:62:5;:::i;:::-;11194:87;;11557:27;11506:11;:47;;;11491:93;;;;:::i;:::-;11407:47;;;:195;-1:-1:-1;;;;;9613:2004:5;9522:2105;9488:2139;11928:26;11957:63;:22;397:27:23;12011:8:5;11957:35;:63::i;:::-;11928:92;;12103:18;12080:19;;12065:56;;;;:::i;:::-;12030:19;:92;12225:21;;12210:62;;12250:22;;12210:62;:::i;:::-;12173:21;:100;12284:25;;;:37;;12313:8;;12284:25;:37;;12313:8;;12284:37;:::i;:::-;;;;;-1:-1:-1;12479:48:5;12508:8;12518;12479:28;:48::i;:::-;12442:11;:33;;:85;;;;;;;:::i;:::-;;;-1:-1:-1;12544:11:5;;7496:5066;-1:-1:-1;;;;;;;7496:5066:5:o;1703:764:4:-;1831:22;;;;;;;1931:37;;;-1:-1:-1;1927:80:4;;1977:30;;;;;;;;;;;;;;1927:80;2075:40;;;;:15;;;:40;;;2125:15;;;:34;;;-1:-1:-1;2227:37:4;;;:13;:37;;;;;;;;:42;;;;:56;;;;;;;;;;;;;;;;;2329:26;;;;;;;:39;;;;;;;;-1:-1:-1;2329:39:4;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2424:36;;;;;;;1703:764::o;3735:847:19:-;3884:20;3906:24;3946:43;3992:42;:3;:40;:42::i;:::-;4182:13;;;;3946:88;;-1:-1:-1;4182:84:19;;;;;;;;;:26;:84::i;:::-;4163:103;-1:-1:-1;4378:197:19;4433:26;4477:84;;;;4478:43;4477:84;:::i;:::-;4378:13;;;;;:197;:37;:197::i;1168:158:18:-;1253:6;1297:3;:22;;;250:3:21;1278:41:18;;;;:::i;25645:437:0:-;25723:9;25748:19;25759:1;25762;25765;25748:10;:19::i;:::-;25744:23;;25859:1;25856;25853;25846:15;25843:223;;;25892:1;25885:9;;25911:141;;25959:10;25953:4;25946:24;26029:4;26023;26016:18;20943:3287;21489:9;;;21651:1;21646;21642;21639;21635:9;21632:16;21628:1;21621:9;21618:31;21614:39;21604:2543;;21700:6;21697:1;21694;21687:20;21753:9;;;21746:17;;21738:26;;22012:1;22009;22006;21999:15;22093:1;22090;22086:9;22083:1;22079:17;22355:2;22352:1;22349:9;22339:161;;22399:10;22393:4;22386:24;22473:4;22467;22460:18;22339:161;22526:9;;;;;23939:8;;;23931:17;;;23962:1;23958:9;;;23954:17;;;23973:1;23950:25;23927:49;23982:9;;23978:17;23924:72;22972:1;22979;22975:9;;22968:17;;23270:11;;;23263:19;;23254:29;23347:11;;;23340:19;;23331:29;23425:11;;;23418:19;;23409:29;23503:11;;;23496:19;;23487:29;23581:11;;;23574:19;;23565:29;24037:11;;;24030:19;;;24026:29;23666:437;24124:5;;21604:2543;24169:9;;20943:3287;-1:-1:-1;;20943:3287:0:o;29513:344::-;29573:9;29670:1;29660:122;;29704:10;29698:4;29691:24;29763:4;29757;29750:18;29660:122;-1:-1:-1;29818:9:0;;;29811:17;29804:25;29831:9;;29800:41;;29513:344::o;2960:667:6:-;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;3048:7:6;3036:8;;:19;3032:47;;3064:15;;;;;;;;;;;;;;3032:47;3134:8;;3090:19;;3119:56;;3144:30;451:1:25;3134:8:6;3144:30;:::i;:::-;3119:14;:9;:14;;:56;:14;:56::i;:::-;3112:64;;;:::i;:::-;3272:5;:14;3090:86;;-1:-1:-1;905:20:25;;;;;955:12;;;;;3272:14:6;;;;;3187:10;3300:16;;;3296:47;;-1:-1:-1;3332:11:6;3296:47;3353:16;3372:32;3386:18;;;3372:11;:32;:::i;:::-;3423:69;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3415:5;:77;;;;;;;;;;;;;;;;;;;;;;;;3503:8;:31;;3423:69;;-1:-1:-1;451:1:25;;-1:-1:-1;;3503:31:6;;451:1:25;;3503:31:6;:::i;:::-;;;;-1:-1:-1;;3550:48:6;;;14221:18:28;14209:31;;;14191:50;;14277:31;;14272:2;14257:18;;14250:59;14357:8;14345:21;;14325:18;;;14318:49;3550:48:6;;;;;;;14179:2:28;3550:48:6;;;-1:-1:-1;;3608:12:6;;;;;;;;3615:5;3608:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2960:667;-1:-1:-1;;;;2960:667:6:o;1357:310:20:-;1431:7;1454:5;1463:1;1454:10;1450:24;;-1:-1:-1;1473:1:20;1466:8;;1450:24;1631:29;1655:5;1632:19;1648:3;1632:19;;;;1631:29;:::i;11718:912:1:-;11937:4;11931:11;11973:6;11965:15;;11955:44;;11991:6;11984:13;;11955:44;12052:3;12045:5;12042:14;12034:5;12029:3;12025:15;12021:36;12122:4;12119:1;12115:12;12108:5;12101:4;12095;12091:15;12082:7;12070:58;12177:1;12171:4;12167:12;12161:19;12155:4;12151:30;12141:270;;12236:4;12226:7;12214:20;12210:31;12317:1;12313:2;12309:10;12291:16;12273;12258:62;12392:1;12387:3;12383:11;12379:1;12374:3;12371:10;12367:28;12364:1;12360:36;12352:5;12349:1;12346:12;12342:55;12337:60;;;12141:270;12437:1;12431:4;12424:15;12505:1;12501;12494:4;12488;12484:15;12480:23;12473:34;12591:1;12584:4;12578;12574:15;12570:23;12564:4;12557:37;;11718:912;;;;;:::o;401:188:28:-;469:20;;529:34;518:46;;508:57;;498:85;;579:1;576;569:12;498:85;401:188;;;:::o;594:196::-;662:20;;722:42;711:54;;701:65;;691:93;;780:1;777;770:12;795:347;846:8;856:6;910:3;903:4;895:6;891:17;887:27;877:55;;928:1;925;918:12;877:55;-1:-1:-1;951:20:28;;994:18;983:30;;980:50;;;1026:1;1023;1016:12;980:50;1063:4;1055:6;1051:17;1039:29;;1115:3;1108:4;1099:6;1091;1087:19;1083:30;1080:39;1077:59;;;1132:1;1129;1122:12;1077:59;795:347;;;;;:::o;1147:672::-;1244:6;1252;1260;1268;1276;1329:3;1317:9;1308:7;1304:23;1300:33;1297:53;;;1346:1;1343;1336:12;1297:53;1391:23;;;-1:-1:-1;1457:38:28;1491:2;1476:18;;1457:38;:::i;:::-;1447:48;;1514:38;1548:2;1537:9;1533:18;1514:38;:::i;:::-;1504:48;;1603:2;1592:9;1588:18;1575:32;1630:18;1622:6;1619:30;1616:50;;;1662:1;1659;1652:12;1616:50;1701:58;1751:7;1742:6;1731:9;1727:22;1701:58;:::i;:::-;1147:672;;;;-1:-1:-1;1147:672:28;;-1:-1:-1;1778:8:28;;1675:84;1147:672;-1:-1:-1;;;1147:672:28:o;2278:171::-;2345:20;;2405:18;2394:30;;2384:41;;2374:69;;2439:1;2436;2429:12;2454:370;2529:6;2537;2545;2598:2;2586:9;2577:7;2573:23;2569:32;2566:52;;;2614:1;2611;2604:12;2566:52;2659:23;;;-1:-1:-1;2725:37:28;2758:2;2743:18;;2725:37;:::i;:::-;2715:47;;2781:37;2814:2;2803:9;2799:18;2781:37;:::i;:::-;2771:47;;2454:370;;;;;:::o;2829:226::-;2888:6;2941:2;2929:9;2920:7;2916:23;2912:32;2909:52;;;2957:1;2954;2947:12;2909:52;-1:-1:-1;3002:23:28;;2829:226;-1:-1:-1;2829:226:28:o;5153:793::-;5259:6;5267;5275;5283;5291;5299;5352:3;5340:9;5331:7;5327:23;5323:33;5320:53;;;5369:1;5366;5359:12;5320:53;5414:23;;;-1:-1:-1;5480:38:28;5514:2;5499:18;;5480:38;:::i;:::-;5470:48;;5537:38;5571:2;5560:9;5556:18;5537:38;:::i;:::-;5527:48;-1:-1:-1;5648:2:28;5633:18;;5620:32;;-1:-1:-1;5729:3:28;5714:19;;5701:33;5757:18;5746:30;;5743:50;;;5789:1;5786;5779:12;5743:50;5828:58;5878:7;5869:6;5858:9;5854:22;5828:58;:::i;:::-;5153:793;;;;-1:-1:-1;5153:793:28;;-1:-1:-1;5153:793:28;;5905:8;;5153:793;-1:-1:-1;;;5153:793:28:o;5951:184::-;6009:6;6062:2;6050:9;6041:7;6037:23;6033:32;6030:52;;;6078:1;6075;6068:12;6030:52;6101:28;6119:9;6101:28;:::i;6350:684::-;6445:6;6453;6461;6514:2;6502:9;6493:7;6489:23;6485:32;6482:52;;;6530:1;6527;6520:12;6482:52;6553:29;6572:9;6553:29;:::i;:::-;6543:39;;6633:2;6622:9;6618:18;6605:32;6660:18;6652:6;6649:30;6646:50;;;6692:1;6689;6682:12;6646:50;6715:22;;6768:4;6760:13;;6756:27;-1:-1:-1;6746:55:28;;6797:1;6794;6787:12;6746:55;6837:2;6824:16;6863:18;6855:6;6852:30;6849:50;;;6895:1;6892;6885:12;6849:50;6948:7;6943:2;6933:6;6930:1;6926:14;6922:2;6918:23;6914:32;6911:45;6908:65;;;6969:1;6966;6959:12;6908:65;6350:684;;7000:2;6992:11;;;;;-1:-1:-1;7022:6:28;;-1:-1:-1;;;6350:684:28:o;7990:184::-;8060:6;8113:2;8101:9;8092:7;8088:23;8084:32;8081:52;;;8129:1;8126;8119:12;8081:52;-1:-1:-1;8152:16:28;;7990:184;-1:-1:-1;7990:184:28:o;8402:::-;8454:77;8451:1;8444:88;8551:4;8548:1;8541:15;8575:4;8572:1;8565:15;8591:125;8656:9;;;8677:10;;;8674:36;;;8690:18;;:::i;8721:184::-;8773:77;8770:1;8763:88;8870:4;8867:1;8860:15;8894:4;8891:1;8884:15;9261:128;9328:9;;;9349:11;;;9346:37;;;9363:18;;:::i;9647:184::-;9699:77;9696:1;9689:88;9796:4;9793:1;9786:15;9820:4;9817:1;9810:15;9836:112;9868:1;9894;9884:35;;9899:18;;:::i;:::-;-1:-1:-1;9933:9:28;;9836:112::o;9953:120::-;9993:1;10019;10009:35;;10024:18;;:::i;:::-;-1:-1:-1;10058:9:28;;9953:120::o;10078:168::-;10151:9;;;10182;;10199:15;;;10193:22;;10179:37;10169:71;;10220:18;;:::i;10822:194::-;10927:18;10920:26;;;10892;;;10888:59;;10959:28;;10956:54;;;10990:18;;:::i;11382:164::-;11477:8;11470:16;;;11452;;;11448:39;;11499:18;;11496:44;;;11520:18;;:::i;11551:876::-;11820:6;11809:9;11802:25;11875:34;11867:6;11863:47;11858:2;11847:9;11843:18;11836:75;11959:42;11951:6;11947:55;11942:2;11931:9;11927:18;11920:83;12051:42;12043:6;12039:55;12034:2;12023:9;12019:18;12012:83;12132:3;12126;12115:9;12111:19;12104:32;12173:6;12167:3;12156:9;12152:19;12145:35;12231:6;12223;12217:3;12206:9;12202:19;12189:49;12288:1;12258:22;;;12282:3;12254:32;;;12247:43;;;;12342:2;12330:15;;;12347:66;12326:88;12311:104;12307:114;;11551:876;-1:-1:-1;;;;;11551:876:28:o;12432:475::-;12579:2;12568:9;12561:21;12542:4;12611:6;12605:13;12654:6;12649:2;12638:9;12634:18;12627:34;12713:6;12708:2;12700:6;12696:15;12691:2;12680:9;12676:18;12670:50;12769:1;12764:2;12755:6;12744:9;12740:22;12736:31;12729:42;12898:2;12828:66;12823:2;12815:6;12811:15;12807:88;12796:9;12792:104;12788:113;12780:121;;;12432:475;;;;:::o;12912:195::-;12951:3;12982:66;12975:5;12972:77;12969:103;;13052:18;;:::i;:::-;-1:-1:-1;13099:1:28;13088:13;;12912:195::o;13112:161::-;13205:8;13180:16;;;13198;;;13176:39;;13227:17;;13224:43;;;13247:18;;:::i;13278:514::-;13395:12;;13443:4;13432:16;;13426:23;13475:66;13467:75;;;13395:12;13565:1;13554:13;;13551:235;;;13709:66;13639;13629:6;13626:1;13622:14;13619:1;13615:22;13611:95;13607:2;13603:104;13599:177;13590:186;;13551:235;;;13278:514;;;:::o;13797:191::-;13900:18;13865:26;;;13893;;;13861:59;;13932:27;;13929:53;;;13962:18;;:::i

Swarm Source

ipfs://1e840418aee3693d42978f5f484854c78e142f7467536258226a973196b0f860

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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