ETH Price: $2,801.21 (-5.02%)
 

Overview

ETH Balance

0.000000000000002369 ETH

Eth Value

Less Than $0.01 (@ $2,801.21/ETH)

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw243049832026-01-24 13:34:1134 hrs ago1769261651IN
Ekubo Protocol: ekuPo Token
0 ETH0.000005340.04268656
Withdraw242967452026-01-23 9:59:112 days ago1769162351IN
Ekubo Protocol: ekuPo Token
0 ETH0.000010490.07169473
Withdraw242966452026-01-23 9:38:592 days ago1769161139IN
Ekubo Protocol: ekuPo Token
0 ETH0.000007170.05021595
Withdraw242957122026-01-23 6:31:352 days ago1769149895IN
Ekubo Protocol: ekuPo Token
0 ETH0.000008520.06645811
Withdraw242936802026-01-22 23:42:593 days ago1769125379IN
Ekubo Protocol: ekuPo Token
0 ETH0.000007030.03748923
Withdraw242922122026-01-22 18:47:473 days ago1769107667IN
Ekubo Protocol: ekuPo Token
0 ETH0.000014330.09189249
Withdraw242922042026-01-22 18:46:113 days ago1769107571IN
Ekubo Protocol: ekuPo Token
0 ETH0.000010880.07005507
Withdraw242891492026-01-22 8:31:473 days ago1769070707IN
Ekubo Protocol: ekuPo Token
0 ETH0.000268512.05999084
Withdraw242845132026-01-21 16:58:474 days ago1769014727IN
Ekubo Protocol: ekuPo Token
0 ETH0.000123260.82975312
Withdraw242831292026-01-21 12:21:234 days ago1768998083IN
Ekubo Protocol: ekuPo Token
0 ETH0.000044180.31649693
Withdraw242820532026-01-21 8:45:354 days ago1768985135IN
Ekubo Protocol: ekuPo Token
0 ETH0.000024410.2057539
Withdraw242788032026-01-20 21:53:115 days ago1768945991IN
Ekubo Protocol: ekuPo Token
0 ETH0.000142681
Withdraw242788022026-01-20 21:52:595 days ago1768945979IN
Ekubo Protocol: ekuPo Token
0 ETH0.000137781
Withdraw242758392026-01-20 11:58:355 days ago1768910315IN
Ekubo Protocol: ekuPo Token
0 ETH0.000006830.050214
Withdraw242750202026-01-20 9:13:475 days ago1768900427IN
Ekubo Protocol: ekuPo Token
0 ETH0.000012590.08636603
Withdraw242702142026-01-19 17:08:236 days ago1768842503IN
Ekubo Protocol: ekuPo Token
0 ETH0.000009270.08368275
Withdraw242702042026-01-19 17:06:236 days ago1768842383IN
Ekubo Protocol: ekuPo Token
0 ETH0.00001080.08445148
Withdraw242691792026-01-19 13:40:116 days ago1768830011IN
Ekubo Protocol: ekuPo Token
0 ETH0.000003670.03291946
Withdraw242675072026-01-19 8:05:116 days ago1768809911IN
Ekubo Protocol: ekuPo Token
0 ETH0.000003160.0295815
Withdraw242675042026-01-19 8:04:356 days ago1768809875IN
Ekubo Protocol: ekuPo Token
0 ETH0.000003320.03074561
Withdraw242675012026-01-19 8:03:596 days ago1768809839IN
Ekubo Protocol: ekuPo Token
0 ETH0.000003360.03141957
Withdraw242667912026-01-19 5:41:236 days ago1768801283IN
Ekubo Protocol: ekuPo Token
0 ETH0.000004550.03646749
Withdraw242661362026-01-19 3:29:596 days ago1768793399IN
Ekubo Protocol: ekuPo Token
0 ETH0.000060020.5416735
Withdraw242661342026-01-19 3:29:356 days ago1768793375IN
Ekubo Protocol: ekuPo Token
0 ETH0.000059580.53765059
Withdraw242661302026-01-19 3:28:476 days ago1768793327IN
Ekubo Protocol: ekuPo Token
0 ETH0.000059890.54048141
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer243133502026-01-25 17:33:476 hrs ago1769362427
Ekubo Protocol: ekuPo Token
0.0499726 ETH
Mint And Deposit...243133502026-01-25 17:33:476 hrs ago1769362427
Ekubo Protocol: ekuPo Token
0.0499726 ETH
Transfer243124522026-01-25 14:33:479 hrs ago1769351627
Ekubo Protocol: ekuPo Token
0.03252117 ETH
Mint And Deposit...243124522026-01-25 14:33:479 hrs ago1769351627
Ekubo Protocol: ekuPo Token
0.03252117 ETH
Transfer243099932026-01-25 6:20:3517 hrs ago1769322035
Ekubo Protocol: ekuPo Token
0.0010811 ETH
Mint And Deposit...243099932026-01-25 6:20:3517 hrs ago1769322035
Ekubo Protocol: ekuPo Token
0.0010811 ETH
Transfer243047352026-01-24 12:44:1135 hrs ago1769258651
Ekubo Protocol: ekuPo Token
0.13025615 ETH
Mint And Deposit...243047352026-01-24 12:44:1135 hrs ago1769258651
Ekubo Protocol: ekuPo Token
0.13025615 ETH
Transfer243005342026-01-23 22:41:112 days ago1769208071
Ekubo Protocol: ekuPo Token
0.04414298 ETH
Mint And Deposit...243005342026-01-23 22:41:112 days ago1769208071
Ekubo Protocol: ekuPo Token
0.04414298 ETH
Transfer242995382026-01-23 19:20:472 days ago1769196047
Ekubo Protocol: ekuPo Token
0.00260164 ETH
Mint And Deposit...242995382026-01-23 19:20:472 days ago1769196047
Ekubo Protocol: ekuPo Token
0.00260164 ETH
Transfer242956932026-01-23 6:27:352 days ago1769149655
Ekubo Protocol: ekuPo Token
0.24580023 ETH
Mint And Deposit...242956932026-01-23 6:27:352 days ago1769149655
Ekubo Protocol: ekuPo Token
0.24580023 ETH
Transfer242854802026-01-21 20:13:354 days ago1769026415
Ekubo Protocol: ekuPo Token
0.40728398 ETH
Mint And Deposit...242854802026-01-21 20:13:354 days ago1769026415
Ekubo Protocol: ekuPo Token
0.40728398 ETH
Transfer242841582026-01-21 15:47:354 days ago1769010455
Ekubo Protocol: ekuPo Token
0.1221054 ETH
Mint And Deposit...242841582026-01-21 15:47:354 days ago1769010455
Ekubo Protocol: ekuPo Token
0.1221054 ETH
Transfer242818512026-01-21 8:05:114 days ago1768982711
Ekubo Protocol: ekuPo Token
0.00196181 ETH
Mint And Deposit...242818512026-01-21 8:05:114 days ago1768982711
Ekubo Protocol: ekuPo Token
0.00196181 ETH
Transfer242788892026-01-20 22:10:235 days ago1768947023
Ekubo Protocol: ekuPo Token
0.00000007 ETH
Mint And Deposit...242788892026-01-20 22:10:235 days ago1768947023
Ekubo Protocol: ekuPo Token
0.00000007 ETH
Transfer242760352026-01-20 12:37:475 days ago1768912667
Ekubo Protocol: ekuPo Token
0.00627892 ETH
Mint And Deposit...242760352026-01-20 12:37:475 days ago1768912667
Ekubo Protocol: ekuPo Token
0.00627892 ETH
Transfer242721592026-01-19 23:38:476 days ago1768865927
Ekubo Protocol: ekuPo Token
0.36454956 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:
Positions

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 9999999 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {ERC721} from "solady/tokens/ERC721.sol";
import {BaseLocker} from "./base/BaseLocker.sol";
import {UsesCore} from "./base/UsesCore.sol";
import {ICore, UpdatePositionParameters} from "./interfaces/ICore.sol";
import {CoreLib} from "./libraries/CoreLib.sol";
import {PoolKey} from "./types/poolKey.sol";
import {PositionKey, Bounds} from "./types/positionKey.sol";
import {FeesPerLiquidity} from "./types/feesPerLiquidity.sol";
import {Position} from "./types/position.sol";
import {tickToSqrtRatio} from "./math/ticks.sol";
import {maxLiquidity, liquidityDeltaToAmountDelta} from "./math/liquidity.sol";
import {PayableMulticallable} from "./base/PayableMulticallable.sol";
import {Permittable} from "./base/Permittable.sol";
import {SlippageChecker} from "./base/SlippageChecker.sol";
import {SqrtRatio} from "./types/sqrtRatio.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {ITokenURIGenerator} from "./interfaces/ITokenURIGenerator.sol";
import {MintableNFT} from "./base/MintableNFT.sol";

/// @title Ekubo Positions
/// @author Moody Salem <[email protected]>
/// @notice Tracks liquidity positions in Ekubo Protocol
contract Positions is UsesCore, PayableMulticallable, SlippageChecker, Permittable, BaseLocker, MintableNFT {
    error DepositFailedDueToSlippage(uint128 liquidity, uint128 minLiquidity);
    error DepositOverflow();

    using CoreLib for ICore;

    constructor(ICore core, ITokenURIGenerator tokenURIGenerator)
        MintableNFT(tokenURIGenerator)
        BaseLocker(core)
        UsesCore(core)
    {}

    function name() public pure override returns (string memory) {
        return "Ekubo Positions";
    }

    function symbol() public pure override returns (string memory) {
        return "ekuPo";
    }

    function getPositionFeesAndLiquidity(uint256 id, PoolKey memory poolKey, Bounds memory bounds)
        external
        view
        returns (uint128 liquidity, uint128 principal0, uint128 principal1, uint128 fees0, uint128 fees1)
    {
        bytes32 poolId = poolKey.toPoolId();
        (SqrtRatio sqrtRatio,,) = core.poolState(poolId);
        bytes32 positionId = PositionKey(bytes32(id), address(this), bounds).toPositionId();
        Position memory position = core.poolPositions(poolId, positionId);

        liquidity = position.liquidity;

        (int128 delta0, int128 delta1) = liquidityDeltaToAmountDelta(
            sqrtRatio,
            -SafeCastLib.toInt128(position.liquidity),
            tickToSqrtRatio(bounds.lower),
            tickToSqrtRatio(bounds.upper)
        );

        (principal0, principal1) = (uint128(-delta0), uint128(-delta1));

        FeesPerLiquidity memory feesPerLiquidityInside = core.getPoolFeesPerLiquidityInside(poolKey, bounds);
        (fees0, fees1) = position.fees(feesPerLiquidityInside);
    }

    function deposit(
        uint256 id,
        PoolKey memory poolKey,
        Bounds memory bounds,
        uint128 maxAmount0,
        uint128 maxAmount1,
        uint128 minLiquidity
    ) public payable authorizedForNft(id) returns (uint128 liquidity, uint128 amount0, uint128 amount1) {
        (SqrtRatio sqrtRatio,,) = core.poolState(poolKey.toPoolId());

        liquidity = maxLiquidity(
            sqrtRatio, tickToSqrtRatio(bounds.lower), tickToSqrtRatio(bounds.upper), maxAmount0, maxAmount1
        );

        if (liquidity < minLiquidity) {
            revert DepositFailedDueToSlippage(liquidity, minLiquidity);
        }

        if (liquidity > uint128(type(int128).max)) {
            revert DepositOverflow();
        }

        (amount0, amount1) =
            abi.decode(lock(abi.encode(bytes1(0xdd), msg.sender, id, poolKey, bounds, liquidity)), (uint128, uint128));
    }

    function collectFees(uint256 id, PoolKey memory poolKey, Bounds memory bounds)
        public
        payable
        authorizedForNft(id)
        returns (uint128 amount0, uint128 amount1)
    {
        (amount0, amount1) = collectFees(id, poolKey, bounds, msg.sender);
    }

    function collectFees(uint256 id, PoolKey memory poolKey, Bounds memory bounds, address recipient)
        public
        payable
        authorizedForNft(id)
        returns (uint128 amount0, uint128 amount1)
    {
        (amount0, amount1) = withdraw(id, poolKey, bounds, 0, recipient, true);
    }

    function withdraw(
        uint256 id,
        PoolKey memory poolKey,
        Bounds memory bounds,
        uint128 liquidity,
        address recipient,
        bool withFees
    ) public payable authorizedForNft(id) returns (uint128 amount0, uint128 amount1) {
        (amount0, amount1) = abi.decode(
            lock(abi.encode(bytes1(0xff), id, poolKey, bounds, liquidity, recipient, withFees)), (uint128, uint128)
        );
    }

    function withdraw(uint256 id, PoolKey memory poolKey, Bounds memory bounds, uint128 liquidity)
        public
        payable
        returns (uint128 amount0, uint128 amount1)
    {
        (amount0, amount1) = withdraw(id, poolKey, bounds, liquidity, address(msg.sender), true);
    }

    function maybeInitializePool(PoolKey memory poolKey, int32 tick)
        external
        payable
        returns (bool initialized, SqrtRatio sqrtRatio)
    {
        // the before update position hook shouldn't be taken into account here
        (sqrtRatio,,) = core.poolState(poolKey.toPoolId());
        if (sqrtRatio.isZero()) {
            initialized = true;
            sqrtRatio = core.initializePool(poolKey, tick);
        }
    }

    function mintAndDeposit(
        PoolKey memory poolKey,
        Bounds memory bounds,
        uint128 maxAmount0,
        uint128 maxAmount1,
        uint128 minLiquidity
    ) external payable returns (uint256 id, uint128 liquidity, uint128 amount0, uint128 amount1) {
        id = mint();
        (liquidity, amount0, amount1) = deposit(id, poolKey, bounds, maxAmount0, maxAmount1, minLiquidity);
    }

    function mintAndDepositWithSalt(
        bytes32 salt,
        PoolKey memory poolKey,
        Bounds memory bounds,
        uint128 maxAmount0,
        uint128 maxAmount1,
        uint128 minLiquidity
    ) external payable returns (uint256 id, uint128 liquidity, uint128 amount0, uint128 amount1) {
        id = mint(salt);
        (liquidity, amount0, amount1) = deposit(id, poolKey, bounds, maxAmount0, maxAmount1, minLiquidity);
    }

    error UnexpectedCallTypeByte(bytes1 b);

    function handleLockData(uint256, bytes memory data) internal override returns (bytes memory result) {
        bytes1 callType = data[0];

        if (callType == 0xdd) {
            (, address caller, uint256 id, PoolKey memory poolKey, Bounds memory bounds, uint128 liquidity) =
                abi.decode(data, (bytes1, address, uint256, PoolKey, Bounds, uint128));

            (int128 delta0, int128 delta1) = core.updatePosition(
                poolKey,
                UpdatePositionParameters({salt: bytes32(id), bounds: bounds, liquidityDelta: int128(liquidity)})
            );

            uint128 amount0 = uint128(delta0);
            uint128 amount1 = uint128(delta1);
            pay(caller, poolKey.token0, amount0);
            pay(caller, poolKey.token1, amount1);

            result = abi.encode(amount0, amount1);
        } else if (callType == 0xff) {
            (
                ,
                uint256 id,
                PoolKey memory poolKey,
                Bounds memory bounds,
                uint128 liquidity,
                address recipient,
                bool withFees
            ) = abi.decode(data, (bytes1, uint256, PoolKey, Bounds, uint128, address, bool));

            uint128 amount0;
            uint128 amount1;

            // collect first in case we are withdrawing the entire amount
            if (withFees) {
                (amount0, amount1) = core.collectFees(poolKey, bytes32(id), bounds);
            }

            if (liquidity != 0) {
                (int128 delta0, int128 delta1) = core.updatePosition(
                    poolKey,
                    UpdatePositionParameters({salt: bytes32(id), bounds: bounds, liquidityDelta: -int128(liquidity)})
                );

                amount0 += uint128(-delta0);
                amount1 += uint128(-delta1);
            }

            withdraw(poolKey.token0, amount0, recipient);
            withdraw(poolKey.token1, amount1, recipient);

            result = abi.encode(amount0, amount1);
        } else {
            revert UnexpectedCallTypeByte(callType);
        }
    }
}

File 2 of 29 : ERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple ERC721 implementation with storage hitchhiking.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol)
///
/// @dev Note:
/// - The ERC721 standard allows for self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - For performance, methods are made payable where permitted by the ERC721 standard.
/// - The `safeTransfer` functions use the identity precompile (0x4)
///   to copy memory internally.
///
/// If you are overriding:
/// - NEVER violate the ERC721 invariant:
///   the balance of an owner MUST always be equal to their number of ownership slots.
///   The transfer functions do not have an underflow guard for user token balances.
/// - Make sure all variables written to storage are properly cleaned
///   (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood).
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC721 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev An account can hold up to 4294967295 tokens.
    uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff;

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

    /// @dev Only the token owner or an approved account can manage the token.
    error NotOwnerNorApproved();

    /// @dev The token does not exist.
    error TokenDoesNotExist();

    /// @dev The token already exists.
    error TokenAlreadyExists();

    /// @dev Cannot query the balance for the zero address.
    error BalanceQueryForZeroAddress();

    /// @dev Cannot mint or transfer to the zero address.
    error TransferToZeroAddress();

    /// @dev The token must be owned by `from`.
    error TransferFromIncorrectOwner();

    /// @dev The recipient's balance has overflowed.
    error AccountBalanceOverflow();

    /// @dev Cannot safely transfer to a contract that does not implement
    /// the ERC721Receiver interface.
    error TransferToNonERC721ReceiverImplementer();

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

    /// @dev Emitted when token `id` is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    /// @dev Emitted when `owner` enables `account` to manage the `id` token.
    event Approval(address indexed owner, address indexed account, uint256 indexed id);

    /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
    event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
    uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
        0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;

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

    /// @dev The ownership data slot of `id` is given by:
    /// ```
    ///     mstore(0x00, id)
    ///     mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
    ///     let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
    /// ```
    /// Bits Layout:
    /// - [0..159]   `addr`
    /// - [160..255] `extraData`
    ///
    /// The approved address slot is given by: `add(1, ownershipSlot)`.
    ///
    /// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip
    ///
    /// The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x1c)
    /// ```
    /// Bits Layout:
    /// - [0..31]   `balance`
    /// - [32..255] `aux`
    ///
    /// The `operator` approval slot of `owner` is given by:
    /// ```
    ///     mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
    ///     mstore(0x00, owner)
    ///     let operatorApprovalSlot := keccak256(0x0c, 0x30)
    /// ```
    uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192;

    /// @dev Pre-shifted and pre-masked constant.
    uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC721 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the token collection name.
    function name() public view virtual returns (string memory);

    /// @dev Returns the token collection symbol.
    function symbol() public view virtual returns (string memory);

    /// @dev Returns the Uniform Resource Identifier (URI) for token `id`.
    function tokenURI(uint256 id) public view virtual returns (string memory);

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

    /// @dev Returns the owner of token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function ownerOf(uint256 id) public view virtual returns (address result) {
        result = _ownerOf(id);
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(result) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns the number of tokens owned by `owner`.
    ///
    /// Requirements:
    /// - `owner` must not be the zero address.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the `owner` is the zero address.
            if iszero(owner) {
                mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`.
                revert(0x1c, 0x04)
            }
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE)
        }
    }

    /// @dev Returns the account approved to manage token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function getApproved(uint256 id) public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            if iszero(shl(96, sload(ownershipSlot))) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            result := sload(add(1, ownershipSlot))
        }
    }

    /// @dev Sets `account` as the approved account to manage token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    /// - The caller must be the owner of the token,
    ///   or an approved operator for the token owner.
    ///
    /// Emits an {Approval} event.
    function approve(address account, uint256 id) public payable virtual {
        _approve(msg.sender, account, id);
    }

    /// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
    function isApprovedForAll(address owner, address operator)
        public
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, operator)
            mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x30))
        }
    }

    /// @dev Sets whether `operator` is approved to manage the tokens of the caller.
    ///
    /// Emits an {ApprovalForAll} event.
    function setApprovalForAll(address operator, bool isApproved) public virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`msg.sender`, `operator`).
            mstore(0x1c, operator)
            mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x30), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            // forgefmt: disable-next-item
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
        }
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 id) public payable virtual {
        _beforeTokenTransfer(from, to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            from := and(bitmaskAddress, from)
            to := and(bitmaskAddress, to)
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller()))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            let owner := and(bitmaskAddress, ownershipPacked)
            // Revert if the token does not exist, or if `from` is not the owner.
            if iszero(mul(owner, eq(owner, from))) {
                // `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`.
                mstore(shl(2, iszero(owner)), 0xceea21b6a1148100)
                revert(0x1c, 0x04)
            }
            // Load, check, and update the token approval.
            {
                mstore(0x00, from)
                let approvedAddress := sload(add(1, ownershipSlot))
                // Revert if the caller is not the owner, nor approved.
                if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Update with the new owner.
            sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
            // Decrement the balance of `from`.
            {
                let fromBalanceSlot := keccak256(0x0c, 0x1c)
                sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
            }
            // Increment the balance of `to`.
            {
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x1c)
                let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
        }
        _afterTokenTransfer(from, to, id);
    }

    /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`.
    function safeTransferFrom(address from, address to, uint256 id) public payable virtual {
        transferFrom(from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function safeTransferFrom(address from, address to, uint256 id, bytes calldata data)
        public
        payable
        virtual
    {
        transferFrom(from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
    /// See: https://eips.ethereum.org/EIPS/eip-165
    /// This function call must use less than 30000 gas.
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let s := shr(224, interfaceId)
            // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f.
            result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL QUERY FUNCTIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns if token `id` exists.
    function _exists(uint256 id) internal view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := iszero(iszero(shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))))
        }
    }

    /// @dev Returns the owner of token `id`.
    /// Returns the zero address instead of reverting if the token does not exist.
    function _ownerOf(uint256 id) internal view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*            INTERNAL DATA HITCHHIKING FUNCTIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance, no events are emitted for the hitchhiking setters.
    // Please emit your own events if required.

    /// @dev Returns the auxiliary data for `owner`.
    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
    /// Auxiliary data can be set for any address, even if it does not have any tokens.
    function _getAux(address owner) internal view virtual returns (uint224 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            result := shr(32, sload(keccak256(0x0c, 0x1c)))
        }
    }

    /// @dev Set the auxiliary data for `owner` to `value`.
    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
    /// Auxiliary data can be set for any address, even if it does not have any tokens.
    function _setAux(address owner, uint224 value) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            let balanceSlot := keccak256(0x0c, 0x1c)
            let packed := sload(balanceSlot)
            sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed)))))
        }
    }

    /// @dev Returns the extra data for token `id`.
    /// Minting, transferring, burning a token will not change the extra data.
    /// The extra data can be set on a non-existent token.
    function _getExtraData(uint256 id) internal view virtual returns (uint96 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Sets the extra data for token `id` to `value`.
    /// Minting, transferring, burning a token will not change the extra data.
    /// The extra data can be set on a non-existent token.
    function _setExtraData(uint256 id, uint96 value) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let packed := sload(ownershipSlot)
            sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed)))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints token `id` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must not exist.
    /// - `to` cannot be the zero address.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 id) internal virtual {
        _beforeTokenTransfer(address(0), to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            to := shr(96, shl(96, to))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            // Revert if the token already exists.
            if shl(96, ownershipPacked) {
                mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`.
                revert(0x1c, 0x04)
            }
            // Update with the owner.
            sstore(ownershipSlot, or(ownershipPacked, to))
            // Increment the balance of the owner.
            {
                mstore(0x00, to)
                let balanceSlot := keccak256(0x0c, 0x1c)
                let balanceSlotPacked := add(sload(balanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(balanceSlot, balanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
        }
        _afterTokenTransfer(address(0), to, id);
    }

    /// @dev Mints token `id` to `to`, and updates the extra data for token `id` to `value`.
    /// Does NOT check if token `id` already exists (assumes `id` is auto-incrementing).
    ///
    /// Requirements:
    ///
    /// - `to` cannot be the zero address.
    ///
    /// Emits a {Transfer} event.
    function _mintAndSetExtraDataUnchecked(address to, uint256 id, uint96 value) internal virtual {
        _beforeTokenTransfer(address(0), to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            to := shr(96, shl(96, to))
            // Update with the owner and extra data.
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            sstore(add(id, add(id, keccak256(0x00, 0x20))), or(shl(160, value), to))
            // Increment the balance of the owner.
            {
                mstore(0x00, to)
                let balanceSlot := keccak256(0x0c, 0x1c)
                let balanceSlotPacked := add(sload(balanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(balanceSlot, balanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
        }
        _afterTokenTransfer(address(0), to, id);
    }

    /// @dev Equivalent to `_safeMint(to, id, "")`.
    function _safeMint(address to, uint256 id) internal virtual {
        _safeMint(to, id, "");
    }

    /// @dev Mints token `id` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must not exist.
    /// - `to` cannot be the zero address.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
        _mint(to, id);
        if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `_burn(address(0), id)`.
    function _burn(uint256 id) internal virtual {
        _burn(address(0), id);
    }

    /// @dev Destroys token `id`, using `by`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function _burn(address by, uint256 id) internal virtual {
        address owner = ownerOf(id);
        _beforeTokenTransfer(owner, address(0), id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            by := shr(96, shl(96, by))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            // Reload the owner in case it is changed in `_beforeTokenTransfer`.
            owner := shr(96, shl(96, ownershipPacked))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // Load and check the token approval.
            {
                mstore(0x00, owner)
                let approvedAddress := sload(add(1, ownershipSlot))
                // If `by` is not the zero address, do the authorization check.
                // Revert if the `by` is not the owner, nor approved.
                if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Clear the owner.
            sstore(ownershipSlot, xor(ownershipPacked, owner))
            // Decrement the balance of `owner`.
            {
                let balanceSlot := keccak256(0x0c, 0x1c)
                sstore(balanceSlot, sub(sload(balanceSlot), 1))
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id)
        }
        _afterTokenTransfer(owner, address(0), id);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL APPROVAL FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `account` is the owner of token `id`, or is approved to manage it.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function _isApprovedOrOwner(address account, uint256 id)
        internal
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            // Clear the upper 96 bits.
            account := shr(96, shl(96, account))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let owner := shr(96, shl(96, sload(ownershipSlot)))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // Check if `account` is the `owner`.
            if iszero(eq(account, owner)) {
                mstore(0x00, owner)
                // Check if `account` is approved to manage the token.
                if iszero(sload(keccak256(0x0c, 0x30))) {
                    result := eq(account, sload(add(1, ownershipSlot)))
                }
            }
        }
    }

    /// @dev Returns the account approved to manage token `id`.
    /// Returns the zero address instead of reverting if the token does not exist.
    function _getApproved(uint256 id) internal view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Equivalent to `_approve(address(0), account, id)`.
    function _approve(address account, uint256 id) internal virtual {
        _approve(address(0), account, id);
    }

    /// @dev Sets `account` as the approved account to manage token `id`, using `by`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    /// - If `by` is not the zero address, `by` must be the owner
    ///   or an approved operator for the token owner.
    ///
    /// Emits a {Approval} event.
    function _approve(address by, address account, uint256 id) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            account := and(bitmaskAddress, account)
            by := and(bitmaskAddress, by)
            // Load the owner of the token.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let owner := and(bitmaskAddress, sload(ownershipSlot))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // If `by` is not the zero address, do the authorization check.
            // Revert if `by` is not the owner, nor approved.
            if iszero(or(iszero(by), eq(by, owner))) {
                mstore(0x00, owner)
                if iszero(sload(keccak256(0x0c, 0x30))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Sets `account` as the approved account to manage `id`.
            sstore(add(1, ownershipSlot), account)
            // Emit the {Approval} event.
            log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id)
        }
    }

    /// @dev Approve or remove the `operator` as an operator for `by`,
    /// without authorization checks.
    ///
    /// Emits an {ApprovalForAll} event.
    function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            by := shr(96, shl(96, by))
            operator := shr(96, shl(96, operator))
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`by`, `operator`).
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
            mstore(0x00, by)
            sstore(keccak256(0x0c, 0x30), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `_transfer(address(0), from, to, id)`.
    function _transfer(address from, address to, uint256 id) internal virtual {
        _transfer(address(0), from, to, id);
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function _transfer(address by, address from, address to, uint256 id) internal virtual {
        _beforeTokenTransfer(from, to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            from := and(bitmaskAddress, from)
            to := and(bitmaskAddress, to)
            by := and(bitmaskAddress, by)
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            let owner := and(bitmaskAddress, ownershipPacked)
            // Revert if the token does not exist, or if `from` is not the owner.
            if iszero(mul(owner, eq(owner, from))) {
                // `TokenDoesNotExist()`, `TransferFromIncorrectOwner()`.
                mstore(shl(2, iszero(owner)), 0xceea21b6a1148100)
                revert(0x1c, 0x04)
            }
            // Load, check, and update the token approval.
            {
                mstore(0x00, from)
                let approvedAddress := sload(add(1, ownershipSlot))
                // If `by` is not the zero address, do the authorization check.
                // Revert if the `by` is not the owner, nor approved.
                if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Update with the new owner.
            sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
            // Decrement the balance of `from`.
            {
                let fromBalanceSlot := keccak256(0x0c, 0x1c)
                sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
            }
            // Increment the balance of `to`.
            {
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x1c)
                let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
                // Revert if `to` is the zero address, or if the account balance overflows.
                if iszero(mul(to, and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE))) {
                    // `TransferToZeroAddress()`, `AccountBalanceOverflow()`.
                    mstore(shl(2, iszero(to)), 0xea553b3401336cea)
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
        }
        _afterTokenTransfer(from, to, id);
    }

    /// @dev Equivalent to `_safeTransfer(from, to, id, "")`.
    function _safeTransfer(address from, address to, uint256 id) internal virtual {
        _safeTransfer(from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeTransfer(address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        _transfer(address(0), from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`.
    function _safeTransfer(address by, address from, address to, uint256 id) internal virtual {
        _safeTransfer(by, from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        _transfer(by, from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    HOOKS FOR OVERRIDING                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any token transfers, including minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {}

    /// @dev Hook that is called after any token transfers, including minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns if `a` has bytecode of non-zero length.
    function _hasCode(address a) private view returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := extcodesize(a) // Can handle dirty upper bits.
        }
    }

    /// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`.
    /// Reverts if the target does not support the function correctly.
    function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data)
        private
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the calldata.
            let m := mload(0x40)
            let onERC721ReceivedSelector := 0x150b7a02
            mstore(m, onERC721ReceivedSelector)
            mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`.
            mstore(add(m, 0x40), shr(96, shl(96, from)))
            mstore(add(m, 0x60), id)
            mstore(add(m, 0x80), 0x80)
            let n := mload(data)
            mstore(add(m, 0xa0), n)
            if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) }
            // Revert if the call reverts.
            if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) {
                if returndatasize() {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(m, 0x00, returndatasize())
                    revert(m, returndatasize())
                }
            }
            // Load the returndata and compare it.
            if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) {
                mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {ILocker, IPayer, IFlashAccountant} from "../interfaces/IFlashAccountant.sol";
import {NATIVE_TOKEN_ADDRESS} from "../math/constants.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";

abstract contract BaseLocker is ILocker, IPayer {
    error BaseLockerAccountantOnly();

    IFlashAccountant internal immutable accountant;

    constructor(IFlashAccountant _accountant) {
        accountant = _accountant;
    }

    /// CALLBACK HANDLERS

    function locked(uint256 id) external {
        if (msg.sender != address(accountant)) revert BaseLockerAccountantOnly();

        bytes memory data = msg.data[36:];

        bytes memory result = handleLockData(id, data);

        assembly ("memory-safe") {
            // raw return whatever the handler sent
            return(add(result, 32), mload(result))
        }
    }

    function payCallback(uint256, address token) external {
        if (msg.sender != address(accountant)) revert BaseLockerAccountantOnly();

        address from;
        uint256 amount;
        assembly ("memory-safe") {
            from := calldataload(68)
            amount := calldataload(100)
        }

        SafeTransferLib.safeTransferFrom2(token, from, address(accountant), amount);
    }

    /// INTERNAL FUNCTIONS

    function lock(bytes memory data) internal returns (bytes memory result) {
        address target = address(accountant);

        assembly ("memory-safe") {
            // We will store result where the free memory pointer is now, ...
            result := mload(0x40)

            // But first use it to store the calldata

            // Selector of lock()
            mstore(result, shl(224, 0xf83d08ba))

            // We only copy the data, not the length, because the length is read from the calldata size
            let len := mload(data)
            mcopy(add(result, 4), add(data, 32), len)

            // If the call failed, pass through the revert
            if iszero(call(gas(), target, 0, result, add(len, 4), 0, 0)) {
                returndatacopy(result, 0, returndatasize())
                revert(result, returndatasize())
            }

            // Copy the entire return data into the space where the result is pointing
            mstore(result, returndatasize())
            returndatacopy(add(result, 32), 0, returndatasize())

            // Update the free memory pointer to be after the end of the data, aligned to the next 32 byte word
            mstore(0x40, and(add(add(result, add(32, returndatasize())), 31), not(31)))
        }
    }

    error ExpectedRevertWithinLock();

    function lockAndExpectRevert(bytes memory data) internal returns (bytes memory result) {
        address target = address(accountant);

        assembly ("memory-safe") {
            // We will store result where the free memory pointer is now, ...
            result := mload(0x40)

            // But first use it to store the calldata

            // Selector of lock()
            mstore(result, shl(224, 0xf83d08ba))

            // We only copy the data, not the length, because the length is read from the calldata size
            let len := mload(data)
            mcopy(add(result, 4), add(data, 32), len)

            // If the call succeeded, revert with ExpectedRevertWithinLock.selector
            if call(gas(), target, 0, result, add(len, 4), 0, 0) {
                mstore(0, shl(224, 0x4c816e2b))
                revert(0, 4)
            }

            // Copy the entire revert data into the space where the result is pointing
            mstore(result, returndatasize())
            returndatacopy(add(result, 32), 0, returndatasize())

            // Update the free memory pointer to be after the end of the data, aligned to the next 32 byte word
            mstore(0x40, and(add(add(result, add(32, returndatasize())), 31), not(31)))
        }
    }

    function pay(address from, address token, uint256 amount) internal {
        if (amount != 0) {
            if (token == NATIVE_TOKEN_ADDRESS) {
                SafeTransferLib.safeTransferETH(address(accountant), amount);
            } else {
                address target = address(accountant);
                assembly ("memory-safe") {
                    let free := mload(0x40)
                    // selector of pay(address)
                    mstore(free, shl(224, 0x0c11dedd))
                    mstore(add(free, 4), token)
                    // additional data is appended to the payCallback
                    mstore(add(free, 36), from)
                    mstore(add(free, 68), amount)

                    // if it failed, pass through revert
                    if iszero(call(gas(), target, 0, free, 100, 0, 0)) {
                        returndatacopy(free, 0, returndatasize())
                        revert(free, returndatasize())
                    }
                }
            }
        }
    }

    function forward(address to, bytes memory data) internal returns (bytes memory result) {
        address target = address(accountant);

        assembly ("memory-safe") {
            // We will store result where the free memory pointer is now, ...
            result := mload(0x40)

            // But first use it to store the calldata

            // Selector of forward(address)
            mstore(result, shl(224, 0x101e8952))
            mstore(add(result, 4), to)

            // We only copy the data, not the length, because the length is read from the calldata size
            let len := mload(data)
            mcopy(add(result, 36), add(data, 32), len)

            // If the call failed, pass through the revert
            if iszero(call(gas(), target, 0, result, add(36, len), 0, 0)) {
                returndatacopy(result, 0, returndatasize())
                revert(result, returndatasize())
            }

            // Copy the entire return data into the space where the result is pointing
            mstore(result, returndatasize())
            returndatacopy(add(result, 32), 0, returndatasize())

            // Update the free memory pointer to be after the end of the data, aligned to the next 32 byte word
            mstore(0x40, and(add(add(result, add(32, returndatasize())), 31), not(31)))
        }
    }

    function withdraw(address token, uint128 amount, address recipient) internal {
        if (amount > 0) {
            accountant.withdraw(token, recipient, amount);
        }
    }

    function handleLockData(uint256 id, bytes memory data) internal virtual returns (bytes memory result);
}

File 4 of 29 : UsesCore.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

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

abstract contract UsesCore {
    error CoreOnly();

    ICore internal immutable core;

    constructor(ICore _core) {
        core = _core;
    }

    modifier onlyCore() {
        if (msg.sender != address(core)) revert CoreOnly();
        _;
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {CallPoints} from "../types/callPoints.sol";
import {PoolKey} from "../types/poolKey.sol";
import {PositionKey, Bounds} from "../types/positionKey.sol";
import {FeesPerLiquidity} from "../types/feesPerLiquidity.sol";
import {IExposedStorage} from "../interfaces/IExposedStorage.sol";
import {IFlashAccountant} from "../interfaces/IFlashAccountant.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";

struct UpdatePositionParameters {
    bytes32 salt;
    Bounds bounds;
    int128 liquidityDelta;
}

interface IExtension {
    function beforeInitializePool(address caller, PoolKey calldata key, int32 tick) external;
    function afterInitializePool(address caller, PoolKey calldata key, int32 tick, SqrtRatio sqrtRatio) external;

    function beforeUpdatePosition(address locker, PoolKey memory poolKey, UpdatePositionParameters memory params)
        external;
    function afterUpdatePosition(
        address locker,
        PoolKey memory poolKey,
        UpdatePositionParameters memory params,
        int128 delta0,
        int128 delta1
    ) external;

    function beforeSwap(
        address locker,
        PoolKey memory poolKey,
        int128 amount,
        bool isToken1,
        SqrtRatio sqrtRatioLimit,
        uint256 skipAhead
    ) external;
    function afterSwap(
        address locker,
        PoolKey memory poolKey,
        int128 amount,
        bool isToken1,
        SqrtRatio sqrtRatioLimit,
        uint256 skipAhead,
        int128 delta0,
        int128 delta1
    ) external;

    function beforeCollectFees(address locker, PoolKey memory poolKey, bytes32 salt, Bounds memory bounds) external;
    function afterCollectFees(
        address locker,
        PoolKey memory poolKey,
        bytes32 salt,
        Bounds memory bounds,
        uint128 amount0,
        uint128 amount1
    ) external;
}

interface ICore is IFlashAccountant, IExposedStorage {
    event ProtocolFeesWithdrawn(address recipient, address token, uint256 amount);
    event ExtensionRegistered(address extension);
    event PoolInitialized(bytes32 poolId, PoolKey poolKey, int32 tick, SqrtRatio sqrtRatio);
    event PositionFeesCollected(bytes32 poolId, PositionKey positionKey, uint128 amount0, uint128 amount1);
    event FeesAccumulated(bytes32 poolId, uint128 amount0, uint128 amount1);
    event PositionUpdated(
        address locker, bytes32 poolId, UpdatePositionParameters params, int128 delta0, int128 delta1
    );

    // This error is thrown by swaps and deposits when this particular deployment of the contract is expired.
    error FailedRegisterInvalidCallPoints();
    error ExtensionAlreadyRegistered();
    error InsufficientSavedBalance();
    error PoolAlreadyInitialized();
    error ExtensionNotRegistered();
    error PoolNotInitialized();
    error MustCollectFeesBeforeWithdrawingAllLiquidity();
    error SqrtRatioLimitOutOfRange();
    error InvalidSqrtRatioLimit();
    error SavedBalanceTokensNotSorted();

    // Allows the owner of the contract to withdraw the protocol withdrawal fees collected
    // To withdraw the native token protocol fees, call with token = NATIVE_TOKEN_ADDRESS
    function withdrawProtocolFees(address recipient, address token, uint256 amount) external;

    // Extensions must call this function to become registered. The call points are validated against the caller address
    function registerExtension(CallPoints memory expectedCallPoints) external;

    // Sets the initial price for a new pool in terms of tick.
    function initializePool(PoolKey memory poolKey, int32 tick) external returns (SqrtRatio sqrtRatio);

    function prevInitializedTick(bytes32 poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
        external
        view
        returns (int32 tick, bool isInitialized);

    function nextInitializedTick(bytes32 poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
        external
        view
        returns (int32 tick, bool isInitialized);

    // Loads 2 tokens from the saved balances of the caller as payment in the current context.
    function load(address token0, address token1, bytes32 salt, uint128 amount0, uint128 amount1) external;

    // Saves an amount of 2 tokens to be used later, in a single slot.
    function save(address owner, address token0, address token1, bytes32 salt, uint128 amount0, uint128 amount1)
        external
        payable;

    // Returns the pool fees per liquidity inside the given bounds.
    function getPoolFeesPerLiquidityInside(PoolKey memory poolKey, Bounds memory bounds)
        external
        view
        returns (FeesPerLiquidity memory);

    // Accumulates tokens to fees of a pool. Only callable by the extension of the specified pool
    // key, i.e. the current locker _must_ be the extension.
    // The extension must call this function within a lock callback.
    function accumulateAsFees(PoolKey memory poolKey, uint128 amount0, uint128 amount1) external payable;

    function updatePosition(PoolKey memory poolKey, UpdatePositionParameters memory params)
        external
        payable
        returns (int128 delta0, int128 delta1);

    function collectFees(PoolKey memory poolKey, bytes32 salt, Bounds memory bounds)
        external
        returns (uint128 amount0, uint128 amount1);

    function swap_611415377(
        PoolKey memory poolKey,
        int128 amount,
        bool isToken1,
        SqrtRatio sqrtRatioLimit,
        uint256 skipAhead
    ) external payable returns (int128 delta0, int128 delta1);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {ICore} from "../interfaces/ICore.sol";
import {ExposedStorageLib} from "./ExposedStorageLib.sol";
import {FeesPerLiquidity} from "../types/feesPerLiquidity.sol";
import {Position} from "../types/position.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";
import {PoolKey} from "../types/poolKey.sol";
import {EfficientHashLib} from "solady/utils/EfficientHashLib.sol";

// Common storage getters we need for external contracts are defined here instead of in the core contract
library CoreLib {
    using ExposedStorageLib for *;

    function isExtensionRegistered(ICore core, address extension) internal view returns (bool registered) {
        bytes32 key;
        assembly ("memory-safe") {
            mstore(0, extension)
            mstore(32, 0)
            key := keccak256(0, 64)
        }

        registered = uint256(core.sload(key)) != 0;
    }

    function protocolFeesCollected(ICore core, address token) internal view returns (uint256 amountCollected) {
        bytes32 key;
        assembly ("memory-safe") {
            mstore(0, token)
            mstore(32, 1)
            key := keccak256(0, 64)
        }

        amountCollected = uint256(core.sload(key));
    }

    function poolState(ICore core, bytes32 poolId)
        internal
        view
        returns (SqrtRatio sqrtRatio, int32 tick, uint128 liquidity)
    {
        bytes32 key;
        assembly ("memory-safe") {
            mstore(0, poolId)
            mstore(32, 2)
            key := keccak256(0, 64)
        }

        bytes32 p = core.sload(key);

        assembly ("memory-safe") {
            sqrtRatio := and(p, 0xffffffffffffffffffffffff)
            tick := and(shr(96, p), 0xffffffff)
            liquidity := shr(128, p)
        }
    }

    function poolPositions(ICore core, bytes32 poolId, bytes32 positionId)
        internal
        view
        returns (Position memory position)
    {
        bytes32 key;
        assembly ("memory-safe") {
            mstore(0, poolId)
            mstore(32, 4)
            let b := keccak256(0, 64)
            mstore(0, positionId)
            mstore(32, b)
            key := keccak256(0, 64)
        }

        (bytes32 v0, bytes32 v1, bytes32 v2) = core.sload(key, bytes32(uint256(key) + 1), bytes32(uint256(key) + 2));

        position.liquidity = uint128(uint256(v0));
        position.feesPerLiquidityInsideLast = FeesPerLiquidity(uint256(v1), uint256(v2));
    }

    function savedBalances(ICore core, address owner, address token, bytes32 salt)
        internal
        view
        returns (uint128 savedBalance)
    {
        bytes32 key = EfficientHashLib.hash(
            bytes32(uint256(uint160(owner))),
            bytes32(uint256(uint160(token))),
            bytes32(uint256(type(uint160).max)),
            salt
        );
        assembly ("memory-safe") {
            mstore(0, key)
            mstore(32, 8)
            key := keccak256(0, 64)
        }

        savedBalance = uint128(uint256(core.sload(key)) >> 128);
    }

    function savedBalances(ICore core, address owner, address token0, address token1, bytes32 salt)
        internal
        view
        returns (uint128 savedBalance0, uint128 savedBalance1)
    {
        bytes32 key = EfficientHashLib.hash(
            bytes32(uint256(uint160(owner))), bytes32(uint256(uint160(token0))), bytes32(uint256(uint160(token1))), salt
        );
        assembly ("memory-safe") {
            mstore(0, key)
            mstore(32, 8)
            key := keccak256(0, 64)
        }

        uint256 value = uint256(core.sload(key));

        savedBalance0 = uint128(value >> 128);
        savedBalance1 = uint128(value);
    }

    function poolTicks(ICore core, bytes32 poolId, int32 tick)
        internal
        view
        returns (int128 liquidityDelta, uint128 liquidityNet)
    {
        bytes32 key;
        assembly ("memory-safe") {
            mstore(0, poolId)
            mstore(32, 5)
            let b := keccak256(0, 64)
            mstore(0, tick)
            mstore(32, b)
            key := keccak256(0, 64)
        }

        bytes32 data = core.sload(key);

        // takes only least significant 128 bits
        liquidityDelta = int128(uint128(uint256(data)));
        // takes only most significant 128 bits
        liquidityNet = uint128(bytes16(data));
    }

    function swap(
        ICore core,
        uint256 value,
        PoolKey memory poolKey,
        int128 amount,
        bool isToken1,
        SqrtRatio sqrtRatioLimit,
        uint256 skipAhead
    ) internal returns (int128 delta0, int128 delta1) {
        (delta0, delta1) = core.swap_611415377{value: value}(poolKey, amount, isToken1, sqrtRatioLimit, skipAhead);
    }

    function save(ICore core, address owner, address token, bytes32 salt, uint128 amount) internal {
        core.save(owner, token, address(type(uint160).max), salt, amount, 0);
    }

    function load(ICore core, address token, bytes32 salt, uint128 amount) internal {
        core.load(token, address(type(uint160).max), salt, amount, 0);
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {MAX_TICK_SPACING, FULL_RANGE_ONLY_TICK_SPACING} from "../math/constants.sol";

using {toPoolId, validatePoolKey, isFullRange, mustLoadFees, tickSpacing, fee, extension} for PoolKey global;

// address (20 bytes) | fee (8 bytes) | tickSpacing (4 bytes)
type Config is bytes32;

function tickSpacing(PoolKey memory pk) pure returns (uint32 r) {
    assembly ("memory-safe") {
        r := and(mload(add(64, pk)), 0xffffffff)
    }
}

function fee(PoolKey memory pk) pure returns (uint64 r) {
    assembly ("memory-safe") {
        r := and(mload(add(60, pk)), 0xffffffffffffffff)
    }
}

function extension(PoolKey memory pk) pure returns (address r) {
    assembly ("memory-safe") {
        r := and(mload(add(52, pk)), 0xffffffffffffffffffffffffffffffffffffffff)
    }
}

function mustLoadFees(PoolKey memory pk) pure returns (bool r) {
    assembly ("memory-safe") {
        // only if either of tick spacing and fee are nonzero
        // if _both_ are zero, then we know we do not need to load fees for swaps
        r := iszero(iszero(and(mload(add(64, pk)), 0xffffffffffffffffffffffff)))
    }
}

function isFullRange(PoolKey memory pk) pure returns (bool r) {
    r = pk.tickSpacing() == FULL_RANGE_ONLY_TICK_SPACING;
}

function toConfig(uint64 _fee, uint32 _tickSpacing, address _extension) pure returns (Config c) {
    assembly ("memory-safe") {
        c := add(add(shl(96, _extension), shl(32, _fee)), _tickSpacing)
    }
}

// Each pool has its own state associated with this key
struct PoolKey {
    address token0;
    address token1;
    Config config;
}

error TokensMustBeSorted();
error InvalidTickSpacing();

function validatePoolKey(PoolKey memory key) pure {
    if (key.token0 >= key.token1) revert TokensMustBeSorted();
    if (key.tickSpacing() > MAX_TICK_SPACING) {
        revert InvalidTickSpacing();
    }
}

function toPoolId(PoolKey memory key) pure returns (bytes32 result) {
    assembly ("memory-safe") {
        // it's already copied into memory
        result := keccak256(key, 96)
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {MIN_TICK, MAX_TICK, FULL_RANGE_ONLY_TICK_SPACING} from "../math/constants.sol";

using {toPositionId} for PositionKey global;
using {validateBounds} for Bounds global;

// Bounds are lower and upper prices for which a position is active
struct Bounds {
    int32 lower;
    int32 upper;
}

error BoundsOrder();
error MinMaxBounds();
error BoundsTickSpacing();
error FullRangeOnlyPool();

function validateBounds(Bounds memory bounds, uint32 tickSpacing) pure {
    if (tickSpacing == FULL_RANGE_ONLY_TICK_SPACING) {
        if (bounds.lower != MIN_TICK || bounds.upper != MAX_TICK) revert FullRangeOnlyPool();
    } else {
        if (bounds.lower >= bounds.upper) revert BoundsOrder();
        if (bounds.lower < MIN_TICK || bounds.upper > MAX_TICK) revert MinMaxBounds();
        int32 spacing = int32(tickSpacing);
        if (bounds.lower % spacing != 0 || bounds.upper % spacing != 0) revert BoundsTickSpacing();
    }
}

// A position is keyed by the pool and this position key
struct PositionKey {
    bytes32 salt;
    address owner;
    Bounds bounds;
}

function toPositionId(PositionKey memory key) pure returns (bytes32 result) {
    assembly ("memory-safe") {
        // salt and owner
        mstore(0, keccak256(key, 64))
        // bounds
        mstore(32, keccak256(mload(add(key, 64)), 64))

        result := keccak256(0, 64)
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

// The total fees per liquidity for each token.
// Since these are always read together we put them in a struct, even though they cannot be packed.
struct FeesPerLiquidity {
    uint256 value0;
    uint256 value1;
}

using {sub} for FeesPerLiquidity global;

function sub(FeesPerLiquidity memory a, FeesPerLiquidity memory b) pure returns (FeesPerLiquidity memory result) {
    assembly ("memory-safe") {
        mstore(result, sub(mload(a), mload(b)))
        mstore(add(result, 32), sub(mload(add(a, 32)), mload(add(b, 32))))
    }
}

function feesPerLiquidityFromAmounts(uint128 amount0, uint128 amount1, uint128 liquidity)
    pure
    returns (FeesPerLiquidity memory result)
{
    assembly ("memory-safe") {
        mstore(result, div(shl(128, amount0), liquidity))
        mstore(add(result, 32), div(shl(128, amount1), liquidity))
    }
}

File 10 of 29 : position.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {FeesPerLiquidity} from "./feesPerLiquidity.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";

struct Position {
    uint128 liquidity;
    FeesPerLiquidity feesPerLiquidityInsideLast;
}

using {fees} for Position global;

/// @dev Returns the fee amounts of token0 and token1 owed to a position based on the given fees per liquidity inside snapshot
///      Note if the computed fees overflows the uint128 type, it will return only the lower 128 bits. It is assumed that accumulated
///      fees will never exceed type(uint128).max.
function fees(Position memory position, FeesPerLiquidity memory feesPerLiquidityInside)
    pure
    returns (uint128, uint128)
{
    FeesPerLiquidity memory difference = feesPerLiquidityInside.sub(position.feesPerLiquidityInsideLast);

    return (
        uint128(FixedPointMathLib.fullMulDivN(difference.value0, position.liquidity, 128)),
        uint128(FixedPointMathLib.fullMulDivN(difference.value1, position.liquidity, 128))
    );
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {MAX_TICK_SPACING, MAX_TICK_MAGNITUDE} from "./constants.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SqrtRatio, toSqrtRatio} from "../types/sqrtRatio.sol";

error InvalidTick(int32 tick);

// Returns the sqrtRatio corresponding for the tick
function tickToSqrtRatio(int32 tick) pure returns (SqrtRatio r) {
    unchecked {
        uint256 t = FixedPointMathLib.abs(tick);
        if (t > MAX_TICK_MAGNITUDE) revert InvalidTick(tick);

        uint256 ratio;
        assembly ("memory-safe") {
            ratio := sub(0x100000000000000000000000000000000, mul(and(t, 0x1), 0x8637b66cd638344daef276cd7c5))
        }

        if ((t & 0x2) != 0) {
            ratio = (ratio * 0xffffef390978c398134b4ff3764fe410) >> 128;
        }
        if ((t & 0x4) != 0) {
            ratio = (ratio * 0xffffde72140b00a354bd3dc828e976c9) >> 128;
        }
        if ((t & 0x8) != 0) {
            ratio = (ratio * 0xffffbce42c7be6c998ad6318193c0b18) >> 128;
        }
        if ((t & 0x10) != 0) {
            ratio = (ratio * 0xffff79c86a8f6150a32d9778eceef97c) >> 128;
        }
        if ((t & 0x20) != 0) {
            ratio = (ratio * 0xfffef3911b7cff24ba1b3dbb5f8f5974) >> 128;
        }
        if ((t & 0x40) != 0) {
            ratio = (ratio * 0xfffde72350725cc4ea8feece3b5f13c8) >> 128;
        }
        if ((t & 0x80) != 0) {
            ratio = (ratio * 0xfffbce4b06c196e9247ac87695d53c60) >> 128;
        }
        if ((t & 0x100) != 0) {
            ratio = (ratio * 0xfff79ca7a4d1bf1ee8556cea23cdbaa5) >> 128;
        }
        if ((t & 0x200) != 0) {
            ratio = (ratio * 0xffef3995a5b6a6267530f207142a5764) >> 128;
        }
        if ((t & 0x400) != 0) {
            ratio = (ratio * 0xffde7444b28145508125d10077ba83b8) >> 128;
        }
        if ((t & 0x800) != 0) {
            ratio = (ratio * 0xffbceceeb791747f10df216f2e53ec57) >> 128;
        }
        if ((t & 0x1000) != 0) {
            ratio = (ratio * 0xff79eb706b9a64c6431d76e63531e929) >> 128;
        }
        if ((t & 0x2000) != 0) {
            ratio = (ratio * 0xfef41d1a5f2ae3a20676bec6f7f9459a) >> 128;
        }
        if ((t & 0x4000) != 0) {
            ratio = (ratio * 0xfde95287d26d81bea159c37073122c73) >> 128;
        }
        if ((t & 0x8000) != 0) {
            ratio = (ratio * 0xfbd701c7cbc4c8a6bb81efd232d1e4e7) >> 128;
        }
        if ((t & 0x10000) != 0) {
            ratio = (ratio * 0xf7bf5211c72f5185f372aeb1d48f937e) >> 128;
        }
        if ((t & 0x20000) != 0) {
            ratio = (ratio * 0xefc2bf59df33ecc28125cf78ec4f167f) >> 128;
        }
        if ((t & 0x40000) != 0) {
            ratio = (ratio * 0xe08d35706200796273f0b3a981d90cfd) >> 128;
        }
        if ((t & 0x80000) != 0) {
            ratio = (ratio * 0xc4f76b68947482dc198a48a54348c4ed) >> 128;
        }
        if ((t & 0x100000) != 0) {
            ratio = (ratio * 0x978bcb9894317807e5fa4498eee7c0fa) >> 128;
        }
        if ((t & 0x200000) != 0) {
            ratio = (ratio * 0x59b63684b86e9f486ec54727371ba6ca) >> 128;
        }
        if ((t & 0x400000) != 0) {
            ratio = (ratio * 0x1f703399d88f6aa83a28b22d4a1f56e3) >> 128;
        }
        if ((t & 0x800000) != 0) {
            ratio = (ratio * 0x3dc5dac7376e20fc8679758d1bcdcfc) >> 128;
        }
        if ((t & 0x1000000) != 0) {
            ratio = (ratio * 0xee7e32d61fdb0a5e622b820f681d0) >> 128;
        }
        if ((t & 0x2000000) != 0) {
            ratio = (ratio * 0xde2ee4bc381afa7089aa84bb66) >> 128;
        }
        if ((t & 0x4000000) != 0) {
            ratio = (ratio * 0xc0d55d4d7152c25fb139) >> 128;
        }

        if (tick > 0) {
            ratio = type(uint256).max / ratio;
        }

        r = toSqrtRatio(ratio, false);
    }
}

function sqrtRatioToTick(SqrtRatio sqrtRatio) pure returns (int32) {
    unchecked {
        uint256 sqrtRatioFixed = sqrtRatio.toFixed();

        bool negative = (sqrtRatioFixed >> 128) == 0;

        uint256 x = negative ? (type(uint256).max / sqrtRatioFixed) : sqrtRatioFixed;

        // we know x >> 128 is never zero because we check bounds above and then reciprocate sqrtRatio if the high 128 bits are zero
        // so we don't need to handle the exceptional case of log2(0)
        uint256 msbHigh = FixedPointMathLib.log2(x >> 128);
        x = x >> (msbHigh + 1);
        uint256 log2_unsigned = msbHigh * 0x10000000000000000;

        assembly ("memory-safe") {
            // 63
            x := shr(127, mul(x, x))
            let is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x8000000000000000))
            x := shr(is_high_nonzero, x)

            // 62
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x4000000000000000))
            x := shr(is_high_nonzero, x)

            // 61
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x2000000000000000))
            x := shr(is_high_nonzero, x)

            // 60
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x1000000000000000))
            x := shr(is_high_nonzero, x)

            // 59
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x800000000000000))
            x := shr(is_high_nonzero, x)

            // 58
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x400000000000000))
            x := shr(is_high_nonzero, x)

            // 57
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x200000000000000))
            x := shr(is_high_nonzero, x)

            // 56
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x100000000000000))
            x := shr(is_high_nonzero, x)

            // 55
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x80000000000000))
            x := shr(is_high_nonzero, x)

            // 54
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x40000000000000))
            x := shr(is_high_nonzero, x)

            // 53
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x20000000000000))
            x := shr(is_high_nonzero, x)

            // 52
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x10000000000000))
            x := shr(is_high_nonzero, x)

            // 51
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x8000000000000))
            x := shr(is_high_nonzero, x)

            // 50
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x4000000000000))
            x := shr(is_high_nonzero, x)

            // 49
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x2000000000000))
            x := shr(is_high_nonzero, x)

            // 48
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x1000000000000))
            x := shr(is_high_nonzero, x)

            // 47
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x800000000000))
            x := shr(is_high_nonzero, x)

            // 46
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x400000000000))
            x := shr(is_high_nonzero, x)

            // 45
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x200000000000))
            x := shr(is_high_nonzero, x)

            // 44
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x100000000000))
            x := shr(is_high_nonzero, x)

            // 43
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x80000000000))
            x := shr(is_high_nonzero, x)

            // 42
            x := shr(127, mul(x, x))
            is_high_nonzero := eq(iszero(shr(128, x)), 0)
            log2_unsigned := add(log2_unsigned, mul(is_high_nonzero, 0x40000000000))
        }

        // 25572630076711825471857579 == 2**64/(log base 2 of sqrt tick size)
        // https://www.wolframalpha.com/input?i=floor%28%281%2F+log+base+2+of+%28sqrt%281.000001%29%29%29*2**64%29
        int256 logBaseTickSizeX128 =
            (negative ? -int256(log2_unsigned) : int256(log2_unsigned)) * 25572630076711825471857579;

        int32 tickLow;
        int32 tickHigh;

        if (negative) {
            tickLow = int32((logBaseTickSizeX128 - 112469616488610087266845472033458199637) >> 128);
            tickHigh = int32((logBaseTickSizeX128) >> 128);
        } else {
            tickLow = int32((logBaseTickSizeX128) >> 128);
            tickHigh = int32((logBaseTickSizeX128 + 112469616488610087266845472033458199637) >> 128);
        }

        if (tickLow == tickHigh) {
            return tickLow;
        }

        if (tickToSqrtRatio(tickHigh) <= sqrtRatio) return tickHigh;

        return tickLow;
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {amount0Delta, amount1Delta, sortAndConvertToFixedSqrtRatios} from "./delta.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";

/**
 * @notice Returns the token0 and token1 delta owed for a given change in liquidity.
 * @param sqrtRatio        Current price (as a sqrt ratio).
 * @param liquidityDelta   Signed liquidity change; positive = added, negative = removed.
 * @param sqrtRatioLower   The lower bound of the price range (as a sqrt ratio).
 * @param sqrtRatioUpper   The upper bound of the price range (as a sqrt ratio).
 */
function liquidityDeltaToAmountDelta(
    SqrtRatio sqrtRatio,
    int128 liquidityDelta,
    SqrtRatio sqrtRatioLower,
    SqrtRatio sqrtRatioUpper
) pure returns (int128 delta0, int128 delta1) {
    unchecked {
        if (liquidityDelta == 0) {
            return (0, 0);
        }
        bool isPositive = (liquidityDelta > 0);
        // type(uint256).max cast to int256 is -1
        int256 sign = int256(FixedPointMathLib.ternary(isPositive, 1, type(uint256).max));
        // absolute value of a int128 always fits in a uint128
        uint128 magnitude = uint128(FixedPointMathLib.abs(liquidityDelta));

        if (sqrtRatio <= sqrtRatioLower) {
            delta0 = SafeCastLib.toInt128(
                sign * int256(uint256(amount0Delta(sqrtRatioLower, sqrtRatioUpper, magnitude, isPositive)))
            );
        } else if (sqrtRatio < sqrtRatioUpper) {
            delta0 = SafeCastLib.toInt128(
                sign * int256(uint256(amount0Delta(sqrtRatio, sqrtRatioUpper, magnitude, isPositive)))
            );
            delta1 = SafeCastLib.toInt128(
                sign * int256(uint256(amount1Delta(sqrtRatioLower, sqrtRatio, magnitude, isPositive)))
            );
        } else {
            delta1 = SafeCastLib.toInt128(
                sign * int256(uint256(amount1Delta(sqrtRatioLower, sqrtRatioUpper, magnitude, isPositive)))
            );
        }
    }
}

function maxLiquidityForToken0(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 amount) pure returns (uint256) {
    unchecked {
        uint256 numerator_1 = FixedPointMathLib.fullMulDivN(sqrtRatioLower, sqrtRatioUpper, 128);

        return FixedPointMathLib.fullMulDiv(amount, numerator_1, (sqrtRatioUpper - sqrtRatioLower));
    }
}

function maxLiquidityForToken1(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 amount) pure returns (uint256) {
    unchecked {
        return (uint256(amount) << 128) / (sqrtRatioUpper - sqrtRatioLower);
    }
}

function maxLiquidity(
    SqrtRatio _sqrtRatio,
    SqrtRatio sqrtRatioA,
    SqrtRatio sqrtRatioB,
    uint128 amount0,
    uint128 amount1
) pure returns (uint128) {
    uint256 sqrtRatio = _sqrtRatio.toFixed();
    (uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);

    if (sqrtRatio <= sqrtRatioLower) {
        return uint128(
            FixedPointMathLib.min(type(uint128).max, maxLiquidityForToken0(sqrtRatioLower, sqrtRatioUpper, amount0))
        );
    } else if (sqrtRatio < sqrtRatioUpper) {
        return uint128(
            FixedPointMathLib.min(
                type(uint128).max,
                FixedPointMathLib.min(
                    maxLiquidityForToken0(sqrtRatio, sqrtRatioUpper, amount0),
                    maxLiquidityForToken1(sqrtRatioLower, sqrtRatio, amount1)
                )
            )
        );
    } else {
        return uint128(
            FixedPointMathLib.min(type(uint128).max, maxLiquidityForToken1(sqrtRatioLower, sqrtRatioUpper, amount1))
        );
    }
}

error LiquidityDeltaOverflow();

function addLiquidityDelta(uint128 liquidity, int128 liquidityDelta) pure returns (uint128 result) {
    assembly ("memory-safe") {
        result := add(liquidity, liquidityDelta)
        if and(result, shl(128, 0xffffffffffffffffffffffffffffffff)) {
            mstore(0, shl(224, 0x6d862c50))
            revert(0, 4)
        }
    }
}

function subLiquidityDelta(uint128 liquidity, int128 liquidityDelta) pure returns (uint128 result) {
    assembly ("memory-safe") {
        result := sub(liquidity, liquidityDelta)
        if and(result, shl(128, 0xffffffffffffffffffffffffffffffff)) {
            mstore(0, shl(224, 0x6d862c50))
            revert(0, 4)
        }
    }
}

File 13 of 29 : PayableMulticallable.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {Multicallable} from "solady/utils/Multicallable.sol";

abstract contract PayableMulticallable is Multicallable {
    function multicall(bytes[] calldata data) public payable override returns (bytes[] memory) {
        _multicallDirectReturn(_multicall(data));
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";

// Contains a single method which allows a user to approve this contract via permit2
// Combining with Multicallable is highly recommended, so that the permit signature can be used to spend tokens in a single transaction
// Note this only allows the msg.sender to execute a permit. Our contracts are not intended for use with some types of contract based account abstraction.
contract Permittable {
    // Method is payable in case it is paired with other payable Multicallable calls
    function permit(address token, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable {
        SafeTransferLib.permit2(token, msg.sender, address(this), amount, deadline, v, r, s);
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {NATIVE_TOKEN_ADDRESS} from "../math/constants.sol";

// Has methods that are multicallable for checking deadlines and balance changes
// Only useful in multicallable context, because these methods are expected to be called as part of another transaction that manipulates balances
// All methods are payable in case they are paired with other payable Multicallable calls
abstract contract SlippageChecker {
    error TransactionExpired(uint256 deadline);
    error MinimumOutputNotReceived(address token, uint256 minimumOutput);
    error MaximumInputExceeded(address token, uint256 maximumInput);

    // cast keccak "SlippageChecker#balanceKey"
    uint256 private constant _BALANCE_KEY_OFFSET = 0x2ea13d3f0340a613d1765d6e239004eca4cb7efa2e253d1e113c4d333b8db7c8;

    function balanceKey(address token, address account) private pure returns (bytes32 key) {
        assembly ("memory-safe") {
            mstore(0, token)
            mstore(32, account)
            key := add(keccak256(0, 64), _BALANCE_KEY_OFFSET)
        }
    }

    function getRecordedBalance(address token, address account) private view returns (uint256 prev) {
        bytes32 key = balanceKey(token, account);
        assembly ("memory-safe") {
            prev := tload(key)
        }
    }

    function getBalance(address token, address account) private view returns (uint256 balance) {
        if (token == NATIVE_TOKEN_ADDRESS) {
            balance = account.balance;
        } else {
            balance = SafeTransferLib.balanceOf(token, account);
        }
    }

    function recordBalanceForSlippageCheck(address token) external payable {
        bytes32 key = balanceKey(token, msg.sender);
        uint256 bal = getBalance(token, msg.sender);
        assembly ("memory-safe") {
            tstore(key, bal)
        }
    }

    function checkDeadline(uint256 deadline) external payable {
        if (block.timestamp > deadline) revert TransactionExpired(deadline);
    }

    function checkMinimumOutputReceived(address token, uint256 minimumOutput) external payable {
        uint256 prev = getRecordedBalance(token, msg.sender);
        uint256 bal = getBalance(token, msg.sender);
        unchecked {
            if (bal < prev || (bal - prev) < minimumOutput) {
                revert MinimumOutputNotReceived(token, minimumOutput);
            }
        }
    }

    function checkMaximumInputNotExceeded(address token, uint256 maximumInput) external payable {
        uint256 prev = getRecordedBalance(token, msg.sender);
        uint256 bal = getBalance(token, msg.sender);
        unchecked {
            if (bal < prev && (prev - bal) > maximumInput) {
                revert MaximumInputExceeded(token, maximumInput);
            }
        }
    }

    // Allows a caller to refund any ETH sent to this contract for purpose of transient payments
    function refundNativeToken() external payable {
        if (address(this).balance > 0) {
            SafeTransferLib.safeTransferETH(msg.sender, address(this).balance);
        }
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

// A dynamic fixed point number (a la floating point) that stores a shifting 94 bit view of the underlying fixed point value,
//  based on the most significant bits (mantissa)
// If the most significant 2 bits are 11, it represents a 64.30
// If the most significant 2 bits are 10, it represents a 32.62 number
// If the most significant 2 bits are 01, it represents a 0.94 number
// If the most significant 2 bits are 00, it represents a 0.126 number that is always less than 2**-32

type SqrtRatio is uint96;

uint96 constant MIN_SQRT_RATIO_RAW = 4611797791050542631;
SqrtRatio constant MIN_SQRT_RATIO = SqrtRatio.wrap(MIN_SQRT_RATIO_RAW);
uint96 constant MAX_SQRT_RATIO_RAW = 79227682466138141934206691491;
SqrtRatio constant MAX_SQRT_RATIO = SqrtRatio.wrap(MAX_SQRT_RATIO_RAW);

uint96 constant TWO_POW_95 = 0x800000000000000000000000;
uint96 constant TWO_POW_94 = 0x400000000000000000000000;
uint96 constant TWO_POW_62 = 0x4000000000000000;
uint96 constant TWO_POW_62_MINUS_ONE = 0x3fffffffffffffff;
uint96 constant BIT_MASK = 0xc00000000000000000000000; // TWO_POW_95 | TWO_POW_94

SqrtRatio constant ONE = SqrtRatio.wrap((TWO_POW_95) + (1 << 62));

using {
    toFixed,
    isValid,
    ge as >=,
    le as <=,
    lt as <,
    gt as >,
    eq as ==,
    neq as !=,
    isZero,
    min,
    max
} for SqrtRatio global;

function isValid(SqrtRatio sqrtRatio) pure returns (bool r) {
    assembly ("memory-safe") {
        r :=
            and(
                // greater than or equal to TWO_POW_62, i.e. the whole number portion is nonzero
                gt(and(sqrtRatio, not(BIT_MASK)), TWO_POW_62_MINUS_ONE),
                // and between min/max sqrt ratio
                and(iszero(lt(sqrtRatio, MIN_SQRT_RATIO_RAW)), iszero(gt(sqrtRatio, MAX_SQRT_RATIO_RAW)))
            )
    }
}

error ValueOverflowsSqrtRatioContainer();

// If passing a value greater than this constant with roundUp = true, toSqrtRatio will overflow
// For roundUp = false, the constant is type(uint192).max
uint256 constant MAX_FIXED_VALUE_ROUND_UP =
    0x1000000000000000000000000000000000000000000000000 - 0x4000000000000000000000000;

// Converts a 64.128 value into the compact SqrtRatio representation
function toSqrtRatio(uint256 sqrtRatio, bool roundUp) pure returns (SqrtRatio r) {
    assembly ("memory-safe") {
        let addend := mul(roundUp, 0x3)

        // lt 2**96 after rounding up
        switch lt(sqrtRatio, sub(0x1000000000000000000000000, addend))
        case 1 { r := shr(2, add(sqrtRatio, addend)) }
        default {
            // 2**34 - 1
            addend := mul(roundUp, 0x3ffffffff)
            // lt 2**128 after rounding up
            switch lt(sqrtRatio, sub(0x100000000000000000000000000000000, addend))
            case 1 { r := or(TWO_POW_94, shr(34, add(sqrtRatio, addend))) }
            default {
                addend := mul(roundUp, 0x3ffffffffffffffff)
                // lt 2**160 after rounding up
                switch lt(sqrtRatio, sub(0x10000000000000000000000000000000000000000, addend))
                case 1 { r := or(TWO_POW_95, shr(66, add(sqrtRatio, addend))) }
                default {
                    // 2**98 - 1
                    addend := mul(roundUp, 0x3ffffffffffffffffffffffff)
                    switch lt(sqrtRatio, sub(0x1000000000000000000000000000000000000000000000000, addend))
                    case 1 { r := or(BIT_MASK, shr(98, add(sqrtRatio, addend))) }
                    default {
                        // cast sig "ValueOverflowsSqrtRatioContainer()"
                        mstore(0, shl(224, 0xa10459f4))
                        revert(0, 4)
                    }
                }
            }
        }
    }
}

// Returns the 64.128 representation of the given sqrt ratio
function toFixed(SqrtRatio sqrtRatio) pure returns (uint256 r) {
    assembly ("memory-safe") {
        r := shl(add(2, shr(89, and(sqrtRatio, BIT_MASK))), and(sqrtRatio, not(BIT_MASK)))
    }
}

// The below operators assume that the SqrtRatio is valid, i.e. SqrtRatio#isValid returns true

function lt(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) < SqrtRatio.unwrap(b);
}

function gt(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) > SqrtRatio.unwrap(b);
}

function le(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) <= SqrtRatio.unwrap(b);
}

function ge(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) >= SqrtRatio.unwrap(b);
}

function eq(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) == SqrtRatio.unwrap(b);
}

function neq(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) != SqrtRatio.unwrap(b);
}

function isZero(SqrtRatio a) pure returns (bool r) {
    assembly ("memory-safe") {
        r := iszero(a)
    }
}

function max(SqrtRatio a, SqrtRatio b) pure returns (SqrtRatio r) {
    assembly ("memory-safe") {
        r := xor(a, mul(xor(a, b), gt(b, a)))
    }
}

function min(SqrtRatio a, SqrtRatio b) pure returns (SqrtRatio r) {
    assembly ("memory-safe") {
        r := xor(a, mul(xor(a, b), lt(b, a)))
    }
}

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

/// @notice Safe integer casting library that reverts on overflow.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
/// @dev Optimized for runtime gas for very high number of optimizer runs (i.e. >= 1000000).
library SafeCastLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unable to cast to the target type due to overflow.
    error Overflow();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*          UNSIGNED INTEGER SAFE CASTING OPERATIONS          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Casts `x` to a uint8. Reverts on overflow.
    function toUint8(uint256 x) internal pure returns (uint8) {
        if (x >= 1 << 8) _revertOverflow();
        return uint8(x);
    }

    /// @dev Casts `x` to a uint16. Reverts on overflow.
    function toUint16(uint256 x) internal pure returns (uint16) {
        if (x >= 1 << 16) _revertOverflow();
        return uint16(x);
    }

    /// @dev Casts `x` to a uint24. Reverts on overflow.
    function toUint24(uint256 x) internal pure returns (uint24) {
        if (x >= 1 << 24) _revertOverflow();
        return uint24(x);
    }

    /// @dev Casts `x` to a uint32. Reverts on overflow.
    function toUint32(uint256 x) internal pure returns (uint32) {
        if (x >= 1 << 32) _revertOverflow();
        return uint32(x);
    }

    /// @dev Casts `x` to a uint40. Reverts on overflow.
    function toUint40(uint256 x) internal pure returns (uint40) {
        if (x >= 1 << 40) _revertOverflow();
        return uint40(x);
    }

    /// @dev Casts `x` to a uint48. Reverts on overflow.
    function toUint48(uint256 x) internal pure returns (uint48) {
        if (x >= 1 << 48) _revertOverflow();
        return uint48(x);
    }

    /// @dev Casts `x` to a uint56. Reverts on overflow.
    function toUint56(uint256 x) internal pure returns (uint56) {
        if (x >= 1 << 56) _revertOverflow();
        return uint56(x);
    }

    /// @dev Casts `x` to a uint64. Reverts on overflow.
    function toUint64(uint256 x) internal pure returns (uint64) {
        if (x >= 1 << 64) _revertOverflow();
        return uint64(x);
    }

    /// @dev Casts `x` to a uint72. Reverts on overflow.
    function toUint72(uint256 x) internal pure returns (uint72) {
        if (x >= 1 << 72) _revertOverflow();
        return uint72(x);
    }

    /// @dev Casts `x` to a uint80. Reverts on overflow.
    function toUint80(uint256 x) internal pure returns (uint80) {
        if (x >= 1 << 80) _revertOverflow();
        return uint80(x);
    }

    /// @dev Casts `x` to a uint88. Reverts on overflow.
    function toUint88(uint256 x) internal pure returns (uint88) {
        if (x >= 1 << 88) _revertOverflow();
        return uint88(x);
    }

    /// @dev Casts `x` to a uint96. Reverts on overflow.
    function toUint96(uint256 x) internal pure returns (uint96) {
        if (x >= 1 << 96) _revertOverflow();
        return uint96(x);
    }

    /// @dev Casts `x` to a uint104. Reverts on overflow.
    function toUint104(uint256 x) internal pure returns (uint104) {
        if (x >= 1 << 104) _revertOverflow();
        return uint104(x);
    }

    /// @dev Casts `x` to a uint112. Reverts on overflow.
    function toUint112(uint256 x) internal pure returns (uint112) {
        if (x >= 1 << 112) _revertOverflow();
        return uint112(x);
    }

    /// @dev Casts `x` to a uint120. Reverts on overflow.
    function toUint120(uint256 x) internal pure returns (uint120) {
        if (x >= 1 << 120) _revertOverflow();
        return uint120(x);
    }

    /// @dev Casts `x` to a uint128. Reverts on overflow.
    function toUint128(uint256 x) internal pure returns (uint128) {
        if (x >= 1 << 128) _revertOverflow();
        return uint128(x);
    }

    /// @dev Casts `x` to a uint136. Reverts on overflow.
    function toUint136(uint256 x) internal pure returns (uint136) {
        if (x >= 1 << 136) _revertOverflow();
        return uint136(x);
    }

    /// @dev Casts `x` to a uint144. Reverts on overflow.
    function toUint144(uint256 x) internal pure returns (uint144) {
        if (x >= 1 << 144) _revertOverflow();
        return uint144(x);
    }

    /// @dev Casts `x` to a uint152. Reverts on overflow.
    function toUint152(uint256 x) internal pure returns (uint152) {
        if (x >= 1 << 152) _revertOverflow();
        return uint152(x);
    }

    /// @dev Casts `x` to a uint160. Reverts on overflow.
    function toUint160(uint256 x) internal pure returns (uint160) {
        if (x >= 1 << 160) _revertOverflow();
        return uint160(x);
    }

    /// @dev Casts `x` to a uint168. Reverts on overflow.
    function toUint168(uint256 x) internal pure returns (uint168) {
        if (x >= 1 << 168) _revertOverflow();
        return uint168(x);
    }

    /// @dev Casts `x` to a uint176. Reverts on overflow.
    function toUint176(uint256 x) internal pure returns (uint176) {
        if (x >= 1 << 176) _revertOverflow();
        return uint176(x);
    }

    /// @dev Casts `x` to a uint184. Reverts on overflow.
    function toUint184(uint256 x) internal pure returns (uint184) {
        if (x >= 1 << 184) _revertOverflow();
        return uint184(x);
    }

    /// @dev Casts `x` to a uint192. Reverts on overflow.
    function toUint192(uint256 x) internal pure returns (uint192) {
        if (x >= 1 << 192) _revertOverflow();
        return uint192(x);
    }

    /// @dev Casts `x` to a uint200. Reverts on overflow.
    function toUint200(uint256 x) internal pure returns (uint200) {
        if (x >= 1 << 200) _revertOverflow();
        return uint200(x);
    }

    /// @dev Casts `x` to a uint208. Reverts on overflow.
    function toUint208(uint256 x) internal pure returns (uint208) {
        if (x >= 1 << 208) _revertOverflow();
        return uint208(x);
    }

    /// @dev Casts `x` to a uint216. Reverts on overflow.
    function toUint216(uint256 x) internal pure returns (uint216) {
        if (x >= 1 << 216) _revertOverflow();
        return uint216(x);
    }

    /// @dev Casts `x` to a uint224. Reverts on overflow.
    function toUint224(uint256 x) internal pure returns (uint224) {
        if (x >= 1 << 224) _revertOverflow();
        return uint224(x);
    }

    /// @dev Casts `x` to a uint232. Reverts on overflow.
    function toUint232(uint256 x) internal pure returns (uint232) {
        if (x >= 1 << 232) _revertOverflow();
        return uint232(x);
    }

    /// @dev Casts `x` to a uint240. Reverts on overflow.
    function toUint240(uint256 x) internal pure returns (uint240) {
        if (x >= 1 << 240) _revertOverflow();
        return uint240(x);
    }

    /// @dev Casts `x` to a uint248. Reverts on overflow.
    function toUint248(uint256 x) internal pure returns (uint248) {
        if (x >= 1 << 248) _revertOverflow();
        return uint248(x);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*           SIGNED INTEGER SAFE CASTING OPERATIONS           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Casts `x` to a int8. Reverts on overflow.
    function toInt8(int256 x) internal pure returns (int8) {
        unchecked {
            if (((1 << 7) + uint256(x)) >> 8 == uint256(0)) return int8(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int16. Reverts on overflow.
    function toInt16(int256 x) internal pure returns (int16) {
        unchecked {
            if (((1 << 15) + uint256(x)) >> 16 == uint256(0)) return int16(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int24. Reverts on overflow.
    function toInt24(int256 x) internal pure returns (int24) {
        unchecked {
            if (((1 << 23) + uint256(x)) >> 24 == uint256(0)) return int24(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int32. Reverts on overflow.
    function toInt32(int256 x) internal pure returns (int32) {
        unchecked {
            if (((1 << 31) + uint256(x)) >> 32 == uint256(0)) return int32(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int40. Reverts on overflow.
    function toInt40(int256 x) internal pure returns (int40) {
        unchecked {
            if (((1 << 39) + uint256(x)) >> 40 == uint256(0)) return int40(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int48. Reverts on overflow.
    function toInt48(int256 x) internal pure returns (int48) {
        unchecked {
            if (((1 << 47) + uint256(x)) >> 48 == uint256(0)) return int48(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int56. Reverts on overflow.
    function toInt56(int256 x) internal pure returns (int56) {
        unchecked {
            if (((1 << 55) + uint256(x)) >> 56 == uint256(0)) return int56(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int64. Reverts on overflow.
    function toInt64(int256 x) internal pure returns (int64) {
        unchecked {
            if (((1 << 63) + uint256(x)) >> 64 == uint256(0)) return int64(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int72. Reverts on overflow.
    function toInt72(int256 x) internal pure returns (int72) {
        unchecked {
            if (((1 << 71) + uint256(x)) >> 72 == uint256(0)) return int72(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int80. Reverts on overflow.
    function toInt80(int256 x) internal pure returns (int80) {
        unchecked {
            if (((1 << 79) + uint256(x)) >> 80 == uint256(0)) return int80(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int88. Reverts on overflow.
    function toInt88(int256 x) internal pure returns (int88) {
        unchecked {
            if (((1 << 87) + uint256(x)) >> 88 == uint256(0)) return int88(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int96. Reverts on overflow.
    function toInt96(int256 x) internal pure returns (int96) {
        unchecked {
            if (((1 << 95) + uint256(x)) >> 96 == uint256(0)) return int96(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int104. Reverts on overflow.
    function toInt104(int256 x) internal pure returns (int104) {
        unchecked {
            if (((1 << 103) + uint256(x)) >> 104 == uint256(0)) return int104(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int112. Reverts on overflow.
    function toInt112(int256 x) internal pure returns (int112) {
        unchecked {
            if (((1 << 111) + uint256(x)) >> 112 == uint256(0)) return int112(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int120. Reverts on overflow.
    function toInt120(int256 x) internal pure returns (int120) {
        unchecked {
            if (((1 << 119) + uint256(x)) >> 120 == uint256(0)) return int120(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int128. Reverts on overflow.
    function toInt128(int256 x) internal pure returns (int128) {
        unchecked {
            if (((1 << 127) + uint256(x)) >> 128 == uint256(0)) return int128(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int136. Reverts on overflow.
    function toInt136(int256 x) internal pure returns (int136) {
        unchecked {
            if (((1 << 135) + uint256(x)) >> 136 == uint256(0)) return int136(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int144. Reverts on overflow.
    function toInt144(int256 x) internal pure returns (int144) {
        unchecked {
            if (((1 << 143) + uint256(x)) >> 144 == uint256(0)) return int144(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int152. Reverts on overflow.
    function toInt152(int256 x) internal pure returns (int152) {
        unchecked {
            if (((1 << 151) + uint256(x)) >> 152 == uint256(0)) return int152(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int160. Reverts on overflow.
    function toInt160(int256 x) internal pure returns (int160) {
        unchecked {
            if (((1 << 159) + uint256(x)) >> 160 == uint256(0)) return int160(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int168. Reverts on overflow.
    function toInt168(int256 x) internal pure returns (int168) {
        unchecked {
            if (((1 << 167) + uint256(x)) >> 168 == uint256(0)) return int168(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int176. Reverts on overflow.
    function toInt176(int256 x) internal pure returns (int176) {
        unchecked {
            if (((1 << 175) + uint256(x)) >> 176 == uint256(0)) return int176(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int184. Reverts on overflow.
    function toInt184(int256 x) internal pure returns (int184) {
        unchecked {
            if (((1 << 183) + uint256(x)) >> 184 == uint256(0)) return int184(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int192. Reverts on overflow.
    function toInt192(int256 x) internal pure returns (int192) {
        unchecked {
            if (((1 << 191) + uint256(x)) >> 192 == uint256(0)) return int192(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int200. Reverts on overflow.
    function toInt200(int256 x) internal pure returns (int200) {
        unchecked {
            if (((1 << 199) + uint256(x)) >> 200 == uint256(0)) return int200(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int208. Reverts on overflow.
    function toInt208(int256 x) internal pure returns (int208) {
        unchecked {
            if (((1 << 207) + uint256(x)) >> 208 == uint256(0)) return int208(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int216. Reverts on overflow.
    function toInt216(int256 x) internal pure returns (int216) {
        unchecked {
            if (((1 << 215) + uint256(x)) >> 216 == uint256(0)) return int216(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int224. Reverts on overflow.
    function toInt224(int256 x) internal pure returns (int224) {
        unchecked {
            if (((1 << 223) + uint256(x)) >> 224 == uint256(0)) return int224(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int232. Reverts on overflow.
    function toInt232(int256 x) internal pure returns (int232) {
        unchecked {
            if (((1 << 231) + uint256(x)) >> 232 == uint256(0)) return int232(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int240. Reverts on overflow.
    function toInt240(int256 x) internal pure returns (int240) {
        unchecked {
            if (((1 << 239) + uint256(x)) >> 240 == uint256(0)) return int240(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int248. Reverts on overflow.
    function toInt248(int256 x) internal pure returns (int248) {
        unchecked {
            if (((1 << 247) + uint256(x)) >> 248 == uint256(0)) return int248(x);
            _revertOverflow();
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               OTHER SAFE CASTING OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Casts `x` to a int8. Reverts on overflow.
    function toInt8(uint256 x) internal pure returns (int8) {
        if (x >= 1 << 7) _revertOverflow();
        return int8(int256(x));
    }

    /// @dev Casts `x` to a int16. Reverts on overflow.
    function toInt16(uint256 x) internal pure returns (int16) {
        if (x >= 1 << 15) _revertOverflow();
        return int16(int256(x));
    }

    /// @dev Casts `x` to a int24. Reverts on overflow.
    function toInt24(uint256 x) internal pure returns (int24) {
        if (x >= 1 << 23) _revertOverflow();
        return int24(int256(x));
    }

    /// @dev Casts `x` to a int32. Reverts on overflow.
    function toInt32(uint256 x) internal pure returns (int32) {
        if (x >= 1 << 31) _revertOverflow();
        return int32(int256(x));
    }

    /// @dev Casts `x` to a int40. Reverts on overflow.
    function toInt40(uint256 x) internal pure returns (int40) {
        if (x >= 1 << 39) _revertOverflow();
        return int40(int256(x));
    }

    /// @dev Casts `x` to a int48. Reverts on overflow.
    function toInt48(uint256 x) internal pure returns (int48) {
        if (x >= 1 << 47) _revertOverflow();
        return int48(int256(x));
    }

    /// @dev Casts `x` to a int56. Reverts on overflow.
    function toInt56(uint256 x) internal pure returns (int56) {
        if (x >= 1 << 55) _revertOverflow();
        return int56(int256(x));
    }

    /// @dev Casts `x` to a int64. Reverts on overflow.
    function toInt64(uint256 x) internal pure returns (int64) {
        if (x >= 1 << 63) _revertOverflow();
        return int64(int256(x));
    }

    /// @dev Casts `x` to a int72. Reverts on overflow.
    function toInt72(uint256 x) internal pure returns (int72) {
        if (x >= 1 << 71) _revertOverflow();
        return int72(int256(x));
    }

    /// @dev Casts `x` to a int80. Reverts on overflow.
    function toInt80(uint256 x) internal pure returns (int80) {
        if (x >= 1 << 79) _revertOverflow();
        return int80(int256(x));
    }

    /// @dev Casts `x` to a int88. Reverts on overflow.
    function toInt88(uint256 x) internal pure returns (int88) {
        if (x >= 1 << 87) _revertOverflow();
        return int88(int256(x));
    }

    /// @dev Casts `x` to a int96. Reverts on overflow.
    function toInt96(uint256 x) internal pure returns (int96) {
        if (x >= 1 << 95) _revertOverflow();
        return int96(int256(x));
    }

    /// @dev Casts `x` to a int104. Reverts on overflow.
    function toInt104(uint256 x) internal pure returns (int104) {
        if (x >= 1 << 103) _revertOverflow();
        return int104(int256(x));
    }

    /// @dev Casts `x` to a int112. Reverts on overflow.
    function toInt112(uint256 x) internal pure returns (int112) {
        if (x >= 1 << 111) _revertOverflow();
        return int112(int256(x));
    }

    /// @dev Casts `x` to a int120. Reverts on overflow.
    function toInt120(uint256 x) internal pure returns (int120) {
        if (x >= 1 << 119) _revertOverflow();
        return int120(int256(x));
    }

    /// @dev Casts `x` to a int128. Reverts on overflow.
    function toInt128(uint256 x) internal pure returns (int128) {
        if (x >= 1 << 127) _revertOverflow();
        return int128(int256(x));
    }

    /// @dev Casts `x` to a int136. Reverts on overflow.
    function toInt136(uint256 x) internal pure returns (int136) {
        if (x >= 1 << 135) _revertOverflow();
        return int136(int256(x));
    }

    /// @dev Casts `x` to a int144. Reverts on overflow.
    function toInt144(uint256 x) internal pure returns (int144) {
        if (x >= 1 << 143) _revertOverflow();
        return int144(int256(x));
    }

    /// @dev Casts `x` to a int152. Reverts on overflow.
    function toInt152(uint256 x) internal pure returns (int152) {
        if (x >= 1 << 151) _revertOverflow();
        return int152(int256(x));
    }

    /// @dev Casts `x` to a int160. Reverts on overflow.
    function toInt160(uint256 x) internal pure returns (int160) {
        if (x >= 1 << 159) _revertOverflow();
        return int160(int256(x));
    }

    /// @dev Casts `x` to a int168. Reverts on overflow.
    function toInt168(uint256 x) internal pure returns (int168) {
        if (x >= 1 << 167) _revertOverflow();
        return int168(int256(x));
    }

    /// @dev Casts `x` to a int176. Reverts on overflow.
    function toInt176(uint256 x) internal pure returns (int176) {
        if (x >= 1 << 175) _revertOverflow();
        return int176(int256(x));
    }

    /// @dev Casts `x` to a int184. Reverts on overflow.
    function toInt184(uint256 x) internal pure returns (int184) {
        if (x >= 1 << 183) _revertOverflow();
        return int184(int256(x));
    }

    /// @dev Casts `x` to a int192. Reverts on overflow.
    function toInt192(uint256 x) internal pure returns (int192) {
        if (x >= 1 << 191) _revertOverflow();
        return int192(int256(x));
    }

    /// @dev Casts `x` to a int200. Reverts on overflow.
    function toInt200(uint256 x) internal pure returns (int200) {
        if (x >= 1 << 199) _revertOverflow();
        return int200(int256(x));
    }

    /// @dev Casts `x` to a int208. Reverts on overflow.
    function toInt208(uint256 x) internal pure returns (int208) {
        if (x >= 1 << 207) _revertOverflow();
        return int208(int256(x));
    }

    /// @dev Casts `x` to a int216. Reverts on overflow.
    function toInt216(uint256 x) internal pure returns (int216) {
        if (x >= 1 << 215) _revertOverflow();
        return int216(int256(x));
    }

    /// @dev Casts `x` to a int224. Reverts on overflow.
    function toInt224(uint256 x) internal pure returns (int224) {
        if (x >= 1 << 223) _revertOverflow();
        return int224(int256(x));
    }

    /// @dev Casts `x` to a int232. Reverts on overflow.
    function toInt232(uint256 x) internal pure returns (int232) {
        if (x >= 1 << 231) _revertOverflow();
        return int232(int256(x));
    }

    /// @dev Casts `x` to a int240. Reverts on overflow.
    function toInt240(uint256 x) internal pure returns (int240) {
        if (x >= 1 << 239) _revertOverflow();
        return int240(int256(x));
    }

    /// @dev Casts `x` to a int248. Reverts on overflow.
    function toInt248(uint256 x) internal pure returns (int248) {
        if (x >= 1 << 247) _revertOverflow();
        return int248(int256(x));
    }

    /// @dev Casts `x` to a int256. Reverts on overflow.
    function toInt256(uint256 x) internal pure returns (int256) {
        if (int256(x) >= 0) return int256(x);
        _revertOverflow();
    }

    /// @dev Casts `x` to a uint256. Reverts on overflow.
    function toUint256(int256 x) internal pure returns (uint256) {
        if (x >= 0) return uint256(x);
        _revertOverflow();
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function _revertOverflow() private pure {
        /// @solidity memory-safe-assembly
        assembly {
            // Store the function selector of `Overflow()`.
            mstore(0x00, 0x35278d12)
            // Revert with (offset, size).
            revert(0x1c, 0x04)
        }
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

// This functionality is externalized so it can be upgraded later, e.g. to change the URL or generate the URI on-chain
interface ITokenURIGenerator {
    function generateTokenURI(uint256 id) external view returns (string memory);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {ERC721} from "solady/tokens/ERC721.sol";
import {ITokenURIGenerator} from "../interfaces/ITokenURIGenerator.sol";

abstract contract MintableNFT is ERC721 {
    error Unauthorized(address caller, uint256 id);

    ITokenURIGenerator public immutable tokenURIGenerator;

    constructor(ITokenURIGenerator _tokenURIGenerator) {
        tokenURIGenerator = _tokenURIGenerator;
    }

    function tokenURI(uint256 id) public view override returns (string memory) {
        return tokenURIGenerator.generateTokenURI(id);
    }

    modifier authorizedForNft(uint256 id) {
        if (!_isApprovedOrOwner(msg.sender, id)) {
            revert Unauthorized(msg.sender, id);
        }
        _;
    }

    function saltToId(address minter, bytes32 salt) public view returns (uint256 result) {
        assembly ("memory-safe") {
            let free := mload(0x40)
            mstore(free, minter)
            mstore(add(free, 32), salt)
            mstore(add(free, 64), chainid())
            mstore(add(free, 96), address())

            result := shr(128, keccak256(free, 128))
        }
    }

    function mint() public payable returns (uint256 id) {
        // generates a pseudorandom salt
        // note this can have encounter conflicts if a sender sends two identical transactions in the same block
        // that happen to consume exactly the same amount of gas
        bytes32 salt;
        assembly ("memory-safe") {
            mstore(0, prevrandao())
            mstore(32, gas())
            salt := keccak256(0, 64)
        }
        id = mint(salt);
    }

    // Mints an NFT for the caller with the ID given by shr(192, keccak256(minter, salt))
    // This prevents us from having to store a counter of how many were minted
    function mint(bytes32 salt) public payable returns (uint256 id) {
        id = saltToId(msg.sender, salt);
        _mint(msg.sender, id);
    }

    // Can be used to refund some gas after the NFT is no longer needed.
    // The NFT ID may be re-minted by the original minter after it is burned by re-using the salt.
    function burn(uint256 id) external payable authorizedForNft(id) {
        _burn(id);
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

interface ILocker {
    function locked(uint256 id) external;
}

interface IForwardee {
    function forwarded(uint256 id, address originalLocker) external;
}

interface IPayer {
    function payCallback(uint256 id, address token) external;
}

interface IFlashAccountant {
    error NotLocked();
    error LockerOnly();
    error NoPaymentMade();
    error DebtsNotZeroed(uint256 id);
    // Thrown if the contract receives too much payment in the payment callback or from a direct native token transfer
    error PaymentOverflow();
    error PayReentrance();

    // Create a lock context
    // Any data passed after the function signature is passed through back to the caller after the locked function signature and data, with no additional encoding
    // In addition, any data returned from ILocker#locked is also returned from this function exactly as is, i.e. with no additional encoding or decoding
    // Reverts are also bubbled up
    function lock() external;

    // Forward the lock from the current locker to the given address
    // Any additional calldata is also passed through to the forwardee, with no additional encoding
    // In addition, any data returned from IForwardee#forwarded is also returned from this function exactly as is, i.e. with no additional encoding or decoding
    // Reverts are also bubbled up
    function forward(address to) external;

    // Pays the given amount of token, by calling the payCallback function on the caller to afford them the opportunity to make the payment.
    // This function, unlike lock and forward, does not return any of the returndata from the callback.
    // This function also cannot be re-entered like lock and forward.
    // Must be locked, as the contract accounts the payment against the current locker's debts.
    // Token must not be the NATIVE_TOKEN_ADDRESS, as the `balanceOf` calls will fail.
    // If you want to pay in the chain's native token, simply transfer it to this contract using a call.
    // The payer must implement payCallback in which they must transfer the token to Core.
    function pay(address token) external returns (uint128 payment);

    // Withdraws a token amount from the accountant to the given recipient.
    // The contract must be locked, as it tracks the withdrawn amount against the current locker's delta.
    function withdraw(address token, address recipient, uint128 amount) external;

    // This contract can receive ETH as a payment as well
    receive() external payable;
}

File 21 of 29 : constants.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

int32 constant MIN_TICK = -88722835;
int32 constant MAX_TICK = 88722835;
uint32 constant MAX_TICK_MAGNITUDE = uint32(MAX_TICK);
uint32 constant MAX_TICK_SPACING = 698605;

uint32 constant FULL_RANGE_ONLY_TICK_SPACING = 0;

// We use this address to represent the native token within the protocol
address constant NATIVE_TOKEN_ADDRESS = address(0);

// 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 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 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: UNLICENSED
pragma solidity =0.8.28;

struct CallPoints {
    bool beforeInitializePool;
    bool afterInitializePool;
    bool beforeSwap;
    bool afterSwap;
    bool beforeUpdatePosition;
    bool afterUpdatePosition;
    bool beforeCollectFees;
    bool afterCollectFees;
}

using {eq, isValid, toUint8} for CallPoints global;

function eq(CallPoints memory a, CallPoints memory b) pure returns (bool) {
    return (
        a.beforeInitializePool == b.beforeInitializePool && a.afterInitializePool == b.afterInitializePool
            && a.beforeSwap == b.beforeSwap && a.afterSwap == b.afterSwap
            && a.beforeUpdatePosition == b.beforeUpdatePosition && a.afterUpdatePosition == b.afterUpdatePosition
            && a.beforeCollectFees == b.beforeCollectFees && a.afterCollectFees == b.afterCollectFees
    );
}

function isValid(CallPoints memory a) pure returns (bool) {
    return (
        a.beforeInitializePool || a.afterInitializePool || a.beforeSwap || a.afterSwap || a.beforeUpdatePosition
            || a.afterUpdatePosition || a.beforeCollectFees || a.afterCollectFees
    );
}

function toUint8(CallPoints memory callPoints) pure returns (uint8 b) {
    assembly ("memory-safe") {
        b :=
            add(
                add(
                    add(
                        add(
                            add(
                                add(
                                    add(mload(callPoints), mul(128, mload(add(callPoints, 32)))),
                                    mul(64, mload(add(callPoints, 64)))
                                ),
                                mul(32, mload(add(callPoints, 96)))
                            ),
                            mul(16, mload(add(callPoints, 128)))
                        ),
                        mul(8, mload(add(callPoints, 160)))
                    ),
                    mul(4, mload(add(callPoints, 192)))
                ),
                mul(2, mload(add(callPoints, 224)))
            )
    }
}

function addressToCallPoints(address a) pure returns (CallPoints memory result) {
    result = byteToCallPoints(uint8(uint160(a) >> 152));
}

function byteToCallPoints(uint8 b) pure returns (CallPoints memory result) {
    // note the order of bytes does not match the struct order of elements because we are matching the cairo implementation
    // which for legacy reasons has the fields in this order
    result = CallPoints({
        beforeInitializePool: (b & 1) != 0,
        afterInitializePool: (b & 128) != 0,
        beforeSwap: (b & 64) != 0,
        afterSwap: (b & 32) != 0,
        beforeUpdatePosition: (b & 16) != 0,
        afterUpdatePosition: (b & 8) != 0,
        beforeCollectFees: (b & 4) != 0,
        afterCollectFees: (b & 2) != 0
    });
}

function shouldCallBeforeInitializePool(address a) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := and(shr(152, a), 1)
    }
}

function shouldCallAfterInitializePool(address a) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := and(shr(159, a), 1)
    }
}

function shouldCallBeforeSwap(address a) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := and(shr(158, a), 1)
    }
}

function shouldCallAfterSwap(address a) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := and(shr(157, a), 1)
    }
}

function shouldCallBeforeUpdatePosition(address a) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := and(shr(156, a), 1)
    }
}

function shouldCallAfterUpdatePosition(address a) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := and(shr(155, a), 1)
    }
}

function shouldCallBeforeCollectFees(address a) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := and(shr(154, a), 1)
    }
}

function shouldCallAfterCollectFees(address a) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := and(shr(153, a), 1)
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

// Exposes all the storage of a contract via view methods.
// Absent https://eips.ethereum.org/EIPS/eip-2330 this makes it easier to access specific pieces of state in the inheriting contract.
interface IExposedStorage {
    // Loads each slot after the function selector from the contract's storage and returns all of them.
    function sload() external view;
    // Loads each slot after the function selector from the contract's transient storage and returns all of them.
    function tload() external view;
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

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

/// @dev This library includes some helper functions for calling IExposedStorage#sload and IExposedStorage#tload.
library ExposedStorageLib {
    function sload(IExposedStorage target, bytes32 slot) internal view returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0, shl(224, 0x380eb4e0))
            mstore(4, slot)

            if iszero(staticcall(gas(), target, 0, 36, 0, 32)) { revert(0, 0) }

            result := mload(0)
        }
    }

    function sload(IExposedStorage target, bytes32 slot0, bytes32 slot1, bytes32 slot2)
        internal
        view
        returns (bytes32 result0, bytes32 result1, bytes32 result2)
    {
        assembly ("memory-safe") {
            let o := mload(0x40)
            mstore(o, shl(224, 0x380eb4e0))
            mstore(add(o, 4), slot0)
            mstore(add(o, 36), slot1)
            mstore(add(o, 68), slot2)

            if iszero(staticcall(gas(), target, o, 100, o, 96)) { revert(0, 0) }

            result0 := mload(o)
            result1 := mload(add(o, 32))
            result2 := mload(add(o, 64))
        }
    }

    function tload(IExposedStorage target, bytes32 slot) internal view returns (bytes32 result) {
        assembly ("memory-safe") {
            mstore(0, shl(224, 0xed832830))
            mstore(4, slot)

            if iszero(staticcall(gas(), target, 0, 36, 0, 32)) { revert(0, 0) }

            result := mload(0)
        }
    }
}

File 26 of 29 : EfficientHashLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for efficiently performing keccak256 hashes.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EfficientHashLib.sol)
/// @dev To avoid stack-too-deep, you can use:
/// ```
/// bytes32[] memory buffer = EfficientHashLib.malloc(10);
/// EfficientHashLib.set(buffer, 0, value0);
/// ..
/// EfficientHashLib.set(buffer, 9, value9);
/// bytes32 finalHash = EfficientHashLib.hash(buffer);
/// ```
library EfficientHashLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               MALLOC-LESS HASHING OPERATIONS               */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `keccak256(abi.encode(v0))`.
    function hash(bytes32 v0) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, v0)
            result := keccak256(0x00, 0x20)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0))`.
    function hash(uint256 v0) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, v0)
            result := keccak256(0x00, 0x20)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1))`.
    function hash(bytes32 v0, bytes32 v1) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, v0)
            mstore(0x20, v1)
            result := keccak256(0x00, 0x40)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1))`.
    function hash(uint256 v0, uint256 v1) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, v0)
            mstore(0x20, v1)
            result := keccak256(0x00, 0x40)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1, v2))`.
    function hash(bytes32 v0, bytes32 v1, bytes32 v2) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            result := keccak256(m, 0x60)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1, v2))`.
    function hash(uint256 v0, uint256 v1, uint256 v2) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            result := keccak256(m, 0x60)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1, v2, v3))`.
    function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            result := keccak256(m, 0x80)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, v1, v2, v3))`.
    function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            result := keccak256(m, 0x80)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v4))`.
    function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3, bytes32 v4)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            result := keccak256(m, 0xa0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v4))`.
    function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3, uint256 v4)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            result := keccak256(m, 0xa0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v5))`.
    function hash(bytes32 v0, bytes32 v1, bytes32 v2, bytes32 v3, bytes32 v4, bytes32 v5)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            result := keccak256(m, 0xc0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v5))`.
    function hash(uint256 v0, uint256 v1, uint256 v2, uint256 v3, uint256 v4, uint256 v5)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            result := keccak256(m, 0xc0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v6))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            result := keccak256(m, 0xe0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v6))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            result := keccak256(m, 0xe0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v7))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            result := keccak256(m, 0x100)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v7))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            result := keccak256(m, 0x100)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v8))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            result := keccak256(m, 0x120)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v8))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            result := keccak256(m, 0x120)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v9))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8,
        bytes32 v9
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            result := keccak256(m, 0x140)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v9))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8,
        uint256 v9
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            result := keccak256(m, 0x140)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v10))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8,
        bytes32 v9,
        bytes32 v10
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            result := keccak256(m, 0x160)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v10))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8,
        uint256 v9,
        uint256 v10
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            result := keccak256(m, 0x160)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v11))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8,
        bytes32 v9,
        bytes32 v10,
        bytes32 v11
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            result := keccak256(m, 0x180)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v11))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8,
        uint256 v9,
        uint256 v10,
        uint256 v11
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            result := keccak256(m, 0x180)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v12))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8,
        bytes32 v9,
        bytes32 v10,
        bytes32 v11,
        bytes32 v12
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            mstore(add(m, 0x180), v12)
            result := keccak256(m, 0x1a0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v12))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8,
        uint256 v9,
        uint256 v10,
        uint256 v11,
        uint256 v12
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            mstore(add(m, 0x180), v12)
            result := keccak256(m, 0x1a0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v13))`.
    function hash(
        bytes32 v0,
        bytes32 v1,
        bytes32 v2,
        bytes32 v3,
        bytes32 v4,
        bytes32 v5,
        bytes32 v6,
        bytes32 v7,
        bytes32 v8,
        bytes32 v9,
        bytes32 v10,
        bytes32 v11,
        bytes32 v12,
        bytes32 v13
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            mstore(add(m, 0x180), v12)
            mstore(add(m, 0x1a0), v13)
            result := keccak256(m, 0x1c0)
        }
    }

    /// @dev Returns `keccak256(abi.encode(v0, .., v13))`.
    function hash(
        uint256 v0,
        uint256 v1,
        uint256 v2,
        uint256 v3,
        uint256 v4,
        uint256 v5,
        uint256 v6,
        uint256 v7,
        uint256 v8,
        uint256 v9,
        uint256 v10,
        uint256 v11,
        uint256 v12,
        uint256 v13
    ) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, v0)
            mstore(add(m, 0x20), v1)
            mstore(add(m, 0x40), v2)
            mstore(add(m, 0x60), v3)
            mstore(add(m, 0x80), v4)
            mstore(add(m, 0xa0), v5)
            mstore(add(m, 0xc0), v6)
            mstore(add(m, 0xe0), v7)
            mstore(add(m, 0x100), v8)
            mstore(add(m, 0x120), v9)
            mstore(add(m, 0x140), v10)
            mstore(add(m, 0x160), v11)
            mstore(add(m, 0x180), v12)
            mstore(add(m, 0x1a0), v13)
            result := keccak256(m, 0x1c0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*             BYTES32 BUFFER HASHING OPERATIONS              */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `keccak256(abi.encode(buffer[0], .., buffer[buffer.length - 1]))`.
    function hash(bytes32[] memory buffer) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := keccak256(add(buffer, 0x20), shl(5, mload(buffer)))
        }
    }

    /// @dev Sets `buffer[i]` to `value`, without a bounds check.
    /// Returns the `buffer` for function chaining.
    function set(bytes32[] memory buffer, uint256 i, bytes32 value)
        internal
        pure
        returns (bytes32[] memory)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(add(buffer, shl(5, add(1, i))), value)
        }
        return buffer;
    }

    /// @dev Sets `buffer[i]` to `value`, without a bounds check.
    /// Returns the `buffer` for function chaining.
    function set(bytes32[] memory buffer, uint256 i, uint256 value)
        internal
        pure
        returns (bytes32[] memory)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(add(buffer, shl(5, add(1, i))), value)
        }
        return buffer;
    }

    /// @dev Returns `new bytes32[](n)`, without zeroing out the memory.
    function malloc(uint256 n) internal pure returns (bytes32[] memory buffer) {
        /// @solidity memory-safe-assembly
        assembly {
            buffer := mload(0x40)
            mstore(buffer, n)
            mstore(0x40, add(shl(5, add(1, n)), buffer))
        }
    }

    /// @dev Frees memory that has been allocated for `buffer`.
    /// No-op if `buffer.length` is zero, or if new memory has been allocated after `buffer`.
    function free(bytes32[] memory buffer) internal pure {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(buffer)
            mstore(shl(6, lt(iszero(n), eq(add(shl(5, add(1, n)), buffer), mload(0x40)))), buffer)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      EQUALITY CHECKS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `a == abi.decode(b, (bytes32))`.
    function eq(bytes32 a, bytes memory b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(eq(0x20, mload(b)), eq(a, mload(add(b, 0x20))))
        }
    }

    /// @dev Returns `abi.decode(a, (bytes32)) == a`.
    function eq(bytes memory a, bytes32 b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(eq(0x20, mload(a)), eq(b, mload(add(a, 0x20))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               BYTE SLICE HASHING OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the keccak256 of the slice from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function hash(bytes memory b, uint256 start, uint256 end)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(b)
            end := xor(end, mul(xor(end, n), lt(n, end)))
            start := xor(start, mul(xor(start, n), lt(n, start)))
            result := keccak256(add(add(b, 0x20), start), mul(gt(end, start), sub(end, start)))
        }
    }

    /// @dev Returns the keccak256 of the slice from `start` to the end of the bytes.
    function hash(bytes memory b, uint256 start) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(b)
            start := xor(start, mul(xor(start, n), lt(n, start)))
            result := keccak256(add(add(b, 0x20), start), mul(gt(n, start), sub(n, start)))
        }
    }

    /// @dev Returns the keccak256 of the bytes.
    function hash(bytes memory b) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := keccak256(add(b, 0x20), mload(b))
        }
    }

    /// @dev Returns the keccak256 of the slice from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function hashCalldata(bytes calldata b, uint256 start, uint256 end)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            end := xor(end, mul(xor(end, b.length), lt(b.length, end)))
            start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
            let n := mul(gt(end, start), sub(end, start))
            calldatacopy(mload(0x40), add(b.offset, start), n)
            result := keccak256(mload(0x40), n)
        }
    }

    /// @dev Returns the keccak256 of the slice from `start` to the end of the bytes.
    function hashCalldata(bytes calldata b, uint256 start) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
            let n := mul(gt(b.length, start), sub(b.length, start))
            calldatacopy(mload(0x40), add(b.offset, start), n)
            result := keccak256(mload(0x40), n)
        }
    }

    /// @dev Returns the keccak256 of the bytes.
    function hashCalldata(bytes calldata b) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            calldatacopy(mload(0x40), b.offset, b.length)
            result := keccak256(mload(0x40), b.length)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      SHA2-256 HELPERS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `sha256(abi.encode(b))`. Yes, it's more efficient.
    function sha2(bytes32 b) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, b)
            result := mload(staticcall(gas(), 2, 0x00, 0x20, 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the slice from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function sha2(bytes memory b, uint256 start, uint256 end)
        internal
        view
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(b)
            end := xor(end, mul(xor(end, n), lt(n, end)))
            start := xor(start, mul(xor(start, n), lt(n, start)))
            // forgefmt: disable-next-item
            result := mload(staticcall(gas(), 2, add(add(b, 0x20), start),
                mul(gt(end, start), sub(end, start)), 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the slice from `start` to the end of the bytes.
    function sha2(bytes memory b, uint256 start) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(b)
            start := xor(start, mul(xor(start, n), lt(n, start)))
            // forgefmt: disable-next-item
            result := mload(staticcall(gas(), 2, add(add(b, 0x20), start),
                mul(gt(n, start), sub(n, start)), 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the bytes.
    function sha2(bytes memory b) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(staticcall(gas(), 2, add(b, 0x20), mload(b), 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the slice from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function sha2Calldata(bytes calldata b, uint256 start, uint256 end)
        internal
        view
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            end := xor(end, mul(xor(end, b.length), lt(b.length, end)))
            start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
            let n := mul(gt(end, start), sub(end, start))
            calldatacopy(mload(0x40), add(b.offset, start), n)
            result := mload(staticcall(gas(), 2, mload(0x40), n, 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the slice from `start` to the end of the bytes.
    function sha2Calldata(bytes calldata b, uint256 start) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            start := xor(start, mul(xor(start, b.length), lt(b.length, start)))
            let n := mul(gt(b.length, start), sub(b.length, start))
            calldatacopy(mload(0x40), add(b.offset, start), n)
            result := mload(staticcall(gas(), 2, mload(0x40), n, 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }

    /// @dev Returns the sha256 of the bytes.
    function sha2Calldata(bytes calldata b) internal view returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            calldatacopy(mload(0x40), b.offset, b.length)
            result := mload(staticcall(gas(), 2, mload(0x40), b.length, 0x01, 0x20))
            if iszero(returndatasize()) { invalid() }
        }
    }
}

File 27 of 29 : 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/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: UNLICENSED
pragma solidity =0.8.28;

import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";

error Amount0DeltaOverflow();
error Amount1DeltaOverflow();

function sortAndConvertToFixedSqrtRatios(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB)
    pure
    returns (uint256 sqrtRatioLower, uint256 sqrtRatioUpper)
{
    uint256 aFixed = sqrtRatioA.toFixed();
    uint256 bFixed = sqrtRatioB.toFixed();
    (sqrtRatioLower, sqrtRatioUpper) = (FixedPointMathLib.min(aFixed, bFixed), FixedPointMathLib.max(aFixed, bFixed));
}

function amount0Delta(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB, uint128 liquidity, bool roundUp)
    pure
    returns (uint128 amount0)
{
    unchecked {
        (uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);

        if (roundUp) {
            uint256 result0 = FixedPointMathLib.fullMulDivUp(
                (uint256(liquidity) << 128), (sqrtRatioUpper - sqrtRatioLower), sqrtRatioUpper
            );
            uint256 result = FixedPointMathLib.divUp(result0, sqrtRatioLower);
            if (result > type(uint128).max) revert Amount0DeltaOverflow();
            amount0 = uint128(result);
        } else {
            uint256 result0 = FixedPointMathLib.fullMulDiv(
                (uint256(liquidity) << 128), (sqrtRatioUpper - sqrtRatioLower), sqrtRatioUpper
            );
            uint256 result = result0 / sqrtRatioLower;
            if (result > type(uint128).max) revert Amount0DeltaOverflow();
            amount0 = uint128(result);
        }
    }
}

function amount1Delta(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB, uint128 liquidity, bool roundUp)
    pure
    returns (uint128 amount1)
{
    unchecked {
        (uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);

        uint256 difference = sqrtRatioUpper - sqrtRatioLower;

        if (roundUp) {
            uint256 result = FixedPointMathLib.fullMulDivN(difference, liquidity, 128);
            assembly {
                // addition is safe from overflow because the result of fullMulDivN will never equal type(uint256).max
                result :=
                    add(result, iszero(iszero(mulmod(difference, liquidity, 0x100000000000000000000000000000000))))
            }
            if (result > type(uint128).max) revert Amount1DeltaOverflow();
            amount1 = uint128(result);
        } else {
            uint256 result = FixedPointMathLib.fullMulDivN(difference, liquidity, 128);
            if (result > type(uint128).max) revert Amount1DeltaOverflow();
            amount1 = uint128(result);
        }
    }
}

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

/// @notice Contract that enables a single call to call multiple methods on itself.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Multicallable.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Multicallable.sol)
///
/// WARNING:
/// This implementation is NOT to be used with ERC2771 out-of-the-box.
/// https://blog.openzeppelin.com/arbitrary-address-spoofing-vulnerability-erc2771context-multicall-public-disclosure
/// This also applies to potentially other ERCs / patterns appending to the back of calldata.
///
/// We do NOT have a check for ERC2771, as we do not inherit from OpenZeppelin's context.
/// Moreover, it is infeasible and inefficient for us to add checks and mitigations
/// for all possible ERC / patterns appending to the back of calldata.
///
/// We would highly recommend using an alternative pattern such as
/// https://github.com/Vectorized/multicaller
/// which is more flexible, futureproof, and safer by default.
abstract contract Multicallable {
    /// @dev Apply `delegatecall` with the current contract to each calldata in `data`,
    /// and store the `abi.encode` formatted results of each `delegatecall` into `results`.
    /// If any of the `delegatecall`s reverts, the entire context is reverted,
    /// and the error is bubbled up.
    ///
    /// By default, this function directly returns the results and terminates the call context.
    /// If you need to add before and after actions to the multicall, please override this function.
    function multicall(bytes[] calldata data) public payable virtual returns (bytes[] memory) {
        // Revert if `msg.value` is non-zero by default to guard against double-spending.
        // (See: https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong)
        //
        // If you really need to pass in a `msg.value`, then you will have to
        // override this function and add in any relevant before and after checks.
        if (msg.value != 0) revert();
        // `_multicallDirectReturn` returns the results directly and terminates the call context.
        _multicallDirectReturn(_multicall(data));
    }

    /// @dev The inner logic of `multicall`.
    /// This function is included so that you can override `multicall`
    /// to add before and after actions, and use the `_multicallDirectReturn` function.
    function _multicall(bytes[] calldata data) internal virtual returns (bytes32 results) {
        /// @solidity memory-safe-assembly
        assembly {
            results := mload(0x40)
            mstore(results, 0x20)
            mstore(add(0x20, results), data.length)
            let c := add(0x40, results)
            let s := c
            let end := shl(5, data.length)
            calldatacopy(c, data.offset, end)
            end := add(c, end)
            let m := end
            if data.length {
                for {} 1 {} {
                    let o := add(data.offset, mload(c))
                    calldatacopy(m, add(o, 0x20), calldataload(o))
                    // forgefmt: disable-next-item
                    if iszero(delegatecall(gas(), address(), m, calldataload(o), codesize(), 0x00)) {
                        // Bubble up the revert if the delegatecall reverts.
                        returndatacopy(results, 0x00, returndatasize())
                        revert(results, returndatasize())
                    }
                    mstore(c, sub(m, s))
                    c := add(0x20, c)
                    // Append the `returndatasize()`, and the return data.
                    mstore(m, returndatasize())
                    let b := add(m, 0x20)
                    returndatacopy(b, 0x00, returndatasize())
                    // Advance `m` by `returndatasize() + 0x20`,
                    // rounded up to the next multiple of 32.
                    m := and(add(add(b, returndatasize()), 0x1f), 0xffffffffffffffe0)
                    mstore(add(b, returndatasize()), 0) // Zeroize the slot after the returndata.
                    if iszero(lt(c, end)) { break }
                }
            }
            mstore(0x40, m) // Allocate memory.
            results := or(shl(64, sub(m, results)), results) // Pack the bytes length into `results`.
        }
    }

    /// @dev Decodes the `results` into an array of bytes.
    /// This can be useful if you need to access the results or re-encode it.
    function _multicallResultsToBytesArray(bytes32 results)
        internal
        pure
        virtual
        returns (bytes[] memory decoded)
    {
        /// @solidity memory-safe-assembly
        assembly {
            decoded := mload(0x40)
            let c := and(0xffffffffffffffff, results) // Extract the offset.
            mstore(decoded, mload(add(c, 0x20))) // Store the length.
            let o := add(decoded, 0x20) // Start of elements in `decoded`.
            let end := add(o, shl(5, mload(decoded)))
            mstore(0x40, end) // Allocate memory.
            let s := add(c, 0x40) // Start of elements in `results`.
            let d := sub(s, o) // Difference between input and output pointers.
            for {} iszero(eq(o, end)) { o := add(o, 0x20) } { mstore(o, add(mload(add(d, o)), s)) }
        }
    }

    /// @dev Directly returns the `results` and terminates the current call context.
    /// `results` must be from `_multicall`, else behavior is undefined.
    function _multicallDirectReturn(bytes32 results) internal pure virtual {
        /// @solidity memory-safe-assembly
        assembly {
            return(and(0xffffffffffffffff, results), shr(64, results))
        }
    }
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "solady/=lib/solady/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 9999999
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract ICore","name":"core","type":"address"},{"internalType":"contract ITokenURIGenerator","name":"tokenURIGenerator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"Amount0DeltaOverflow","type":"error"},{"inputs":[],"name":"Amount1DeltaOverflow","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"BaseLockerAccountantOnly","type":"error"},{"inputs":[],"name":"CoreOnly","type":"error"},{"inputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint128","name":"minLiquidity","type":"uint128"}],"name":"DepositFailedDueToSlippage","type":"error"},{"inputs":[],"name":"DepositOverflow","type":"error"},{"inputs":[],"name":"ExpectedRevertWithinLock","type":"error"},{"inputs":[{"internalType":"int32","name":"tick","type":"int32"}],"name":"InvalidTick","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maximumInput","type":"uint256"}],"name":"MaximumInputExceeded","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"minimumOutput","type":"uint256"}],"name":"MinimumOutputNotReceived","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"TransactionExpired","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"bytes1","name":"b","type":"bytes1"}],"name":"UnexpectedCallTypeByte","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"checkDeadline","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maximumInput","type":"uint256"}],"name":"checkMaximumInputNotExceeded","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"minimumOutput","type":"uint256"}],"name":"checkMinimumOutputReceived","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"Config","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"components":[{"internalType":"int32","name":"lower","type":"int32"},{"internalType":"int32","name":"upper","type":"int32"}],"internalType":"struct Bounds","name":"bounds","type":"tuple"}],"name":"collectFees","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"Config","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"components":[{"internalType":"int32","name":"lower","type":"int32"},{"internalType":"int32","name":"upper","type":"int32"}],"internalType":"struct Bounds","name":"bounds","type":"tuple"},{"internalType":"address","name":"recipient","type":"address"}],"name":"collectFees","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"Config","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"components":[{"internalType":"int32","name":"lower","type":"int32"},{"internalType":"int32","name":"upper","type":"int32"}],"internalType":"struct Bounds","name":"bounds","type":"tuple"},{"internalType":"uint128","name":"maxAmount0","type":"uint128"},{"internalType":"uint128","name":"maxAmount1","type":"uint128"},{"internalType":"uint128","name":"minLiquidity","type":"uint128"}],"name":"deposit","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"Config","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"components":[{"internalType":"int32","name":"lower","type":"int32"},{"internalType":"int32","name":"upper","type":"int32"}],"internalType":"struct Bounds","name":"bounds","type":"tuple"}],"name":"getPositionFeesAndLiquidity","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint128","name":"principal0","type":"uint128"},{"internalType":"uint128","name":"principal1","type":"uint128"},{"internalType":"uint128","name":"fees0","type":"uint128"},{"internalType":"uint128","name":"fees1","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"locked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"Config","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"internalType":"int32","name":"tick","type":"int32"}],"name":"maybeInitializePool","outputs":[{"internalType":"bool","name":"initialized","type":"bool"},{"internalType":"SqrtRatio","name":"sqrtRatio","type":"uint96"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mint","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"mint","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"Config","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"components":[{"internalType":"int32","name":"lower","type":"int32"},{"internalType":"int32","name":"upper","type":"int32"}],"internalType":"struct Bounds","name":"bounds","type":"tuple"},{"internalType":"uint128","name":"maxAmount0","type":"uint128"},{"internalType":"uint128","name":"maxAmount1","type":"uint128"},{"internalType":"uint128","name":"minLiquidity","type":"uint128"}],"name":"mintAndDeposit","outputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"Config","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"components":[{"internalType":"int32","name":"lower","type":"int32"},{"internalType":"int32","name":"upper","type":"int32"}],"internalType":"struct Bounds","name":"bounds","type":"tuple"},{"internalType":"uint128","name":"maxAmount0","type":"uint128"},{"internalType":"uint128","name":"maxAmount1","type":"uint128"},{"internalType":"uint128","name":"minLiquidity","type":"uint128"}],"name":"mintAndDepositWithSalt","outputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"name":"payCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"recordBalanceForSlippageCheck","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"refundNativeToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"saltToId","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenURIGenerator","outputs":[{"internalType":"contract ITokenURIGenerator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"Config","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"components":[{"internalType":"int32","name":"lower","type":"int32"},{"internalType":"int32","name":"upper","type":"int32"}],"internalType":"struct Bounds","name":"bounds","type":"tuple"},{"internalType":"uint128","name":"liquidity","type":"uint128"}],"name":"withdraw","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"Config","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"components":[{"internalType":"int32","name":"lower","type":"int32"},{"internalType":"int32","name":"upper","type":"int32"}],"internalType":"struct Bounds","name":"bounds","type":"tuple"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"withFees","type":"bool"}],"name":"withdraw","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"payable","type":"function"}]

60e0346100f557601f61489d38819003918201601f19168301916001600160401b038311848410176100f95780849260409485528339810103126100f5578051906001600160a01b038216908183036100f55760200151916001600160a01b03831683036100f55760805260a05260c05260405161478f908161010e82396080518181816101f1015281816104740152818161077a01528181610f8c0152818161129f0152818161142401528181611973015261283e015260a051818181610daf015281816120ad01528181613da4015281816142810152818161435f01526143d7015260c051818181610bcb01526118bc0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f905f3560e01c90816301ffc9a714612d335750806306fdde0314612cbe578063081812fc14612c2f578063095ea7b314612b445780630a52f5f114612a715780631249c58b14612a3e57806323b872dd14612a2c57806327c3a090146129be57806330460039146127b4578063418652701461277a57806342842e0e1461273f57806342966c68146125d157806343dce5301461249f5780634d5980b51461230d57806357b94f41146121dd578063599d07141461205f5780636352211e1461200557806370a0823114611f7f5780637ac2ff7b14611d1757806395d89b4114611ca2578063974bafe314611b055780639df3913d146118e0578063a143c66f14611872578063a22cb465146117d2578063a699094614611771578063ac4563de1461169c578063ac9650d814611557578063adf2cead14611520578063b45a3c0e14610d69578063b88d4fde14610c95578063c87b56dd14610b51578063d0523d0c14610762578063daaa7887146106f8578063e985e9c514610695578063f1695e8e1461037d5763f3e32468146101aa575f80fd5b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610361576101dd36613003565b90606435908160030b8092036103615780917f000000000000000000000000000000000000000000000000000000000000000061021d6060862082613630565b5050928315610246575b6040805186151581526bffffffffffffffffffffffff86166020820152f35b92509250926102e160846020928473ffffffffffffffffffffffffffffffffffffffff600197604051998a9687957fc053024400000000000000000000000000000000000000000000000000000000875260048701906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b6064850152165af1928315610370578193610311575b50506bffffffffffffffffffffffff604092905f80610227565b9092506020813d602011610368575b8161032d60209383612f39565b810103126103645751916bffffffffffffffffffffffff8316830361036157506bffffffffffffffffffffffff60406102f7565b80fd5b8280fd5b3d9150610320565b50604051903d90823e3d90fd5b506101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610361576103b236613003565b9060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c36011261036157604051916103ea83612f1d565b6064358060030b8103610364578352608435918260030b8303610361576020840192835260a435936fffffffffffffffffffffffffffffffff85168503610691576104336130f5565b61043b613114565b9561044461322a565b9561044f87336135a0565b1561066157916104bb916fffffffffffffffffffffffffffffffff93610498606089207f0000000000000000000000000000000000000000000000000000000000000000613630565b5050906104b56104ab885160030b6136df565b915160030b6136df565b91613c35565b9516916fffffffffffffffffffffffffffffffff86169280841061063157506f7fffffffffffffffffffffffffffffff83116106095750610605926105c99261059e6105ba93610584604051947fdd00000000000000000000000000000000000000000000000000000000000000602087015233604087015289606087015260808601906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60e084019060208091805160030b8452015160030b910152565b61012082015261012081526105b561014082612f39565b613d52565b602080825183010191016133ba565b906040519485948592936fffffffffffffffffffffffffffffffff80929695816060956080880199885216602087015216604085015216910152565b0390f35b807f1132981b0000000000000000000000000000000000000000000000000000000060049252fd5b60449250837f805338bc000000000000000000000000000000000000000000000000000000008352600452602452fd5b604485887fda47202300000000000000000000000000000000000000000000000000000000825233600452602452fd5b5080fd5b50346103615760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610361576106cd612dfb565b906106d6612e1e565b601c52670a5a2e7a000000006008525260206030600c20546040519015158152f35b50346103615760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261036157602061075a610735612dfb565b6024359060809160405191825260208201524660408201523060608201522060801c90565b604051908152f35b50346103615761077136613153565b919060608120907f00000000000000000000000000000000000000000000000000000000000000009160406107a68285613630565b5050948151906107b582612ed4565b8152306020820152818082019188835220885251206020526040862091604051926107df84612f1d565b87845260208401926040516107f381612f1d565b5f81525f60208201528452885260046020526040882090885260205273ffffffffffffffffffffffffffffffffffffffff6040882094169360018101808211610b24576002820190818311610af757604051927f380eb4e0000000000000000000000000000000000000000000000000000000008452600484015260248301526044820152606081606481885afa15610af35780516fffffffffffffffffffffffffffffffff6040602084015193015191168552604051916108b483612f1d565b8252602082015282526fffffffffffffffffffffffffffffffff835116946f80000000000000000000000000000000861015610ae6576109b460a488999361093c61092f6040958b9c61090c6109369d600f0b6134f6565b610929602061091e845160030b6136df565b93015160030b6136df565b9261409d565b9a906134f6565b996134f6565b976109ce85519c8d9586947f05d7e69400000000000000000000000000000000000000000000000000000000865260048601906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b606484019060208091805160030b8452015160030b910152565b5afa968715610370578197610a67575b5050916fffffffffffffffffffffffffffffffff80610a4781948280610a3b60a09c6020809b5191816040519d8e94610a1686612f1d565b5f86525f8287015280518451038096520151910151039b019a8b52828551169061464f565b1697519151169061464f565b169460405196875216602086015216604084015260608301526080820152f35b9096506040813d604011610ade575b81610a8360409383612f39565b81010312610ada576fffffffffffffffffffffffffffffffff610a4781948280610a3b602099978a839860a09f508160405191610abf83612f1d565b805183520151828201529e50509799505050509450506109de565b8680fd5b3d9150610a76565b6335278d1288526004601cfd5b8780fd5b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b50346103615760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610361576040517f210fa96b0000000000000000000000000000000000000000000000000000000081526004356004820152818160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115610c8a578291610c09575b604051806106058482612db3565b90503d8083833e610c1a8183612f39565b8101906020818303126103645780519067ffffffffffffffff8211610c86570181601f8201121561036457805190610c51826131f0565b92610c5f6040519485612f39565b82845260208383010111610c865781610605949260208093018386015e830101525f610bfb565b8380fd5b6040513d84823e3d90fd5b5060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261036157610cc8612dfb565b610cd0612e1e565b6044356064359167ffffffffffffffff8311610d655736602384011215610d655782600401359367ffffffffffffffff8511610d61573660248686010111610d6157610d1d838383613240565b85823b610d275780f35b6020610d5896806024610d39826131f0565b98610d476040519a8b612f39565b828a52018389013786010152613e27565b5f808080808580f35b8580fd5b8480fd5b346110df5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036114f857366024116110df577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601610e09816131f0565b610e166040519182612f39565b8181526020810191602483375f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36830101528051606090156114cb5782517fff00000000000000000000000000000000000000000000000000000000000000167fdd0000000000000000000000000000000000000000000000000000000000000081036110e3575050805181019061012081602084019303126110df57610ec0610f7293613ee1565b506040610ece818301613f0e565b6fffffffffffffffffffffffffffffffff60608401519173ffffffffffffffffffffffffffffffffffffffff610f21610120610f1a610f108a60808b01613f2f565b9960e08a01613f7d565b970161339d565b911694845193610f3085612ed4565b8452602084015216600f0b828201528151809581927f55f48d010000000000000000000000000000000000000000000000000000000083528660048401613fe2565b03815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156110d4575f935f9361107b575b50611073929173ffffffffffffffffffffffffffffffffffffffff6020836110008361101c9651166fffffffffffffffffffffffffffffffff8a16908661431a565b0151166fffffffffffffffffffffffffffffffff84169161431a565b604080516fffffffffffffffffffffffffffffffff9485166020820152939091169083015281606081015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282612f39565b602081519101f35b6020945061107393509173ffffffffffffffffffffffffffffffffffffffff6110bf61101c939460403d6040116110cd575b6110b78183612f39565b810190613fc1565b969096955050509190610fbe565b503d6110ad565b6040513d5f823e3d90fd5b5f80fd5b919290917fff0000000000000000000000000000000000000000000000000000000000000081036114a05750825183019061014084602084019303126110df5761112c90613ee1565b5061114b611141826040860151948601613f2f565b9160c08501613f7d565b90611159610100850161339d565b9261014061116a6101208701613f0e565b9501519485151586036110df5773ffffffffffffffffffffffffffffffffffffffff16925f945f96611385575b6fffffffffffffffffffffffffffffffff1680611221575b505050818473ffffffffffffffffffffffffffffffffffffffff6020846111e1889761121c99856111e999511661424d565b01511661424d565b611047604051938492602084019092916fffffffffffffffffffffffffffffffff60209181604085019616845216910152565b611073565b9160409161123761128597989694600f0b6134f6565b9083519261124484612ed4565b83526020830152600f0b828201528151809681927f55f48d010000000000000000000000000000000000000000000000000000000083528660048401613fe2565b03815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156110d45760209561121c955f905f95611332575b506111e9949561130661131e73ffffffffffffffffffffffffffffffffffffffff94611306611324956134f6565b6fffffffffffffffffffffffffffffffff169061406b565b976134f6565b9650829495508193506111af565b6111e9955061132491509561130661131e73ffffffffffffffffffffffffffffffffffffffff946113746113069a60403d6040116110cd576110b78183612f39565b9a909a9950509450505095906112d8565b604080517f645ec9b5000000000000000000000000000000000000000000000000000000008152855173ffffffffffffffffffffffffffffffffffffffff9081166004830152602087015116602482015290850151604482015296509450816064870152611406608487018260208091805160030b8452015160030b910152565b60408660c4815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af180156110d4575f965f9161145d575b509594611197565b6fffffffffffffffffffffffffffffffff9750611492915060403d604011611499575b61148a8183612f39565b8101906133ba565b9096611455565b503d611480565b7fbcafffd0000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f3ed2a9c0000000000000000000000000000000000000000000000000000000005f5260045ffd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57602061075a60043561342a565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5760043567ffffffffffffffff81116110df57366023820112156110df57806004013567ffffffffffffffff81116110df578060051b36602482850101116110df576040519283926020845280846020015260408481019380602485018637850101928391611607575b505050806040520360401b178060401c9067ffffffffffffffff16f35b919350915b80915f6044825187016024810135918291018537389084305af41561169357602067ffffffffffffffe0603f5f937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08a87030181528301943d81523d858583013e3d010116933d010152848382101561168657509061160c565b93505090508380806115ea565b853d5f823e3d90fd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df576116ce612dfb565b60243590805f52336020527f2ea13d3f0340a613d1765d6e239004eca4cb7efa2e253d1e113c4d333b8db7c860405f20015c8261170b3384613552565b91808310928315611765575b50505061172057005b73ffffffffffffffffffffffffffffffffffffffff907ff9e94710000000000000000000000000000000000000000000000000000000005f521660045260245260445ffd5b03109050828480611717565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df576004358042116117a757005b7f6634e923000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346110df5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57611809612dfb565b602435908115158092036110df5773ffffffffffffffffffffffffffffffffffffffff9181601c52670a5a2e7a00000000600852335f52806030600c20555f5216337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa3005b346110df575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b6101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5761191436612f7a565b61191d3661309a565b906119266130f5565b9061192f613114565b92611938613133565b9361194460043561342a565b9361194f85336135a0565b15611ad5576fffffffffffffffffffffffffffffffff916119b691611997606087207f0000000000000000000000000000000000000000000000000000000000000000613630565b50506119a6865160030b6136df565b6104b5602088015160030b6136df565b9416906fffffffffffffffffffffffffffffffff851691808310611aa657506f7fffffffffffffffffffffffffffffff8211611a7e57610605926105c99261059e6105ba93610584604051947fdd00000000000000000000000000000000000000000000000000000000000000602087015233604087015289606087015260808601906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b7f1132981b000000000000000000000000000000000000000000000000000000005f5260045ffd5b827f805338bc000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b847fda472023000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57600435611b3b36612f7a565b611b443661309a565b60c4359173ffffffffffffffffffffffffffffffffffffffff83168093036110df57611b7084336135a0565b15611c7257611b7f84336135a0565b15611c725791611c27611c4d9492611c0d6105ba95604051957fff000000000000000000000000000000000000000000000000000000000000006020880152604087015260608601906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60c084019060208091805160030b8452015160030b910152565b5f610100830152610120820152600161014082015261014081526105b561016082612f39565b604080516fffffffffffffffffffffffffffffffff9384168152919092166020820152f35b837fda472023000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b346110df575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57610605604051611ce1604082612f39565b600581527f656b75506f000000000000000000000000000000000000000000000000000000602082015260405191829182612db3565b60c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57611d49612dfb565b60243560443560643560ff811681036110df576084359060a4355f73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2871860601b153360601b908460ff169015611e7a575b505015611d9857005b6040519263927da10584523360208501528673ffffffffffffffffffffffffffffffffffffffff16604085015260608401943086523060c08601528660a01c156e22d473030f116ddee9f6b43ac78ba30296601c86019660608160648a8c5afa605f3d111615611e6357632b67b57087525265ffffffffffff608086015260e085015261010080850152604161012085015261014084015261016083015260f81b610180909101525f9138916101849184905af1903b0215611e5657005b636b836e6b5f526004601cfd5b600489676b836e6b8757f0fd5f521560021b601801fd5b633644e5155f5260205f6004601c8c611388fa60203d145f5115101615611f7a575f92506040519130603484015260208301528660748301527fdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7835114611f17576fd505accf000000000000000000000000825287605483015260948201528460b48201528260d482015260e46010389201838a5af18780611d8f565b336014526f7ecebe000000000000000000000000008352602060548301602460108c5afa88151060948301526f8fcbaf0c000000000000000000000000825260b48201528460d48201528260f48201526101046010389201838a5af18780611d8f565b611d8f565b346110df5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57611fb6612dfb565b8015611ff8577f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c525f52602063ffffffff601c600c205416604051908152f35b638f4eb6045f526004601cfd5b346110df5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5760206120416004356133db565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346110df5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57612096612e1e565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016908133036114f8576064359160405191836060528160405260443560601b80602c526f23b872dd000000000000000000000000600c5260205f6064601c82865af18060015f511416156121d2575b5f606052846040521561212a57005b5f9273ffffffffffffffffffffffffffffffffffffffff83166074860152856054860152603485015260208401526f36c785160000000000000000000000008352600146149081156121b8575b3b15109160846010389201836e22d473030f116ddee9f6b43ac78ba35af1161561219d57005b600490677939f4248757f0fd5f5260a01c151560021b601801fd5b6e22d473030f116ddee9f6b43ac78ba33b15159150612177565b3d833b15171061211b565b6121e636613153565b906121f183336135a0565b156122dd5761220083336135a0565b156122dd5761220f83336135a0565b156122dd57916122b66105ba9261229c611c4d95604051947fff000000000000000000000000000000000000000000000000000000000000006020870152604086015260608501906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60c083019060208091805160030b8452015160030b910152565b5f61010082015233610120820152600161014082015261014081526105b561016082612f39565b827fda472023000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b6101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5760043561234436612f7a565b61234d3661309a565b6123556130f5565b60e4359173ffffffffffffffffffffffffffffffffffffffff83168093036110df5761010435938415158095036110df5761239086336135a0565b1561246f576105ba949261244b611c4d9795936124316fffffffffffffffffffffffffffffffff94604051987fff0000000000000000000000000000000000000000000000000000000000000060208b015260408a015260608901906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60c087019060208091805160030b8452015160030b910152565b1661010084015261012083015261014082015261014081526105b561016082612f39565b857fda472023000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df576004356124d536612f7a565b6124de3661309a565b6124e66130f5565b916124f184336135a0565b15611c72576105ba926125aa611c4d95936125906fffffffffffffffffffffffffffffffff94604051967fff000000000000000000000000000000000000000000000000000000000000006020890152604088015260608701906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60c085019060208091805160030b8452015160030b910152565b1661010082015233610120820152600161014082015261014081526105b561016082612f39565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5760043561260881336135a0565b1561271057612616816133db565b50805f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f208101810180549073ffffffffffffffffffffffffffffffffffffffff821690811561270357815f52806001019283548015600117156126e4575b905f9484926126db575b50189055601c600c207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81540190557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4005b85905586612686565b906030600c2054156126f6579061267c565b634b6e7f185f526004601cfd5b63ceea21b65f526004601cfd5b7fda472023000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b61274836612e62565b6127558183859495613240565b823b61275d57005b6127789260405192612770602085612f39565b5f8452613e27565b005b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57476127aa57005b6127784733613e0b565b6101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df576004356127eb36612f7a565b906127f53661309a565b6127fd6130f5565b91612806613114565b61280e613133565b9361281983336135a0565b156122dd576128816fffffffffffffffffffffffffffffffff92839261286260608a207f0000000000000000000000000000000000000000000000000000000000000000613630565b5050612871885160030b6136df565b6104b560208a015160030b6136df565b941693169280841061298f57506f7fffffffffffffffffffffffffffffff8311611a7e576129796105ba6fffffffffffffffffffffffffffffffff93926129618594612947606099604051947fdd0000000000000000000000000000000000000000000000000000000000000060208701523360408701528b86015260808501906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60e083019060208091805160030b8452015160030b910152565b8561012082015261012081526105b561014082612f39565b9190604051948552166020840152166040820152f35b837f805338bc000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df576129f0612dfb565b805f5233602052612a287f2ea13d3f0340a613d1765d6e239004eca4cb7efa2e253d1e113c4d333b8db7c860405f2001913390613552565b905d005b612778612a3836612e62565b91613240565b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57602061075a61322a565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57612aa3612dfb565b60243590805f52336020527f2ea13d3f0340a613d1765d6e239004eca4cb7efa2e253d1e113c4d333b8db7c860405f20015c82612ae03384613552565b8281109283612b38575b505050612af357005b73ffffffffffffffffffffffffffffffffffffffff907ffef2c60e000000000000000000000000000000000000000000000000000000005f521660045260245260445ffd5b03119050828480612aea565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57612b76612dfb565b6024359073ffffffffffffffffffffffffffffffffffffffff16815f52337f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260205f2082018201805473ffffffffffffffffffffffffffffffffffffffff1690811561270357829082331433151715612c18575b600101557f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b9050815f526030600c2054156126f6578290612bee565b346110df5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57600435805f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f20810101805460601b15612703576020906001015473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346110df575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57610605604051612cfd604082612f39565b600f81527f456b75626f20506f736974696f6e730000000000000000000000000000000000602082015260405191829182612db3565b346110df5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57600435907fffffffff00000000000000000000000000000000000000000000000000000000821682036110df5760209160e01c635b5e139f8114906301ffc9a76380ac58cd82149114171715158152f35b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602060409481855280519182918282880152018686015e5f8582860101520116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036110df57565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036110df57565b359073ffffffffffffffffffffffffffffffffffffffff821682036110df57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126110df5760043573ffffffffffffffffffffffffffffffffffffffff811681036110df579060243573ffffffffffffffffffffffffffffffffffffffff811681036110df579060443590565b6060810190811067ffffffffffffffff821117612ef057604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff821117612ef057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612ef057604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc60609101126110df5760405190612fb182612ed4565b8160243573ffffffffffffffffffffffffffffffffffffffff811681036110df57815260443573ffffffffffffffffffffffffffffffffffffffff811681036110df5760208201526040606435910152565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126110df576040519061303a82612ed4565b8160043573ffffffffffffffffffffffffffffffffffffffff811681036110df57815260243573ffffffffffffffffffffffffffffffffffffffff811681036110df5760208201526040604435910152565b35908160030b82036110df57565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c60409101126110df57604051906130d182612f1d565b816084358060030b81036110df57815260a435908160030b82036110df5760200152565b60c435906fffffffffffffffffffffffffffffffff821682036110df57565b60e435906fffffffffffffffffffffffffffffffff821682036110df57565b61010435906fffffffffffffffffffffffffffffffff821682036110df57565b60c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126110df5760043591606060248303126110df5760406084815161319b81612ed4565b6131a56024612e41565b81526131b16044612e41565b60208201528260240135838201529303126110df576040516131d281612f1d565b6131dc608461308c565b81526131e860a461308c565b602082015290565b67ffffffffffffffff8111612ef057601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b445f525a60205261323d60405f2061342a565b90565b73ffffffffffffffffffffffffffffffffffffffff169073ffffffffffffffffffffffffffffffffffffffff1690825f52337f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260205f208301830180548073ffffffffffffffffffffffffffffffffffffffff168381148102156133885750825f528160010180548033148533141715613371575b613368575b50838318189055601c600c207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8154019055815f52601c600c2060018154019063ffffffff821684021561335357557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4565b67ea553b3401336cea841560021b526004601cfd5b5f90555f6132de565b6030600c20546132d957634b6e7f185f526004601cfd5b67ceea21b6a1148100901560021b526004601cfd5b51906fffffffffffffffffffffffffffffffff821682036110df57565b91908260409103126110df5761323d60206133d48461339d565b930161339d565b73ffffffffffffffffffffffffffffffffffffffff90805f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f20810101541690811561270357565b613450903360809160405191825260208201524660408201523060608201522060801c90565b90815f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f208201820180548060601b6134e95733179055335f52601c600c2060018154019063ffffffff82163302156134d4575581335f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4565b67ea553b3401336cea331560021b526004601cfd5b63c991cbb15f526004601cfd5b600f0b7fffffffffffffffffffffffffffffffff800000000000000000000000000000008114613525575f0390565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b73ffffffffffffffffffffffffffffffffffffffff811661357257503190565b602460106020939284936014526f70a082310000000000000000000000005f525afa601f3d11166020510290565b919073ffffffffffffffffffffffffffffffffffffffff6001931690805f52817f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260205f2081010173ffffffffffffffffffffffffffffffffffffffff81541680156127035780830361361657505050565b5f526030600c205415613627575050565b60010154149150565b60245f73ffffffffffffffffffffffffffffffffffffffff8193602095835260028652604083207f380eb4e0000000000000000000000000000000000000000000000000000000008452600452165afa156110df575f516bffffffffffffffffffffffff81169163ffffffff8260601c169160801c90565b81156136b2570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b60030b8060ff1d8082011890630549cd938211613c0a576d08637b66cd638344daef276cd7c56001831602700100000000000000000000000000000000039160028116613bee575b60048116613bd2575b60088116613bb6575b60108116613b9a575b60208116613b7e575b60408116613b62575b60808116613b46575b6101008116613b2a575b6102008116613b0e575b6104008116613af2575b6108008116613ad6575b6110008116613aba575b6120008116613a9e575b6140008116613a82575b6180008116613a66575b620100008116613a4a575b620200008116613a2e575b620400008116613a12575b6208000081166139f6575b6210000081166139da575b6220000081166139be575b6240000081166139a2575b628000008116613986575b6301000000811661396b575b63020000008116613951575b63040000001661393a575b5f1261390d575b6c0100000000000000000000000081106001146139075770010000000000000000000000000000000081106001146138f3577401000000000000000000000000000000000000000081106001146138df57780100000000000000000000000000000000000000000000000081106001146138cb577fa10459f4000000000000000000000000000000000000000000000000000000005f5260045ffd5b60621c6bc000000000000000000000001790565b60421c6b8000000000000000000000001790565b60221c6b4000000000000000000000001790565b60021c90565b80156136b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0461382f565b69c0d55d4d7152c25fb13990910260801c90613828565b6cde2ee4bc381afa7089aa84bb6690920260801c9161381d565b916e0ee7e32d61fdb0a5e622b820f681d00260801c91613811565b916f03dc5dac7376e20fc8679758d1bcdcfc0260801c91613805565b916f1f703399d88f6aa83a28b22d4a1f56e30260801c916137fa565b916f59b63684b86e9f486ec54727371ba6ca0260801c916137ef565b916f978bcb9894317807e5fa4498eee7c0fa0260801c916137e4565b916fc4f76b68947482dc198a48a54348c4ed0260801c916137d9565b916fe08d35706200796273f0b3a981d90cfd0260801c916137ce565b916fefc2bf59df33ecc28125cf78ec4f167f0260801c916137c3565b916ff7bf5211c72f5185f372aeb1d48f937e0260801c916137b8565b916ffbd701c7cbc4c8a6bb81efd232d1e4e70260801c916137ad565b916ffde95287d26d81bea159c37073122c730260801c916137a3565b916ffef41d1a5f2ae3a20676bec6f7f9459a0260801c91613799565b916fff79eb706b9a64c6431d76e63531e9290260801c9161378f565b916fffbceceeb791747f10df216f2e53ec570260801c91613785565b916fffde7444b28145508125d10077ba83b80260801c9161377b565b916fffef3995a5b6a6267530f207142a57640260801c91613771565b916ffff79ca7a4d1bf1ee8556cea23cdbaa50260801c91613767565b916ffffbce4b06c196e9247ac87695d53c600260801c9161375d565b916ffffde72350725cc4ea8feece3b5f13c80260801c91613754565b916ffffef3911b7cff24ba1b3dbb5f8f59740260801c9161374b565b916fffff79c86a8f6150a32d9778eceef97c0260801c91613742565b916fffffbce42c7be6c998ad6318193c0b180260801c91613739565b916fffffde72140b00a354bd3dc828e976c90260801c91613730565b916fffffef390978c398134b4ff3764fe4100260801c91613727565b7f073ee172000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6fffffffffffffffffffffffffffffffff94919392613c82613c889260607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b946141d0565b939091828211613cba5750509183613cad93613ca4838261464f565b920392166146b4565b8181109082180281181690565b9290938085105f14613d1e5791613cfd7fffffffffffffffffffffffffffffffff0000000000000000000000000000000092613d0995948888613ca4838261464f565b94039160801b166136a8565b81811090821802188181109082180281181690565b613cad94507fffffffffffffffffffffffffffffffff00000000000000000000000000000000929150039160801b166136a8565b905f8060046020604051957ff83d08ba0000000000000000000000000000000000000000000000000000000087528051918291018388015e01848273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af115613e02573d82523d5f602084013e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f3d84010116604052565b503d5f823e3d90fd5b5f80809338935af115613e1a57565b63b12d13eb5f526004601cfd5b9060a473ffffffffffffffffffffffffffffffffffffffff9460209460405196879563150b7a0287523388880152166040860152606085015260808085015280518091818060a0880152613ecd575b505001905f601c8401915af115613ebe575b517feaf485fe0000000000000000000000000000000000000000000000000000000001613eb157565b63d1a57ed65f526004601cfd5b3d15613e88575b3d5f823e3d90fd5b818760c08801920160045afa50805f613e76565b51907fff00000000000000000000000000000000000000000000000000000000000000821682036110df57565b519073ffffffffffffffffffffffffffffffffffffffff821682036110df57565b91908260609103126110df57604051613f4781612ed4565b6040808294613f5581613f0e565b8452613f6360208201613f0e565b60208501520151910152565b51908160030b82036110df57565b91908260409103126110df57604051613f9581612f1d565b6020613fae818395613fa681613f6f565b855201613f6f565b910152565b519081600f0b82036110df57565b91908260409103126110df5761323d6020613fdb84613fb3565b9301613fb3565b909291604060c0916140388460e08101976040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b805160608501526140616020820151608086019060208091805160030b8452015160030b910152565b0151600f0b910152565b906fffffffffffffffffffffffffffffffff809116911601906fffffffffffffffffffffffffffffffff821161352557565b90925f905f94600f0b9384156141c4575f8513936fffffffffffffffffffffffffffffffff85157ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02600118968060ff1d8091011816936bffffffffffffffffffffffff82166bffffffffffffffffffffffff84168111155f1461414a57505050926fffffffffffffffffffffffffffffffff9261413f926141469695614511565b16026144e3565b915b565b929897509092916bffffffffffffffffffffffff831611156141a0575092828261419a6fffffffffffffffffffffffffffffffff61419285829861323d9b9a61413f99614511565b1687026144e3565b98614401565b9661413f92509261323d9594916fffffffffffffffffffffffffffffffff94614401565b5050505050505f905f90565b61420861423a9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9081811881808411820281189310021891565b906fffffffffffffffffffffffffffffffff168061426a57505050565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b156110df575f928360649273ffffffffffffffffffffffffffffffffffffffff948560405198899788967f03a65ab600000000000000000000000000000000000000000000000000000000885216600487015216602485015260448401525af180156110d4576143105750565b5f61414891612f39565b9190918161432757505050565b73ffffffffffffffffffffffffffffffffffffffff83166143845750614148915073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016613e0b565b604051927f0c11dedd0000000000000000000000000000000000000000000000000000000084526004840152602483015260448201525f806064838273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af115613ec55750565b9061440b916141d0565b03911561449a57806144416fffffffffffffffffffffffffffffffff70010000000000000000000000000000000093168461464f565b92091515016fffffffffffffffffffffffffffffffff8111614472576fffffffffffffffffffffffffffffffff1690565b7f59d2b24a000000000000000000000000000000000000000000000000000000005f5260045ffd5b906fffffffffffffffffffffffffffffffff6144b792169061464f565b6fffffffffffffffffffffffffffffffff8111614472576fffffffffffffffffffffffffffffffff1690565b806f800000000000000000000000000000000160801c1561450b576335278d125f526004601cfd5b600f0b90565b9061451e919392936141d0565b929091156145e85760801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000169181810361455a8282866146b4565b93096145d0575b80156145c35780820491061515016fffffffffffffffffffffffffffffffff811161459b576fffffffffffffffffffffffffffffffff1690565b7fb4ef2546000000000000000000000000000000000000000000000000000000005f5260045ffd5b6365244e4e5f526004601cfd5b906001019081614561575b63ae47f7025f526004601cfd5b8261461e917fffffffffffffffffffffffffffffffff000000000000000000000000000000008461462396039160801b166146b4565b6136a8565b6fffffffffffffffffffffffffffffffff811161459b576fffffffffffffffffffffffffffffffff1690565b8181029180828404148215171561466857505060801c90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff910981811082019003908160801c156146a95763ae47f7025f526004601cfd5b60801c9060801b0190565b818102929181158285048214178302156146cf575050900490565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8492840985811086019003920990825f03831692818111156145db5783900480600302600218808202600203028082026002030280820260020302808202600203028082026002030280910260020302936001848483030494805f030401921190030217029056fea26469706673582212202dcb46da28868b53365e52a05f6bbe498d38071868b159f39ba8ebf8819ad5c864736f6c634300081c0033000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444000000000000000000000000ecdbfba64c900bdc249676fd691d4900c513c68c

Deployed Bytecode

0x6080806040526004361015610012575f80fd5b5f905f3560e01c90816301ffc9a714612d335750806306fdde0314612cbe578063081812fc14612c2f578063095ea7b314612b445780630a52f5f114612a715780631249c58b14612a3e57806323b872dd14612a2c57806327c3a090146129be57806330460039146127b4578063418652701461277a57806342842e0e1461273f57806342966c68146125d157806343dce5301461249f5780634d5980b51461230d57806357b94f41146121dd578063599d07141461205f5780636352211e1461200557806370a0823114611f7f5780637ac2ff7b14611d1757806395d89b4114611ca2578063974bafe314611b055780639df3913d146118e0578063a143c66f14611872578063a22cb465146117d2578063a699094614611771578063ac4563de1461169c578063ac9650d814611557578063adf2cead14611520578063b45a3c0e14610d69578063b88d4fde14610c95578063c87b56dd14610b51578063d0523d0c14610762578063daaa7887146106f8578063e985e9c514610695578063f1695e8e1461037d5763f3e32468146101aa575f80fd5b60807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610361576101dd36613003565b90606435908160030b8092036103615780917f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d44461021d6060862082613630565b5050928315610246575b6040805186151581526bffffffffffffffffffffffff86166020820152f35b92509250926102e160846020928473ffffffffffffffffffffffffffffffffffffffff600197604051998a9687957fc053024400000000000000000000000000000000000000000000000000000000875260048701906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b6064850152165af1928315610370578193610311575b50506bffffffffffffffffffffffff604092905f80610227565b9092506020813d602011610368575b8161032d60209383612f39565b810103126103645751916bffffffffffffffffffffffff8316830361036157506bffffffffffffffffffffffff60406102f7565b80fd5b8280fd5b3d9150610320565b50604051903d90823e3d90fd5b506101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610361576103b236613003565b9060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c36011261036157604051916103ea83612f1d565b6064358060030b8103610364578352608435918260030b8303610361576020840192835260a435936fffffffffffffffffffffffffffffffff85168503610691576104336130f5565b61043b613114565b9561044461322a565b9561044f87336135a0565b1561066157916104bb916fffffffffffffffffffffffffffffffff93610498606089207f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444613630565b5050906104b56104ab885160030b6136df565b915160030b6136df565b91613c35565b9516916fffffffffffffffffffffffffffffffff86169280841061063157506f7fffffffffffffffffffffffffffffff83116106095750610605926105c99261059e6105ba93610584604051947fdd00000000000000000000000000000000000000000000000000000000000000602087015233604087015289606087015260808601906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60e084019060208091805160030b8452015160030b910152565b61012082015261012081526105b561014082612f39565b613d52565b602080825183010191016133ba565b906040519485948592936fffffffffffffffffffffffffffffffff80929695816060956080880199885216602087015216604085015216910152565b0390f35b807f1132981b0000000000000000000000000000000000000000000000000000000060049252fd5b60449250837f805338bc000000000000000000000000000000000000000000000000000000008352600452602452fd5b604485887fda47202300000000000000000000000000000000000000000000000000000000825233600452602452fd5b5080fd5b50346103615760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610361576106cd612dfb565b906106d6612e1e565b601c52670a5a2e7a000000006008525260206030600c20546040519015158152f35b50346103615760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261036157602061075a610735612dfb565b6024359060809160405191825260208201524660408201523060608201522060801c90565b604051908152f35b50346103615761077136613153565b919060608120907f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d4449160406107a68285613630565b5050948151906107b582612ed4565b8152306020820152818082019188835220885251206020526040862091604051926107df84612f1d565b87845260208401926040516107f381612f1d565b5f81525f60208201528452885260046020526040882090885260205273ffffffffffffffffffffffffffffffffffffffff6040882094169360018101808211610b24576002820190818311610af757604051927f380eb4e0000000000000000000000000000000000000000000000000000000008452600484015260248301526044820152606081606481885afa15610af35780516fffffffffffffffffffffffffffffffff6040602084015193015191168552604051916108b483612f1d565b8252602082015282526fffffffffffffffffffffffffffffffff835116946f80000000000000000000000000000000861015610ae6576109b460a488999361093c61092f6040958b9c61090c6109369d600f0b6134f6565b610929602061091e845160030b6136df565b93015160030b6136df565b9261409d565b9a906134f6565b996134f6565b976109ce85519c8d9586947f05d7e69400000000000000000000000000000000000000000000000000000000865260048601906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b606484019060208091805160030b8452015160030b910152565b5afa968715610370578197610a67575b5050916fffffffffffffffffffffffffffffffff80610a4781948280610a3b60a09c6020809b5191816040519d8e94610a1686612f1d565b5f86525f8287015280518451038096520151910151039b019a8b52828551169061464f565b1697519151169061464f565b169460405196875216602086015216604084015260608301526080820152f35b9096506040813d604011610ade575b81610a8360409383612f39565b81010312610ada576fffffffffffffffffffffffffffffffff610a4781948280610a3b602099978a839860a09f508160405191610abf83612f1d565b805183520151828201529e50509799505050509450506109de565b8680fd5b3d9150610a76565b6335278d1288526004601cfd5b8780fd5b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b50346103615760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610361576040517f210fa96b0000000000000000000000000000000000000000000000000000000081526004356004820152818160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ecdbfba64c900bdc249676fd691d4900c513c68c165afa908115610c8a578291610c09575b604051806106058482612db3565b90503d8083833e610c1a8183612f39565b8101906020818303126103645780519067ffffffffffffffff8211610c86570181601f8201121561036457805190610c51826131f0565b92610c5f6040519485612f39565b82845260208383010111610c865781610605949260208093018386015e830101525f610bfb565b8380fd5b6040513d84823e3d90fd5b5060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261036157610cc8612dfb565b610cd0612e1e565b6044356064359167ffffffffffffffff8311610d655736602384011215610d655782600401359367ffffffffffffffff8511610d61573660248686010111610d6157610d1d838383613240565b85823b610d275780f35b6020610d5896806024610d39826131f0565b98610d476040519a8b612f39565b828a52018389013786010152613e27565b5f808080808580f35b8580fd5b8480fd5b346110df5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d4441633036114f857366024116110df577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601610e09816131f0565b610e166040519182612f39565b8181526020810191602483375f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36830101528051606090156114cb5782517fff00000000000000000000000000000000000000000000000000000000000000167fdd0000000000000000000000000000000000000000000000000000000000000081036110e3575050805181019061012081602084019303126110df57610ec0610f7293613ee1565b506040610ece818301613f0e565b6fffffffffffffffffffffffffffffffff60608401519173ffffffffffffffffffffffffffffffffffffffff610f21610120610f1a610f108a60808b01613f2f565b9960e08a01613f7d565b970161339d565b911694845193610f3085612ed4565b8452602084015216600f0b828201528151809581927f55f48d010000000000000000000000000000000000000000000000000000000083528660048401613fe2565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444165af19182156110d4575f935f9361107b575b50611073929173ffffffffffffffffffffffffffffffffffffffff6020836110008361101c9651166fffffffffffffffffffffffffffffffff8a16908661431a565b0151166fffffffffffffffffffffffffffffffff84169161431a565b604080516fffffffffffffffffffffffffffffffff9485166020820152939091169083015281606081015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282612f39565b602081519101f35b6020945061107393509173ffffffffffffffffffffffffffffffffffffffff6110bf61101c939460403d6040116110cd575b6110b78183612f39565b810190613fc1565b969096955050509190610fbe565b503d6110ad565b6040513d5f823e3d90fd5b5f80fd5b919290917fff0000000000000000000000000000000000000000000000000000000000000081036114a05750825183019061014084602084019303126110df5761112c90613ee1565b5061114b611141826040860151948601613f2f565b9160c08501613f7d565b90611159610100850161339d565b9261014061116a6101208701613f0e565b9501519485151586036110df5773ffffffffffffffffffffffffffffffffffffffff16925f945f96611385575b6fffffffffffffffffffffffffffffffff1680611221575b505050818473ffffffffffffffffffffffffffffffffffffffff6020846111e1889761121c99856111e999511661424d565b01511661424d565b611047604051938492602084019092916fffffffffffffffffffffffffffffffff60209181604085019616845216910152565b611073565b9160409161123761128597989694600f0b6134f6565b9083519261124484612ed4565b83526020830152600f0b828201528151809681927f55f48d010000000000000000000000000000000000000000000000000000000083528660048401613fe2565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444165af19182156110d45760209561121c955f905f95611332575b506111e9949561130661131e73ffffffffffffffffffffffffffffffffffffffff94611306611324956134f6565b6fffffffffffffffffffffffffffffffff169061406b565b976134f6565b9650829495508193506111af565b6111e9955061132491509561130661131e73ffffffffffffffffffffffffffffffffffffffff946113746113069a60403d6040116110cd576110b78183612f39565b9a909a9950509450505095906112d8565b604080517f645ec9b5000000000000000000000000000000000000000000000000000000008152855173ffffffffffffffffffffffffffffffffffffffff9081166004830152602087015116602482015290850151604482015296509450816064870152611406608487018260208091805160030b8452015160030b910152565b60408660c4815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444165af180156110d4575f965f9161145d575b509594611197565b6fffffffffffffffffffffffffffffffff9750611492915060403d604011611499575b61148a8183612f39565b8101906133ba565b9096611455565b503d611480565b7fbcafffd0000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f3ed2a9c0000000000000000000000000000000000000000000000000000000005f5260045ffd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57602061075a60043561342a565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5760043567ffffffffffffffff81116110df57366023820112156110df57806004013567ffffffffffffffff81116110df578060051b36602482850101116110df576040519283926020845280846020015260408481019380602485018637850101928391611607575b505050806040520360401b178060401c9067ffffffffffffffff16f35b919350915b80915f6044825187016024810135918291018537389084305af41561169357602067ffffffffffffffe0603f5f937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08a87030181528301943d81523d858583013e3d010116933d010152848382101561168657509061160c565b93505090508380806115ea565b853d5f823e3d90fd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df576116ce612dfb565b60243590805f52336020527f2ea13d3f0340a613d1765d6e239004eca4cb7efa2e253d1e113c4d333b8db7c860405f20015c8261170b3384613552565b91808310928315611765575b50505061172057005b73ffffffffffffffffffffffffffffffffffffffff907ff9e94710000000000000000000000000000000000000000000000000000000005f521660045260245260445ffd5b03109050828480611717565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df576004358042116117a757005b7f6634e923000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346110df5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57611809612dfb565b602435908115158092036110df5773ffffffffffffffffffffffffffffffffffffffff9181601c52670a5a2e7a00000000600852335f52806030600c20555f5216337f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160205fa3005b346110df575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ecdbfba64c900bdc249676fd691d4900c513c68c168152f35b6101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5761191436612f7a565b61191d3661309a565b906119266130f5565b9061192f613114565b92611938613133565b9361194460043561342a565b9361194f85336135a0565b15611ad5576fffffffffffffffffffffffffffffffff916119b691611997606087207f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444613630565b50506119a6865160030b6136df565b6104b5602088015160030b6136df565b9416906fffffffffffffffffffffffffffffffff851691808310611aa657506f7fffffffffffffffffffffffffffffff8211611a7e57610605926105c99261059e6105ba93610584604051947fdd00000000000000000000000000000000000000000000000000000000000000602087015233604087015289606087015260808601906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b7f1132981b000000000000000000000000000000000000000000000000000000005f5260045ffd5b827f805338bc000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b847fda472023000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57600435611b3b36612f7a565b611b443661309a565b60c4359173ffffffffffffffffffffffffffffffffffffffff83168093036110df57611b7084336135a0565b15611c7257611b7f84336135a0565b15611c725791611c27611c4d9492611c0d6105ba95604051957fff000000000000000000000000000000000000000000000000000000000000006020880152604087015260608601906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60c084019060208091805160030b8452015160030b910152565b5f610100830152610120820152600161014082015261014081526105b561016082612f39565b604080516fffffffffffffffffffffffffffffffff9384168152919092166020820152f35b837fda472023000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b346110df575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57610605604051611ce1604082612f39565b600581527f656b75506f000000000000000000000000000000000000000000000000000000602082015260405191829182612db3565b60c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57611d49612dfb565b60243560443560643560ff811681036110df576084359060a4355f73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2871860601b153360601b908460ff169015611e7a575b505015611d9857005b6040519263927da10584523360208501528673ffffffffffffffffffffffffffffffffffffffff16604085015260608401943086523060c08601528660a01c156e22d473030f116ddee9f6b43ac78ba30296601c86019660608160648a8c5afa605f3d111615611e6357632b67b57087525265ffffffffffff608086015260e085015261010080850152604161012085015261014084015261016083015260f81b610180909101525f9138916101849184905af1903b0215611e5657005b636b836e6b5f526004601cfd5b600489676b836e6b8757f0fd5f521560021b601801fd5b633644e5155f5260205f6004601c8c611388fa60203d145f5115101615611f7a575f92506040519130603484015260208301528660748301527fdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7835114611f17576fd505accf000000000000000000000000825287605483015260948201528460b48201528260d482015260e46010389201838a5af18780611d8f565b336014526f7ecebe000000000000000000000000008352602060548301602460108c5afa88151060948301526f8fcbaf0c000000000000000000000000825260b48201528460d48201528260f48201526101046010389201838a5af18780611d8f565b611d8f565b346110df5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57611fb6612dfb565b8015611ff8577f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c525f52602063ffffffff601c600c205416604051908152f35b638f4eb6045f526004601cfd5b346110df5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5760206120416004356133db565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346110df5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57612096612e1e565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d44416908133036114f8576064359160405191836060528160405260443560601b80602c526f23b872dd000000000000000000000000600c5260205f6064601c82865af18060015f511416156121d2575b5f606052846040521561212a57005b5f9273ffffffffffffffffffffffffffffffffffffffff83166074860152856054860152603485015260208401526f36c785160000000000000000000000008352600146149081156121b8575b3b15109160846010389201836e22d473030f116ddee9f6b43ac78ba35af1161561219d57005b600490677939f4248757f0fd5f5260a01c151560021b601801fd5b6e22d473030f116ddee9f6b43ac78ba33b15159150612177565b3d833b15171061211b565b6121e636613153565b906121f183336135a0565b156122dd5761220083336135a0565b156122dd5761220f83336135a0565b156122dd57916122b66105ba9261229c611c4d95604051947fff000000000000000000000000000000000000000000000000000000000000006020870152604086015260608501906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60c083019060208091805160030b8452015160030b910152565b5f61010082015233610120820152600161014082015261014081526105b561016082612f39565b827fda472023000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b6101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5760043561234436612f7a565b61234d3661309a565b6123556130f5565b60e4359173ffffffffffffffffffffffffffffffffffffffff83168093036110df5761010435938415158095036110df5761239086336135a0565b1561246f576105ba949261244b611c4d9795936124316fffffffffffffffffffffffffffffffff94604051987fff0000000000000000000000000000000000000000000000000000000000000060208b015260408a015260608901906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60c087019060208091805160030b8452015160030b910152565b1661010084015261012083015261014082015261014081526105b561016082612f39565b857fda472023000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df576004356124d536612f7a565b6124de3661309a565b6124e66130f5565b916124f184336135a0565b15611c72576105ba926125aa611c4d95936125906fffffffffffffffffffffffffffffffff94604051967fff000000000000000000000000000000000000000000000000000000000000006020890152604088015260608701906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60c085019060208091805160030b8452015160030b910152565b1661010082015233610120820152600161014082015261014081526105b561016082612f39565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df5760043561260881336135a0565b1561271057612616816133db565b50805f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f208101810180549073ffffffffffffffffffffffffffffffffffffffff821690811561270357815f52806001019283548015600117156126e4575b905f9484926126db575b50189055601c600c207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81540190557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8280a4005b85905586612686565b906030600c2054156126f6579061267c565b634b6e7f185f526004601cfd5b63ceea21b65f526004601cfd5b7fda472023000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b61274836612e62565b6127558183859495613240565b823b61275d57005b6127789260405192612770602085612f39565b5f8452613e27565b005b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57476127aa57005b6127784733613e0b565b6101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df576004356127eb36612f7a565b906127f53661309a565b6127fd6130f5565b91612806613114565b61280e613133565b9361281983336135a0565b156122dd576128816fffffffffffffffffffffffffffffffff92839261286260608a207f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444613630565b5050612871885160030b6136df565b6104b560208a015160030b6136df565b941693169280841061298f57506f7fffffffffffffffffffffffffffffff8311611a7e576129796105ba6fffffffffffffffffffffffffffffffff93926129618594612947606099604051947fdd0000000000000000000000000000000000000000000000000000000000000060208701523360408701528b86015260808501906040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b60e083019060208091805160030b8452015160030b910152565b8561012082015261012081526105b561014082612f39565b9190604051948552166020840152166040820152f35b837f805338bc000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df576129f0612dfb565b805f5233602052612a287f2ea13d3f0340a613d1765d6e239004eca4cb7efa2e253d1e113c4d333b8db7c860405f2001913390613552565b905d005b612778612a3836612e62565b91613240565b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57602061075a61322a565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57612aa3612dfb565b60243590805f52336020527f2ea13d3f0340a613d1765d6e239004eca4cb7efa2e253d1e113c4d333b8db7c860405f20015c82612ae03384613552565b8281109283612b38575b505050612af357005b73ffffffffffffffffffffffffffffffffffffffff907ffef2c60e000000000000000000000000000000000000000000000000000000005f521660045260245260445ffd5b03119050828480612aea565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57612b76612dfb565b6024359073ffffffffffffffffffffffffffffffffffffffff16815f52337f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260205f2082018201805473ffffffffffffffffffffffffffffffffffffffff1690811561270357829082331433151715612c18575b600101557f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b9050815f526030600c2054156126f6578290612bee565b346110df5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57600435805f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f20810101805460601b15612703576020906001015473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346110df575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57610605604051612cfd604082612f39565b600f81527f456b75626f20506f736974696f6e730000000000000000000000000000000000602082015260405191829182612db3565b346110df5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110df57600435907fffffffff00000000000000000000000000000000000000000000000000000000821682036110df5760209160e01c635b5e139f8114906301ffc9a76380ac58cd82149114171715158152f35b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602060409481855280519182918282880152018686015e5f8582860101520116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036110df57565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036110df57565b359073ffffffffffffffffffffffffffffffffffffffff821682036110df57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126110df5760043573ffffffffffffffffffffffffffffffffffffffff811681036110df579060243573ffffffffffffffffffffffffffffffffffffffff811681036110df579060443590565b6060810190811067ffffffffffffffff821117612ef057604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff821117612ef057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612ef057604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc60609101126110df5760405190612fb182612ed4565b8160243573ffffffffffffffffffffffffffffffffffffffff811681036110df57815260443573ffffffffffffffffffffffffffffffffffffffff811681036110df5760208201526040606435910152565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126110df576040519061303a82612ed4565b8160043573ffffffffffffffffffffffffffffffffffffffff811681036110df57815260243573ffffffffffffffffffffffffffffffffffffffff811681036110df5760208201526040604435910152565b35908160030b82036110df57565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7c60409101126110df57604051906130d182612f1d565b816084358060030b81036110df57815260a435908160030b82036110df5760200152565b60c435906fffffffffffffffffffffffffffffffff821682036110df57565b60e435906fffffffffffffffffffffffffffffffff821682036110df57565b61010435906fffffffffffffffffffffffffffffffff821682036110df57565b60c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126110df5760043591606060248303126110df5760406084815161319b81612ed4565b6131a56024612e41565b81526131b16044612e41565b60208201528260240135838201529303126110df576040516131d281612f1d565b6131dc608461308c565b81526131e860a461308c565b602082015290565b67ffffffffffffffff8111612ef057601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b445f525a60205261323d60405f2061342a565b90565b73ffffffffffffffffffffffffffffffffffffffff169073ffffffffffffffffffffffffffffffffffffffff1690825f52337f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260205f208301830180548073ffffffffffffffffffffffffffffffffffffffff168381148102156133885750825f528160010180548033148533141715613371575b613368575b50838318189055601c600c207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8154019055815f52601c600c2060018154019063ffffffff821684021561335357557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4565b67ea553b3401336cea841560021b526004601cfd5b5f90555f6132de565b6030600c20546132d957634b6e7f185f526004601cfd5b67ceea21b6a1148100901560021b526004601cfd5b51906fffffffffffffffffffffffffffffffff821682036110df57565b91908260409103126110df5761323d60206133d48461339d565b930161339d565b73ffffffffffffffffffffffffffffffffffffffff90805f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f20810101541690811561270357565b613450903360809160405191825260208201524660408201523060608201522060801c90565b90815f527f7d8825530a5a2e7a000000000000000000000000000000000000000000000000601c5260205f208201820180548060601b6134e95733179055335f52601c600c2060018154019063ffffffff82163302156134d4575581335f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4565b67ea553b3401336cea331560021b526004601cfd5b63c991cbb15f526004601cfd5b600f0b7fffffffffffffffffffffffffffffffff800000000000000000000000000000008114613525575f0390565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b73ffffffffffffffffffffffffffffffffffffffff811661357257503190565b602460106020939284936014526f70a082310000000000000000000000005f525afa601f3d11166020510290565b919073ffffffffffffffffffffffffffffffffffffffff6001931690805f52817f7d8825530a5a2e7a00000000000000000000000000000000000000000000000017601c5260205f2081010173ffffffffffffffffffffffffffffffffffffffff81541680156127035780830361361657505050565b5f526030600c205415613627575050565b60010154149150565b60245f73ffffffffffffffffffffffffffffffffffffffff8193602095835260028652604083207f380eb4e0000000000000000000000000000000000000000000000000000000008452600452165afa156110df575f516bffffffffffffffffffffffff81169163ffffffff8260601c169160801c90565b81156136b2570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b60030b8060ff1d8082011890630549cd938211613c0a576d08637b66cd638344daef276cd7c56001831602700100000000000000000000000000000000039160028116613bee575b60048116613bd2575b60088116613bb6575b60108116613b9a575b60208116613b7e575b60408116613b62575b60808116613b46575b6101008116613b2a575b6102008116613b0e575b6104008116613af2575b6108008116613ad6575b6110008116613aba575b6120008116613a9e575b6140008116613a82575b6180008116613a66575b620100008116613a4a575b620200008116613a2e575b620400008116613a12575b6208000081166139f6575b6210000081166139da575b6220000081166139be575b6240000081166139a2575b628000008116613986575b6301000000811661396b575b63020000008116613951575b63040000001661393a575b5f1261390d575b6c0100000000000000000000000081106001146139075770010000000000000000000000000000000081106001146138f3577401000000000000000000000000000000000000000081106001146138df57780100000000000000000000000000000000000000000000000081106001146138cb577fa10459f4000000000000000000000000000000000000000000000000000000005f5260045ffd5b60621c6bc000000000000000000000001790565b60421c6b8000000000000000000000001790565b60221c6b4000000000000000000000001790565b60021c90565b80156136b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0461382f565b69c0d55d4d7152c25fb13990910260801c90613828565b6cde2ee4bc381afa7089aa84bb6690920260801c9161381d565b916e0ee7e32d61fdb0a5e622b820f681d00260801c91613811565b916f03dc5dac7376e20fc8679758d1bcdcfc0260801c91613805565b916f1f703399d88f6aa83a28b22d4a1f56e30260801c916137fa565b916f59b63684b86e9f486ec54727371ba6ca0260801c916137ef565b916f978bcb9894317807e5fa4498eee7c0fa0260801c916137e4565b916fc4f76b68947482dc198a48a54348c4ed0260801c916137d9565b916fe08d35706200796273f0b3a981d90cfd0260801c916137ce565b916fefc2bf59df33ecc28125cf78ec4f167f0260801c916137c3565b916ff7bf5211c72f5185f372aeb1d48f937e0260801c916137b8565b916ffbd701c7cbc4c8a6bb81efd232d1e4e70260801c916137ad565b916ffde95287d26d81bea159c37073122c730260801c916137a3565b916ffef41d1a5f2ae3a20676bec6f7f9459a0260801c91613799565b916fff79eb706b9a64c6431d76e63531e9290260801c9161378f565b916fffbceceeb791747f10df216f2e53ec570260801c91613785565b916fffde7444b28145508125d10077ba83b80260801c9161377b565b916fffef3995a5b6a6267530f207142a57640260801c91613771565b916ffff79ca7a4d1bf1ee8556cea23cdbaa50260801c91613767565b916ffffbce4b06c196e9247ac87695d53c600260801c9161375d565b916ffffde72350725cc4ea8feece3b5f13c80260801c91613754565b916ffffef3911b7cff24ba1b3dbb5f8f59740260801c9161374b565b916fffff79c86a8f6150a32d9778eceef97c0260801c91613742565b916fffffbce42c7be6c998ad6318193c0b180260801c91613739565b916fffffde72140b00a354bd3dc828e976c90260801c91613730565b916fffffef390978c398134b4ff3764fe4100260801c91613727565b7f073ee172000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6fffffffffffffffffffffffffffffffff94919392613c82613c889260607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b946141d0565b939091828211613cba5750509183613cad93613ca4838261464f565b920392166146b4565b8181109082180281181690565b9290938085105f14613d1e5791613cfd7fffffffffffffffffffffffffffffffff0000000000000000000000000000000092613d0995948888613ca4838261464f565b94039160801b166136a8565b81811090821802188181109082180281181690565b613cad94507fffffffffffffffffffffffffffffffff00000000000000000000000000000000929150039160801b166136a8565b905f8060046020604051957ff83d08ba0000000000000000000000000000000000000000000000000000000087528051918291018388015e01848273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444165af115613e02573d82523d5f602084013e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f3d84010116604052565b503d5f823e3d90fd5b5f80809338935af115613e1a57565b63b12d13eb5f526004601cfd5b9060a473ffffffffffffffffffffffffffffffffffffffff9460209460405196879563150b7a0287523388880152166040860152606085015260808085015280518091818060a0880152613ecd575b505001905f601c8401915af115613ebe575b517feaf485fe0000000000000000000000000000000000000000000000000000000001613eb157565b63d1a57ed65f526004601cfd5b3d15613e88575b3d5f823e3d90fd5b818760c08801920160045afa50805f613e76565b51907fff00000000000000000000000000000000000000000000000000000000000000821682036110df57565b519073ffffffffffffffffffffffffffffffffffffffff821682036110df57565b91908260609103126110df57604051613f4781612ed4565b6040808294613f5581613f0e565b8452613f6360208201613f0e565b60208501520151910152565b51908160030b82036110df57565b91908260409103126110df57604051613f9581612f1d565b6020613fae818395613fa681613f6f565b855201613f6f565b910152565b519081600f0b82036110df57565b91908260409103126110df5761323d6020613fdb84613fb3565b9301613fb3565b909291604060c0916140388460e08101976040809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff60208201511660208501520151910152565b805160608501526140616020820151608086019060208091805160030b8452015160030b910152565b0151600f0b910152565b906fffffffffffffffffffffffffffffffff809116911601906fffffffffffffffffffffffffffffffff821161352557565b90925f905f94600f0b9384156141c4575f8513936fffffffffffffffffffffffffffffffff85157ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02600118968060ff1d8091011816936bffffffffffffffffffffffff82166bffffffffffffffffffffffff84168111155f1461414a57505050926fffffffffffffffffffffffffffffffff9261413f926141469695614511565b16026144e3565b915b565b929897509092916bffffffffffffffffffffffff831611156141a0575092828261419a6fffffffffffffffffffffffffffffffff61419285829861323d9b9a61413f99614511565b1687026144e3565b98614401565b9661413f92509261323d9594916fffffffffffffffffffffffffffffffff94614401565b5050505050505f905f90565b61420861423a9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9081811881808411820281189310021891565b906fffffffffffffffffffffffffffffffff168061426a57505050565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d44416803b156110df575f928360649273ffffffffffffffffffffffffffffffffffffffff948560405198899788967f03a65ab600000000000000000000000000000000000000000000000000000000885216600487015216602485015260448401525af180156110d4576143105750565b5f61414891612f39565b9190918161432757505050565b73ffffffffffffffffffffffffffffffffffffffff83166143845750614148915073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d44416613e0b565b604051927f0c11dedd0000000000000000000000000000000000000000000000000000000084526004840152602483015260448201525f806064838273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444165af115613ec55750565b9061440b916141d0565b03911561449a57806144416fffffffffffffffffffffffffffffffff70010000000000000000000000000000000093168461464f565b92091515016fffffffffffffffffffffffffffffffff8111614472576fffffffffffffffffffffffffffffffff1690565b7f59d2b24a000000000000000000000000000000000000000000000000000000005f5260045ffd5b906fffffffffffffffffffffffffffffffff6144b792169061464f565b6fffffffffffffffffffffffffffffffff8111614472576fffffffffffffffffffffffffffffffff1690565b806f800000000000000000000000000000000160801c1561450b576335278d125f526004601cfd5b600f0b90565b9061451e919392936141d0565b929091156145e85760801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000169181810361455a8282866146b4565b93096145d0575b80156145c35780820491061515016fffffffffffffffffffffffffffffffff811161459b576fffffffffffffffffffffffffffffffff1690565b7fb4ef2546000000000000000000000000000000000000000000000000000000005f5260045ffd5b6365244e4e5f526004601cfd5b906001019081614561575b63ae47f7025f526004601cfd5b8261461e917fffffffffffffffffffffffffffffffff000000000000000000000000000000008461462396039160801b166146b4565b6136a8565b6fffffffffffffffffffffffffffffffff811161459b576fffffffffffffffffffffffffffffffff1690565b8181029180828404148215171561466857505060801c90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff910981811082019003908160801c156146a95763ae47f7025f526004601cfd5b60801c9060801b0190565b818102929181158285048214178302156146cf575050900490565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8492840985811086019003920990825f03831692818111156145db5783900480600302600218808202600203028082026002030280820260020302808202600203028082026002030280910260020302936001848483030494805f030401921190030217029056fea26469706673582212202dcb46da28868b53365e52a05f6bbe498d38071868b159f39ba8ebf8819ad5c864736f6c634300081c0033

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

000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444000000000000000000000000ecdbfba64c900bdc249676fd691d4900c513c68c

-----Decoded View---------------
Arg [0] : core (address): 0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444
Arg [1] : tokenURIGenerator (address): 0xeCDbFBa64C900Bdc249676fd691D4900c513C68c

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000e0e0e08a6a4b9dc7bd67bcb7aade5cf48157d444
Arg [1] : 000000000000000000000000ecdbfba64c900bdc249676fd691d4900c513c68c


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

OVERVIEW

Ekubo Protocol is the most efficient AMM protocol on Ethereum.

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.