Abstract Testnet

Contract

0x946732510A0C1997Ed5675Ac34d46BBd62986348

Overview

ETH Balance

0 ETH

Token Holdings

Multichain Info

N/A
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions and 1 Token Transfer found.

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
49767812025-01-24 0:17:462 days ago1737677866
0x94673251...d62986348
0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
g8keepBondingCurve

Compiler Version
v0.8.26+commit.8a97fa7a

ZkSolc Version
v1.5.7

Optimization Enabled:
Yes with Mode 3

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 25 : g8keepBondingCurve.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {SafeTransferLib} from "solady/utils/ext/zksync/SafeTransferLib.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {Parameters, CurveSettings} from "./Common.sol";
import {g8keepBondingCurveFactory} from "./g8keepBondingCurveFactory.sol";
import {TickMath} from "./libraries/TickMath.sol";
import {IWETH} from "./interfaces/IWETH.sol";
import {INonfungiblePositionManager} from "./interfaces/INonfungiblePositionManager.sol";
import {IG8keepLockerFactory} from "./interfaces/IG8keepLockerFactory.sol";
import {IUniswapV3Factory} from "./interfaces/IUniswapV3Factory.sol";
import {IUniswapV3Pool} from "./interfaces/IUniswapV3Pool.sol";

/**
 * @title   g8keepBondingCurve
 * @notice  g8keep token contract with built-in bonding curve and world-class snipe protection.
 */
contract g8keepBondingCurve is IERC20 {
    /// @dev Stores a set of ETH/token balances for a point on the bonding curve.
    struct Reserves {
        uint112 reserve0;
        uint112 reserve1;
    }

    /// @dev Stores token balance split by Class A and Class B tokens.
    struct Balance {
        uint112 classA; // pre-liquidity met purchases
        uint112 classB; // penalty / post-liquidity met purchases
    }

    /// @dev Name of the token.
    string public name;
    /// @dev Symbol of the token.
    string public symbol;
    /// @dev Token decimals.
    uint8 public constant decimals = 18;
    /// @dev Total supply of the token.
    uint112 private immutable TOTAL_SUPPLY;

    /// @dev Farcaster ID for tokens launched through farcaster.
    uint256 public fid;
    string public image;
    string public castHash;

    /// @dev Fee denominator constant equal to 100% in BPS.
    uint16 private constant FEE_DENOMINATOR = 10_000;

    /// @dev Address of the UniswapV3 pair created during token deployment for migration.
    address private immutable PAIR_ADDRESS;
    /// @dev Address of the LP locker created when token migrates.
    address private lpLocker;

    /// @dev Address of the Uniswap V3 Position Manager.
    INonfungiblePositionManager private immutable UNISWAP_POSITION_MANAGER;
    /// @dev Address of the g8keep Bonding Curve Factory.
    g8keepBondingCurveFactory private immutable G8KEEP_FACTORY;
    /// @dev Address of the LP locker factory.
    IG8keepLockerFactory private immutable LOCKER_FACTORY;
    /// @dev Uniswap V3 fee tier that will be used for migrated liquidity.
    uint24 private immutable UNISWAP_FEE_TIER;
    /// @dev Address of the WETH contract.
    IWETH private immutable WETH;
    /// @dev Address of the g8keep fee wallet.
    address public immutable G8KEEP_FEE_WALLET;
    /// @dev Adjustment to the initial price of a token simulating concentrated single-sided liquidity.
    uint112 private immutable LIQUIDITY_SHIFT;
    /// @dev Amount of liquidity required to migrate a token to Uniswap V3.
    uint112 private immutable MIGRATION_MINIMUM_LIQUIDITY;
    /// @dev Fee rate in BPS that will be applied to trades to collect ETH for the initial liquidity shift and deployer reward.
    /// @dev Trades after the liquidity supplement has been filled will not be assessed a liquidity supplement fee.
    uint16 private immutable LIQUIDITY_SUPPLEMENT_FEE;
    /// @dev Amount of liquidity supplement fee that must be collected to migrate a token to Uniswap V3.
    uint112 private immutable MIGRATION_MINIMUM_LIQUIDITY_SUPPLEMENT;
    /// @dev Address of the token deployer.
    address private immutable DEPLOYER;
    /// @dev Amount of reward for the deployer in ETH.
    uint112 private immutable DEPLOYER_REWARD;
    /// @dev Fee rate in BPS for g8keep.
    uint16 private immutable G8KEEP_FEE;
    /// @dev Block timestamp at time of deployment.
    uint40 private immutable GENESIS_TIME;
    /// @dev Amount of supply that is snipe protected.
    uint112 private immutable SNIPE_PROTECTED_SUPPLY;
    /// @dev Timestamp that snipe protection ends.
    uint40 private immutable SNIPE_PROTECTION_END;
    /// @dev Length in seconds that snipe protection is enabled.
    uint40 private immutable SNIPE_PROTECTION_SECONDS;
    /// @dev Length in seconds that heavy snipe protection is enabled.
    uint40 private immutable SNIPE_PROTECTION_HEAVY_PENALTY_SECONDS;
    /// @dev Base exponent for snipe protection.
    uint8 private constant SNIPE_PENALTY_BASE_EXPONENT = 2;
    /// @dev Exponent value that heavy snipe protection starts at.
    uint8 private immutable SNIPE_PROTECTION_HEAVY_EXPONENT_START;
    /// @dev Uniswap V3 min tick for liquidity migration.
    int24 private immutable MIGRATION_MIN_TICK;
    /// @dev Uniswap V3 max tick for liquidity migration.
    int24 private immutable MIGRATION_MAX_TICK;

    /// @dev Current status of the bonding curve using bitwise flags.
    uint8 private curveStatus;
    /// @dev Bitwise flag set when curve has met minimum liquidity.
    uint8 private constant STATUS_LIQUIDITY_FLAG = 1;
    /// @dev Bitwise flag set when curve has met minimum volume to supplement liquidity shift and deployer reward.
    uint8 private constant STATUS_VOLUME_FLAG = 2;
    /// @dev Bitwise flag when curve is in the process of migrating to allow token transfers to the Uniswap V3 pair.
    uint8 private constant STATUS_MIGRATING_FLAG = 4;
    /// @dev Bitwise flag to indicate when a migration has failed so that trading may continue on the bonding curve.
    uint8 private constant STATUS_MIGRATION_FAILED_FLAG = 8;

    /// @dev Current amount of liquidity supplement that has been collected.
    uint112 private liquiditySupplement;
    /// @dev Snipe-protected, pre-liquidity met portion of the bonding curve.
    Reserves private aReserve;
    /// @dev Post-liquidity met portion of the bonding curve, snipe penalty purchases buy from this portion of the curve.
    /// @dev Until the liquidity threshold has been met, only Class B tokens can be sold against the B reserves. This
    /// @dev provides class-protection for early large buyers as the liquidity threshold cannot be met until after snipe
    /// @dev protection has ended.
    Reserves private bReserve;

    /// @dev Mapping of token balances for token owners.
    mapping(address => Balance) private _balances;
    /// @dev Mapping of spender approvals.
    mapping(address => mapping(address => uint256)) private _allowances;

    /// @dev Emitted when tokens are purchased from the bonding curve.
    event Buy(address indexed buyer, uint256 ethAmountA, uint256 ethAmountB, uint256 classA, uint256 classB);
    /// @dev Emitted when tokens are sold back to the bonding curve.
    event Sell(address indexed seller, uint256 tokenAmount, uint256 ethAmount);
    /// @dev Emitted when a migration fails.
    event MigrationFailed();
    /// @dev Emitted when a token migrates to Uniswap V3.
    event TokenMigrated(address indexed pair, address indexed lpLocker, uint256 ethAmount, uint256 tokenAmount);

    /// @dev Thrown when a buy/sell has a value of zero or a calculation for the bonding curve uses an invalid amount.
    error InvalidAmount();
    /// @dev Thrown when transferring tokens to or from the Uniswap V3 pair when the curve has not migrated or when deployer attempts to withdraw excess prior to migration.
    error NotMigrated();
    /// @dev Thrown when the spender does not have a sufficient allowance for the transfer being made.
    error InsufficientAllowance();
    /// @dev Thrown when a transfer amount exceeds the sender's balance.
    error InsufficientBalance();
    /// @dev Thrown when calculating migration ticks and the minimum tick price is invalid.
    error InvalidReserves();
    /// @dev Thrown when setting an approval or attempting to transfer tokens to the zero address.
    error ZeroAddress();
    /// @dev Thrown when attempting to buy or sell through the bonding curve when liquidity has migrated to Uniswap V3.
    error CurveMigrated();
    /// @dev Thrown when the output amount from a buy or sell does not meet the minimum specified.
    error InsufficientOutput();
    /// @dev Thrown when a caller is not allowed to execute a function.
    error InvalidCaller();
    /// @dev Thrown when deploying a token and the corresponding Uniswap V3 pool has already been created.
    error PoolAlreadyCreated();

    constructor(Parameters memory parameters) {
        G8KEEP_FACTORY = g8keepBondingCurveFactory(msg.sender);

        // Setup base token details
        name = parameters.TOKEN_NAME;
        symbol = parameters.TOKEN_SYMBOL;
        TOTAL_SUPPLY = parameters.TOTAL_SUPPLY;

        fid = parameters.FID;
        image = parameters.IMAGE;
        castHash = parameters.CAST_HASH;

        G8KEEP_FEE_WALLET = parameters.G8KEEP_FEE_WALLET;
        G8KEEP_FEE = parameters.G8KEEP_FEE;
        LIQUIDITY_SUPPLEMENT_FEE = parameters.LIQUIDITY_SUPPLEMENT_FEE;

        // Setup migration parameters
        DEPLOYER = parameters.DEPLOYER;
        DEPLOYER_REWARD = parameters.DEPLOYER_REWARD;
        UNISWAP_POSITION_MANAGER = parameters.UNISWAP_POSITION_MANAGER;
        LOCKER_FACTORY = parameters.LOCKER_FACTORY;
        UNISWAP_FEE_TIER = parameters.UNISWAP_FEE_TIER;
        WETH = IWETH(parameters.WETH);
        LIQUIDITY_SHIFT = parameters.LIQUIDITY_SHIFT;
        MIGRATION_MINIMUM_LIQUIDITY = parameters.MIGRATION_MINIMUM_LIQUIDITY;
        MIGRATION_MINIMUM_LIQUIDITY_SUPPLEMENT = DEPLOYER_REWARD;

        IUniswapV3Factory factory = IUniswapV3Factory(UNISWAP_POSITION_MANAGER.factory());
        int24 tickSpacing = factory.feeAmountTickSpacing(UNISWAP_FEE_TIER);

        // Calculate valid range ticks based on fee tier tick spacing
        (bool priceInvalid, uint160 minTickSqrtPriceX96) = _calculatePrice(LIQUIDITY_SHIFT, TOTAL_SUPPLY);
        if (priceInvalid) {
            revert InvalidReserves();
        }
        unchecked {
            MIGRATION_MIN_TICK = TickMath.getTickAtSqrtRatio(minTickSqrtPriceX96) / tickSpacing * tickSpacing - tickSpacing;
            MIGRATION_MAX_TICK = TickMath.MAX_TICK / tickSpacing * tickSpacing;
        }

        // Setup curves
        aReserve.reserve0 = LIQUIDITY_SHIFT;
        aReserve.reserve1 = TOTAL_SUPPLY;

        uint256 bReserve0In = MIGRATION_MINIMUM_LIQUIDITY - LIQUIDITY_SHIFT;
        SNIPE_PROTECTED_SUPPLY = _getAmountOut(bReserve0In, LIQUIDITY_SHIFT, TOTAL_SUPPLY);
        uint256 bReserve1 = TOTAL_SUPPLY - SNIPE_PROTECTED_SUPPLY;

        bReserve.reserve0 = MIGRATION_MINIMUM_LIQUIDITY;
        bReserve.reserve1 = uint112(bReserve1);

        // Setup snipe protection
        GENESIS_TIME = uint40(block.timestamp);
        SNIPE_PROTECTION_SECONDS = parameters.SNIPE_PROTECTION_SECONDS;
        SNIPE_PROTECTION_END = uint40(block.timestamp) + SNIPE_PROTECTION_SECONDS;
        SNIPE_PROTECTION_HEAVY_PENALTY_SECONDS = parameters.SNIPE_PROTECTION_HEAVY_PENALTY_SECONDS;
        SNIPE_PROTECTION_HEAVY_EXPONENT_START = parameters.SNIPE_PROTECTION_HEAVY_EXPONENT_START;

        _balances[address(this)].classA = TOTAL_SUPPLY - 1 ether;
        emit Transfer(address(0), address(this), TOTAL_SUPPLY - 1 ether);

        // 1 wei of token to g8keepBondingCurveFactory for LP Guard
        _balances[msg.sender].classA = 1 ether;
        emit Transfer(address(0), msg.sender, 1 ether);

        if (factory.getPool(address(this), address(WETH), UNISWAP_FEE_TIER) != address(0)) {
            revert PoolAlreadyCreated();
        }

        address poolAddress = UNISWAP_POSITION_MANAGER.createAndInitializePoolIfNecessary(
            address(this), address(WETH), UNISWAP_FEE_TIER, parameters.START_SQRT_PRICE
        );
        PAIR_ADDRESS = poolAddress;
    }

    /////////// TOKEN FUNCTIONS ///////////

    /**
     * @notice  Returns the token total supply.
     *
     * @return _totalSupply  Total supply of the token.
     */
    function totalSupply() external view returns (uint256 _totalSupply) {
        _totalSupply = TOTAL_SUPPLY;
    }

    /**
     * @notice  Returns the token balance of `account`.
     *
     * @param account  Address to retrieve the balance of.
     *
     * @return _balance  The token balance of `account`.
     */
    function balanceOf(address account) public view returns (uint256 _balance) {
        unchecked {
            (, uint256 classA, uint256 classB) = _getBalances(account);
            _balance = classA + classB;
        }
    }

    /**
     * @notice  Returns the token balance of `account` by class.
     *
     * @param account  Address to retrieve the class balances of.
     *
     * @return _balanceA  The Class A balance of `account`.
     * @return _balanceB  The Class B balance of `account`.
     */
    function classBalanceOf(address account) public view returns (uint256 _balanceA, uint256 _balanceB) {
        (, _balanceA, _balanceB) = _getBalances(account);
    }

    /**
     * @notice  Returns the amount `owner` has authorized `spender` to transfer.
     *
     * @dev     An allowance of `type(uint256).max` is considered an unlimited allowance.
     *
     * @param owner    The owner of the tokens `spender` is allowed to transfer.
     * @param spender  The account authorized by `owner` to spend tokens on behalf of.
     *
     * @return _allowance  The amount of tokens of `owner` that `spender` is allowed to transfer.
     */
    function allowance(address owner, address spender) public view returns (uint256 _allowance) {
        _allowance = _allowances[owner][spender];
    }

    /**
     * @notice  Sets the allowed amount that `spender` may transfer on behalf of the caller.
     *
     * @dev     An `amount` of `type(uint256).max` is considered an unlimited allowance.
     *
     * @param spender  Address of the account that the caller is authorizing.
     * @param amount   Amount of tokens the caller is authorizing `spender` to transfer.
     */
    function approve(address spender, uint256 amount) external returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    /**
     * @notice  Transfers `amount` of tokens from the caller to `recipient`.
     *
     * @param recipient  Address that will receive the tokens from the caller.
     * @param amount     Amount of tokens to transfer to `recipient`.
     */
    function transfer(address recipient, uint256 amount) external returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    /**
     * @notice  Transfers `amount` of tokens from `sender` to `recipient` if the
     * @notice  caller has a sufficient allowance set by `sender`.
     *
     * @param sender     Account to send the tokens from.
     * @param recipient  Address that will receive the tokens from `sender`.
     * @param amount     Amount of tokens to transfer to `recipient` from `sender`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
        uint256 currentAllowance = _allowances[sender][msg.sender];
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < amount) revert InsufficientAllowance();
            unchecked {
                _approve(sender, msg.sender, currentAllowance - amount);
            }
        }

        _transfer(sender, recipient, amount);

        return true;
    }

    /////////// CURVE FUNCTIONS ///////////

    /**
     * @notice  Calculates the expected output amounts for a bonding curve buy.
     * 
     * @param ethAmount  The amount of ETH value that will be used for the buy.
     * 
     * @return tokenAmount  The total amount of tokens that would be purchased.
     * @return ethA         The amount of ETH that would be applied to Class A purchase.
     * @return ethB         The amount of ETH that would be applied to Class B purchase.
     * @return classA       The amount of Class A tokens that would be purchased.
     * @return classB       The amount of Class B tokens that would be purchased.
     */
    function calculateBuy(uint112 ethAmount)
        public
        view
        returns (uint112 tokenAmount, uint112 ethA, uint112 ethB, uint112 classA, uint112 classB)
    {
        (bool curveLiquidityMet, bool curveVolumeMet, bool curveMigrated,) = _getCurveStatus();
        if (curveMigrated) revert CurveMigrated();

        ethAmount = _applyFeeCalculation(ethAmount);
        ethAmount = _applyLiquiditySupplementCalculation(ethAmount, curveVolumeMet);

        (ethA, ethB, classA, classB) = _curveBuyCalculation(ethAmount, curveLiquidityMet);
        tokenAmount = classA + classB;
    }

    /**
     * @notice  Calculates the expected output amount for a bonding curve sale.
     * 
     * @param from         Address of the seller.
     * @param tokenAmount  The amount of token to be sold.
     * 
     * @return ethAmount  The total amount of ETH that would be received from the sale.
     */
    function calculateSell(address from, uint112 tokenAmount) public view returns (uint112 ethAmount) {
        (bool curveLiquidityMet, bool curveVolumeMet, bool curveMigrated,) = _getCurveStatus();
        if (curveMigrated) revert CurveMigrated();

        ethAmount = _curveSellCalculation(from, tokenAmount, curveLiquidityMet);
        ethAmount = _applyFeeCalculation(ethAmount);
        ethAmount = _applyLiquiditySupplementCalculation(ethAmount, curveVolumeMet);
    }

    /**
     * @notice  Returns the current status of the bonding curve.
     * 
     * @return _aReserve                    Current values of the A Reserve.
     * @return _bReserve                    Current values of the B Reserve.
     * @return _liquiditySupplement         Current amount of liquidity supplement that has been collected.
     * @return _minimumLiquiditySupplement  Amount of liquidity supplement required to migrate.
     * @return _curveLiquidityMet           True if the curve has hit the migration liquidity.
     * @return _curveVolumeMet              True if the curve has hit the migration volume.
     * @return _curveMigrated               True if the curve has migrated.
     * @return _migrationFailed             True if the curve has attempted to migrate and failed to.
     */
    function getCurveStatus()
        external
        view
        returns (
            Reserves memory _aReserve,
            Reserves memory _bReserve,
            uint256 _liquiditySupplement,
            uint256 _minimumLiquiditySupplement,
            bool _curveLiquidityMet,
            bool _curveVolumeMet,
            bool _curveMigrated,
            bool _migrationFailed
        )
    {
        _aReserve = aReserve;
        _bReserve = bReserve;

        _liquiditySupplement = liquiditySupplement;
        _minimumLiquiditySupplement = MIGRATION_MINIMUM_LIQUIDITY_SUPPLEMENT;

        (_curveLiquidityMet, _curveVolumeMet, _curveMigrated, _migrationFailed) = _getCurveStatus();
    }

    /**
     * @notice  Returns the bonding curves settings from deployment.
     *
     * @return curveSettings  Settings that were set during token deployment.
     */
    function getCurveSettings() external view returns (CurveSettings memory curveSettings) {
        curveSettings.deployer = DEPLOYER;
        curveSettings.deployerReward = DEPLOYER_REWARD;
        curveSettings.g8keepFeeWallet = G8KEEP_FEE_WALLET;
        curveSettings.g8keepFee = G8KEEP_FEE;
        curveSettings.liquidityShift = LIQUIDITY_SHIFT;
        curveSettings.migrationMinimumLiquidity = MIGRATION_MINIMUM_LIQUIDITY;
        curveSettings.liquiditySupplementFee = LIQUIDITY_SUPPLEMENT_FEE;
        curveSettings.migrationPositionManager = address(UNISWAP_POSITION_MANAGER);
        curveSettings.migrationFeeTier = UNISWAP_FEE_TIER;
        curveSettings.genesisTime = GENESIS_TIME;
        curveSettings.snipeProtectionEnd = SNIPE_PROTECTION_END;
        curveSettings.snipeProtectionSeconds = SNIPE_PROTECTION_SECONDS;
        curveSettings.snipeProtectionHeavySeconds = SNIPE_PROTECTION_HEAVY_PENALTY_SECONDS;
        curveSettings.snipeProtectionHeavyExponentStart = SNIPE_PROTECTION_HEAVY_EXPONENT_START;
        curveSettings.pairAddress = PAIR_ADDRESS;
        curveSettings.lpLocker = lpLocker;
    }

    /**
     * @notice  Executes a buy of tokens using `msg.value` and reverting if the amount of tokens
     * @notice  received is less than `minimumOut`.
     * 
     * @param minimumOut  The minimum amount of tokens to receive without reverting the buy.
     */
    function buy(uint256 minimumOut) external payable {
        _buy(msg.sender, minimumOut);
    }

    /**
     * @notice  Executes a buy of tokens to another address using `msg.value` and reverting if 
     * @notice  the amount of tokens received is less than `minimumOut`.
     * 
     * @param to          The address that will receive the purchased tokens.
     * @param minimumOut  The minimum amount of tokens to receive without reverting the buy.
     */
    function buy(address to, uint256 minimumOut) external payable {
        if (to == PAIR_ADDRESS) {
            revert NotMigrated();
        }
        _buy(to, minimumOut);
    }

    /**
     * @notice  Executes a sale of tokens from the caller and reverting if the amount of
     * @notice  tokens received is less than `minimumOut`. Sales will sell Class B tokens first.
     * 
     * @param amount      The amount of tokens to sell.
     * @param minimumOut  The minimum amount of ETH to receive without reverting the sale.
     */
    function sell(uint112 amount, uint112 minimumOut) external {
        _sell(msg.sender, amount, minimumOut);
    }

    /**
     * @notice  Executes a sale of tokens from `G8KEEP_FACTORY` on behalf of `seller` and reverting if the amount of
     * @notice  tokens received is less than `minimumOut`. Sales will sell Class B tokens first.
     */
    function factorySell(address seller, uint112 amount, uint112 minimumOut) external {
        if (msg.sender != address(G8KEEP_FACTORY)) revert InvalidCaller();

        _sell(seller, amount, minimumOut);
    }

    /**
     * @notice  Executes a sale of tokens from the caller and reverting if the amount of
     * @notice  tokens received is less than `minimumOut`. Sales will sell Class B tokens first.
     * 
     * @param seller      The address that will receive the ETH from the sale.
     * @param amount      The amount of tokens to sell.
     * @param minimumOut  The minimum amount of ETH to receive without reverting the sale.
     */
    function _sell(address seller, uint112 amount, uint112 minimumOut) internal {
        if (amount == 0) revert InvalidAmount();

        (bool curveLiquidityMet, bool curveVolumeMet, bool curveMigrated, bool migrationFailed) = _getCurveStatus();
        if (curveMigrated) revert CurveMigrated();

        uint112 sellValue = _applyFee(_curveSell(seller, amount, curveLiquidityMet));
        (sellValue, curveVolumeMet) = _applyLiquiditySupplement(sellValue, curveVolumeMet);

        if (curveLiquidityMet && curveVolumeMet && !migrationFailed) {
            _migrateToken(sellValue);
        }

        if (sellValue < minimumOut) revert InsufficientOutput();
        SafeTransferLib.safeTransferETH(seller, sellValue);

        emit Sell(seller, amount, sellValue);
    }

    /**
     * @notice  Returns the current maximum purchase of tokens that can be executed without a snipe penalty.
     */
    function maxBuyWithoutPenalty() external view returns (uint256) {
        uint256 currentBalance = _balances[address(this)].classA;
        if (block.timestamp >= SNIPE_PROTECTION_END) {
            return currentBalance;
        }

        uint256 elapsedSeconds = block.timestamp - GENESIS_TIME;
        uint256 expectedBalance = TOTAL_SUPPLY - SNIPE_PROTECTED_SUPPLY * elapsedSeconds / SNIPE_PROTECTION_SECONDS;

        if (currentBalance <= expectedBalance) {
            return 0;
        }
        return currentBalance - expectedBalance;
    }

    /**
     * @notice  Allows the deployer to withdraw any excess tokens held by the contract after migration.
     */
    function withdrawExcess() external {
        if (msg.sender != DEPLOYER) {
            revert InvalidCaller();
        }

        (,, bool curveMigrated,) = _getCurveStatus();
        if (!curveMigrated) {
            revert NotMigrated();
        }

        (, uint112 thisClassA, uint112 thisClassB) = _getBalances(address(this));
        if (thisClassA | thisClassB > 0) {
            unchecked {
                _transfer(address(this), DEPLOYER, thisClassA + thisClassB);
            }
        }
        G8KEEP_FACTORY.transferGuardTokens(DEPLOYER);
        uint256 wethBalance = WETH.balanceOf(address(this));
        if (wethBalance > 0) {
            WETH.transfer(DEPLOYER, wethBalance);
        }
        uint256 ethBalance = address(this).balance;
        if (ethBalance > 0) {
            SafeTransferLib.forceSafeTransferETH(DEPLOYER, ethBalance);
        }
    }

    /////////// INTERNAL FUNCTIONS ///////////

    /**
     * @dev  Internal function to handle approvals.
     * @dev  Updates the `_allowances` mapping and emits an `Approval` event.
     *
     * @param owner    Account that owns the tokens being authorized. This will always be the caller.
     * @param spender  Address of the account that the caller is authorizing.
     * @param amount   Amount of tokens the caller is authorizing `spender` to transfer.
     */
    function _approve(address owner, address spender, uint256 amount) private {
        if (owner == address(0)) revert ZeroAddress();
        if (spender == address(0)) revert ZeroAddress();

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev  Internal function to handle transfers.
     * @dev  Updates the `_balances` mapping for `from` and `to` and emits a `Transfer` event.
     *
     * @param from    Address of the account to transfer tokens from.
     * @param to      Address of the account to transfer tokens to.
     * @param amount  Amount of tokens to transfer from `from` to `to`.
     */
    function _transfer(address from, address to, uint256 amount) private {
        if (from == address(0)) revert ZeroAddress();
        if (to == address(0)) revert ZeroAddress();
        (bool curveLiquidityMet,, bool curveMigrated,) = _getCurveStatus();
        if (!curveMigrated) {
            address poolAddress = PAIR_ADDRESS;
            if (to == poolAddress || from == poolAddress) {
                bool fromValid = from == address(this) || from == address(G8KEEP_FACTORY);
                if (!(msg.sender == address(UNISWAP_POSITION_MANAGER) && fromValid)) {
                    if (!(msg.sender == poolAddress && from == poolAddress && to == address(this) && curveStatus & STATUS_MIGRATING_FLAG == STATUS_MIGRATING_FLAG)) {
                        if (!(msg.sender == poolAddress && from == address(this) && to == poolAddress)) {
                            revert NotMigrated();
                        }
                    }
                }
            }
        }

        if (amount > type(uint112).max) {
            revert InsufficientBalance();
        }
        uint112 castAmount = uint112(amount);
        (Balance storage senderBalance, uint112 senderClassA, uint112 senderClassB) = _getBalances(from);

        unchecked {
            if (curveLiquidityMet) {
                if (senderClassA < castAmount) {
                    senderClassA += senderClassB;
                    if (senderClassA < castAmount) {
                        revert InsufficientBalance();
                    }
                    senderBalance.classB = 0;
                }
                senderBalance.classA = senderClassA - castAmount;
                _balances[to].classA += castAmount;
            } else {
                if (senderClassA < castAmount) {
                    uint112 transferClassB = castAmount - senderClassA;
                    if (senderClassB < transferClassB) {
                        revert InsufficientBalance();
                    }
                    senderBalance.classA = 0;
                    senderBalance.classB = senderClassB - transferClassB;
                    Balance storage receiverBalance = _balances[to];
                    receiverBalance.classA += senderClassA;
                    receiverBalance.classB += transferClassB;
                } else {
                    senderBalance.classA = senderClassA - castAmount;
                    _balances[to].classA += castAmount;
                }
            }
        }
        emit Transfer(from, to, castAmount);
    }

    /**
     * @dev  Internal function to return the `Balance` storage pointer and class balances for `account`.
     * 
     * @param account  Address to retrieve balance for.
     */
    function _getBalances(address account)
        internal
        view
        returns (Balance storage balance, uint112 classA, uint112 classB)
    {
        balance = _balances[account];
        classA = balance.classA;
        classB = balance.classB;
    }

    /**
     * @dev  Internal function to apply snipe protection to an amount of output tokens.
     * 
     * @param amountOut  The amount of tokens being purchased.
     * 
     * @return adjustedAmountOut  The amount of tokens after snipe protection has been applied.
     */
    function _applySnipeProtection(uint112 amountOut) internal view returns (uint112 adjustedAmountOut) {
        if (block.timestamp >= SNIPE_PROTECTION_END) {
            return amountOut;
        }

        // Get balance, adjust by amount out
        uint256 tmpAdjustedAmountOut = amountOut;
        uint112 adjustedBalance;
        unchecked {
            adjustedBalance = _balances[address(this)].classA - amountOut;
        }

        // Calculate expected balance based on elapsed time
        uint112 elapsedSeconds = uint112(block.timestamp - GENESIS_TIME);
        uint112 expectedBalance = TOTAL_SUPPLY - SNIPE_PROTECTED_SUPPLY * elapsedSeconds / SNIPE_PROTECTION_SECONDS;

        // Apply snipe penalties if adjusted balance is less than expected
        if (expectedBalance > adjustedBalance) {
            uint256 exponentPenalty = SNIPE_PENALTY_BASE_EXPONENT;
            if (elapsedSeconds < SNIPE_PROTECTION_HEAVY_PENALTY_SECONDS) {
                unchecked {
                    exponentPenalty += SNIPE_PROTECTION_HEAVY_EXPONENT_START
                        * (SNIPE_PROTECTION_HEAVY_PENALTY_SECONDS - elapsedSeconds) / SNIPE_PROTECTION_HEAVY_PENALTY_SECONDS;
                }
            }
            for (uint256 i; i < exponentPenalty; ++i) {
                tmpAdjustedAmountOut = tmpAdjustedAmountOut * adjustedBalance / expectedBalance;
            }
        }
        adjustedAmountOut = uint112(tmpAdjustedAmountOut);
    }

    /**
     * @dev  Internal function to execute a purchase of tokens.
     * @dev  Applies g8keep and liquidity supplement fees.
     * @dev  Calculates buy amounts, updates reserves, updates balances.
     * @dev  If migration conditions are met, executes curve migration to Uniswap V3.
     * @dev  Emits a `Buy` event.
     * 
     * @param to          Address buying the tokens.
     * @param minimumOut  Minimum output of tokens without reverting the transaction.
     */
    function _buy(address to, uint256 minimumOut) internal {
        if (msg.value == 0 || msg.value > type(uint112).max) revert InvalidAmount();

        (bool curveLiquidityMet, bool curveVolumeMet, bool curveMigrated, bool migrationFailed) = _getCurveStatus();
        if (curveMigrated) revert CurveMigrated();

        uint112 buyValue = _applyFee(uint112(msg.value));
        (buyValue, curveVolumeMet) = _applyLiquiditySupplement(buyValue, curveVolumeMet);

        uint112 ethA;
        uint112 ethB;
        uint112 classA;
        uint112 classB;
        (ethA, ethB, classA, classB, curveLiquidityMet) = _curveBuy(buyValue, curveLiquidityMet);

        uint112 totalPurchase = classA + classB;
        if (totalPurchase < minimumOut) revert InsufficientOutput();

        unchecked {
            _balances[address(this)].classA -= totalPurchase;
            Balance storage balance = _balances[to];
            balance.classA += classA;
            balance.classB += classB;
            emit Transfer(address(this), to, totalPurchase);
        }

        if (curveLiquidityMet && curveVolumeMet && !migrationFailed) {
            _migrateToken(0);
        }

        emit Buy(to, ethA, ethB, classA, classB);
    }

    /**
     * @dev  Internal function to parse the `curveStatus` storage value into boolean flags.
     * 
     * @return curveLiquidityMet  True if the curve has hit the migration liquidity.
     * @return curveVolumeMet     True if the curve has hit the migration volume.
     * @return curveMigrated      True if the curve has migrated.
     * @return migrationFailed    True if the curve has attempted to migrate and failed to.
     */
    function _getCurveStatus()
        internal
        view
        returns (bool curveLiquidityMet, bool curveVolumeMet, bool curveMigrated, bool migrationFailed)
    {
        uint8 _curveStatus = curveStatus;

        curveLiquidityMet = _curveStatus & STATUS_LIQUIDITY_FLAG == STATUS_LIQUIDITY_FLAG;
        curveVolumeMet = _curveStatus & STATUS_VOLUME_FLAG == STATUS_VOLUME_FLAG;
        migrationFailed = _curveStatus & STATUS_MIGRATION_FAILED_FLAG == STATUS_MIGRATION_FAILED_FLAG;
        curveMigrated = curveLiquidityMet && curveVolumeMet && !migrationFailed;
    }

    /**
     * @dev  Internal function to apply the g8keep fee to a purchase or sale.
     * 
     * @param value  The current value for the transaction.
     *
     * @return remainingValue  The amount of value remaining after applying the g8keep fee.
     */
    function _applyFee(uint112 value) internal returns (uint112 remainingValue) {
        unchecked {
            uint112 fee = value * G8KEEP_FEE / FEE_DENOMINATOR;
            SafeTransferLib.forceSafeTransferETH(G8KEEP_FEE_WALLET, fee);
            remainingValue = value - fee;
        }
    }

    /**
     * @dev  View representation of the `_applyFee` function for calculations.
     * 
     * @param value  The current value for the transaction.
     *
     * @return remainingValue  The amount of value remaining after applying the g8keep fee.
     */
    function _applyFeeCalculation(uint112 value) internal view returns (uint112 remainingValue) {
        unchecked {
            uint112 fee = value * G8KEEP_FEE / FEE_DENOMINATOR;
            remainingValue = value - fee;
        }
    }

    /**
     * @dev  Internal function to apply the liquidity supplement fee to a purchase or sale.
     * 
     * @param value           The current value for the transaction.
     * @param curveVolumeMet  Starting value for if the curve has met the minimum volume.
     *
     * @return remainingValue         The amount of value remaining after applying the liquidity supplement fee.
     * @return updatedCurveVolumeMet  True if the curve has met the minimum volume.
     */
    function _applyLiquiditySupplement(uint112 value, bool curveVolumeMet)
        internal
        returns (uint112 remainingValue, bool updatedCurveVolumeMet)
    {
        remainingValue = value;
        updatedCurveVolumeMet = curveVolumeMet;

        if (!updatedCurveVolumeMet) {
            unchecked {
                uint112 _liquiditySupplement = liquiditySupplement;
                uint112 maxSupplementFee = MIGRATION_MINIMUM_LIQUIDITY_SUPPLEMENT - _liquiditySupplement;
                uint112 liquiditySupplementFee = value * LIQUIDITY_SUPPLEMENT_FEE / FEE_DENOMINATOR;
                if (maxSupplementFee <= liquiditySupplementFee) {
                    liquiditySupplementFee = maxSupplementFee;
                    curveStatus = curveStatus | STATUS_VOLUME_FLAG;
                    updatedCurveVolumeMet = true;
                }
                liquiditySupplement = _liquiditySupplement + liquiditySupplementFee;
                remainingValue = remainingValue - liquiditySupplementFee;
            }
        }
    }

    /**
     * @dev  View representation of the `_applyLiquiditySupplement` function for calculations.
     * 
     * @param value           The current value for the transaction.
     * @param curveVolumeMet  Starting value for if the curve has met the minimum volume.
     *
     * @return remainingValue         The amount of value remaining after applying the liquidity supplement fee.
     */
    function _applyLiquiditySupplementCalculation(uint112 value, bool curveVolumeMet)
        internal
        view
        returns (uint112 remainingValue)
    {
        remainingValue = value;

        if (!curveVolumeMet) {
            unchecked {
                uint112 _liquiditySupplement = liquiditySupplement;
                uint112 maxSupplementFee = MIGRATION_MINIMUM_LIQUIDITY_SUPPLEMENT - _liquiditySupplement;
                uint112 liquiditySupplementFee = value * LIQUIDITY_SUPPLEMENT_FEE / FEE_DENOMINATOR;
                if (maxSupplementFee < liquiditySupplementFee) {
                    liquiditySupplementFee = maxSupplementFee;
                }
                remainingValue = remainingValue - liquiditySupplementFee;
            }
        }
    }

    /**
     * @dev  Internal function to process a buy on the bonding curve.
     * @dev  If the curve has met liquidity requirement, buys will be processed on the B Reserves.
     * @dev  Applies snipe protection to A Reserve purchases during the snipe protection window.
     * 
     * @param buyValue           Amount of ETH being used for the purchase.
     * @param curveLiquidityMet  Starting value of if the curve has met the liquidity requirement.
     * 
     * @return ethA                      The amount of ETH applied to Class A purchase.
     * @return ethB                      The amount of ETH applied to Class B purchase.
     * @return classA                    The amount of Class A tokens purchased.
     * @return classB                    The amount of Class B tokens purchased.
     * @return updatedCurveLiquidityMet  Updated value if the purchase results in liquidity requirement being met.
     */
    function _curveBuy(uint112 buyValue, bool curveLiquidityMet)
        internal
        returns (uint112 ethA, uint112 ethB, uint112 classA, uint112 classB, bool updatedCurveLiquidityMet)
    {
        updatedCurveLiquidityMet = curveLiquidityMet;
        uint112 remainingBuyValue;

        if (updatedCurveLiquidityMet) {
            remainingBuyValue = buyValue;
            ethB = buyValue;
        } else {
            uint112 reserve0 = aReserve.reserve0;
            uint112 reserve1 = aReserve.reserve1;
            uint112 maxAReserveIn = MIGRATION_MINIMUM_LIQUIDITY - reserve0;
            uint112 reserveAIn = buyValue;

            if (reserveAIn > maxAReserveIn) {
                reserveAIn = maxAReserveIn;
                remainingBuyValue = buyValue - reserveAIn;
            }
            ethA = reserveAIn;
            uint112 amount1Out = _getAmountOut(reserveAIn, reserve0, reserve1);

            classA = _applySnipeProtection(amount1Out);
            if (amount1Out > classA) {
                uint112 classACost = _getAmountIn(classA, reserve0, reserve1);
                remainingBuyValue = remainingBuyValue + reserveAIn - classACost;
                reserveAIn = classACost;
                ethA = classACost;
            }
            ethB = buyValue - ethA;
            reserve0 = reserve0 + reserveAIn;
            aReserve.reserve0 = reserve0;
            aReserve.reserve1 = reserve1 - classA;
            updatedCurveLiquidityMet = reserve0 == MIGRATION_MINIMUM_LIQUIDITY;
            if (updatedCurveLiquidityMet) {
                curveStatus = curveStatus | STATUS_LIQUIDITY_FLAG;
            }
        }

        if (remainingBuyValue > 0) {
            uint112 reserve0 = bReserve.reserve0;
            uint112 reserve1 = bReserve.reserve1;

            classB = _getAmountOut(remainingBuyValue, reserve0, reserve1);

            bReserve.reserve0 = reserve0 + remainingBuyValue;
            bReserve.reserve1 = reserve1 - classB;
        }
    }

    /**
     * @dev  View representation of the `_curveBuy` function for calculations.
     * 
     * @param buyValue           Amount of ETH being used for the purchase.
     * @param curveLiquidityMet  Starting value of if the curve has met the liquidity requirement.
     * 
     * @return ethA    The amount of ETH applied to Class A purchase.
     * @return ethB    The amount of ETH applied to Class B purchase.
     * @return classA  The amount of Class A tokens purchased.
     * @return classB  The amount of Class B tokens purchased.
     */
    function _curveBuyCalculation(uint112 buyValue, bool curveLiquidityMet)
        internal
        view
        returns (uint112 ethA, uint112 ethB, uint112 classA, uint112 classB)
    {
        uint112 remainingBuyValue;

        if (curveLiquidityMet) {
            remainingBuyValue = buyValue;
            ethB = buyValue;
        } else {
            uint112 reserve0 = aReserve.reserve0;
            uint112 reserve1 = aReserve.reserve1;
            uint112 maxAReserveIn = MIGRATION_MINIMUM_LIQUIDITY - reserve0;
            uint112 reserveAIn = buyValue;

            if (reserveAIn > maxAReserveIn) {
                reserveAIn = maxAReserveIn;
                remainingBuyValue = buyValue - reserveAIn;
            }
            ethA = reserveAIn;
            uint112 amount1Out = _getAmountOut(reserveAIn, reserve0, reserve1);

            classA = _applySnipeProtection(amount1Out);
            if (amount1Out > classA) {
                uint112 classACost = _getAmountIn(classA, reserve0, reserve1);
                remainingBuyValue = remainingBuyValue + reserveAIn - classACost;
                ethA = classACost;
            }

            ethB = buyValue - ethA;
        }

        if (remainingBuyValue > 0) {
            uint112 reserve0 = bReserve.reserve0;
            uint112 reserve1 = bReserve.reserve1;

            classB = _getAmountOut(remainingBuyValue, reserve0, reserve1);
        }
    }

    /**
     * @dev  Internal function to process a sale on the bonding curve.
     * @dev  If the curve has met liquidity requirement, sales will be processed on the B Reserves.
     * @dev  Otherwise sales will start with Class B tokens selling on B Reserves then any remaining
     * @dev  tokens to sell will sell on A Reserves.
     * 
     * @param from               Address selling tokens.
     * @param amount             Amount of tokens to sell.
     * @param curveLiquidityMet  True if the curve has previously met the liquidity requirement.
     * 
     * @return amountOut  Amount of ETH to be received from the sale.
     */
    function _curveSell(address from, uint112 amount, bool curveLiquidityMet) internal returns (uint112 amountOut) {
        (Balance storage balance, uint112 classA, uint112 classB) = _getBalances(from);

        uint112 remainingToSell = amount;

        if (classB > 0) {
            if (curveLiquidityMet) {
                classA = classA + classB;
                classB = 0;
            } else {
                uint112 reserve0 = bReserve.reserve0;
                uint112 reserve1 = bReserve.reserve1;

                uint112 amountToSell = amount;
                if (classB < amountToSell) {
                    amountToSell = classB;
                    remainingToSell -= classB;
                    classB = 0;
                } else {
                    classB -= amountToSell;
                    remainingToSell = 0;
                }

                amountOut = _getAmountOut(amountToSell, reserve1, reserve0);
                bReserve.reserve0 -= amountOut;
                bReserve.reserve1 += amountToSell;
            }
        }

        if (remainingToSell > 0) {
            Reserves storage sellFromReserves = aReserve;
            if (curveLiquidityMet) {
                sellFromReserves = bReserve;
            }
            uint112 reserve0 = sellFromReserves.reserve0;
            uint112 reserve1 = sellFromReserves.reserve1;

            uint112 amountToSell = remainingToSell;
            if (classA < amountToSell) {
                revert InsufficientBalance();
            } else {
                classA -= amountToSell;
            }

            uint112 reserve0Out = _getAmountOut(amountToSell, reserve1, reserve0);
            amountOut += reserve0Out;
            sellFromReserves.reserve0 -= reserve0Out;
            sellFromReserves.reserve1 += amountToSell;
        }

        balance.classA = classA;
        balance.classB = classB;
        _balances[address(this)].classA += amount;
        emit Transfer(from, address(this), amount);
    }

    /**
     * @dev  View representation of the `_curveSell` function for calculations.
     * 
     * @param from               Address selling tokens.
     * @param amount             Amount of tokens to sell.
     * @param curveLiquidityMet  True if the curve has previously met the liquidity requirement.
     * 
     * @return amountOut  Amount of ETH to be received from the sale.
     */
    function _curveSellCalculation(address from, uint112 amount, bool curveLiquidityMet)
        internal
        view
        returns (uint112 amountOut)
    {
        (, uint112 classA, uint112 classB) = _getBalances(from);

        uint112 remainingToSell = amount;

        if (classB > 0) {
            if (curveLiquidityMet) {
                classA = classA + classB;
                classB = 0;
            } else {
                uint112 reserve0 = bReserve.reserve0;
                uint112 reserve1 = bReserve.reserve1;

                uint112 amountToSell = amount;
                if (classB < amountToSell) {
                    amountToSell = classB;
                    remainingToSell -= classB;
                    classB = 0;
                } else {
                    classB -= amountToSell;
                    remainingToSell = 0;
                }

                amountOut = _getAmountOut(amountToSell, reserve1, reserve0);
            }
        }

        if (remainingToSell > 0) {
            Reserves storage sellFromReserves = aReserve;
            if (curveLiquidityMet) {
                sellFromReserves = bReserve;
            }
            uint112 reserve0 = sellFromReserves.reserve0;
            uint112 reserve1 = sellFromReserves.reserve1;

            uint112 amountToSell = remainingToSell;
            if (classA < amountToSell) {
                revert InsufficientBalance();
            } else {
                classA -= amountToSell;
            }

            amountOut += _getAmountOut(amountToSell, reserve1, reserve0);
        }
    }

    /**
     * @dev  Internal function to execute the migration of liquidity to Uniswap V3.
     * 
     * @param sellValue  The amount of sale value to deduct from the ETH balance to transfer to the seller after migration.
     */
    function _migrateToken(uint256 sellValue) internal {
        // Set migrating flag to allow token transfers to/from pool by token contract
        curveStatus = curveStatus | STATUS_MIGRATING_FLAG;

        // Remove LP guard to allow price to move for initial liquidity
        G8KEEP_FACTORY.removeLPGuardLiquidity();

        // Approve tokens to transfer by Uniswap V3 Position Manager
        _approve(address(this), address(UNISWAP_POSITION_MANAGER), type(uint256).max);
        WETH.approve(address(UNISWAP_POSITION_MANAGER), type(uint256).max);

        // Calculate token amounts for initial liquidity
        uint112 tokenAmount = _balances[address(this)].classA;
        uint256 ethAmount = address(this).balance - DEPLOYER_REWARD - sellValue;
        WETH.deposit{value: ethAmount}();

        // Calculate Uniswap V3 price based on current liquidity in bonding curve
        (bool priceInvalid, uint160 sqrtPriceX96) = _calculatePrice(ethAmount + LIQUIDITY_SHIFT, tokenAmount);
        if (priceInvalid) {
            // Failure due to current liquidity not being valid for Uniswap V3
            _handleMigrationFailed(sellValue);
            return;
        }

        // Retrieve Uniswap V3 parameters
        address poolAddress = PAIR_ADDRESS;
        (uint160 sqrtPriceX96Start,,,,,,) = IUniswapV3Pool(poolAddress).slot0();

        // If the current pool price does not match the price for the bonding curve's liquidity, attempt to move the pool price.
        if (sqrtPriceX96Start != sqrtPriceX96) {
            bool adjustFailed = _adjustPoolPrice(poolAddress, sqrtPriceX96, ethAmount);

            if (adjustFailed) {
                // Failure due to pool price adjustment not moving the price to the current price
                _handleMigrationFailed(sellValue);
                return;
            }
        }

        // Mint liquidity
        INonfungiblePositionManager.MintParams memory params = INonfungiblePositionManager.MintParams({
            token0: address(this),
            token1: address(WETH),
            fee: UNISWAP_FEE_TIER,
            tickLower: MIGRATION_MIN_TICK,
            tickUpper: MIGRATION_MAX_TICK,
            amount0Desired: tokenAmount,
            amount1Desired: ethAmount,
            amount0Min: 0,
            amount1Min: 0,
            recipient: address(this),
            deadline: block.timestamp
        });
        (uint256 lpTokenId,,,) = UNISWAP_POSITION_MANAGER.mint(params);

        // Deploy liquidity locker and lock liquidity
        UNISWAP_POSITION_MANAGER.approve(address(LOCKER_FACTORY), lpTokenId);
        address _lpLocker = LOCKER_FACTORY.deploy(lpTokenId, DEPLOYER, G8KEEP_FEE_WALLET);
        lpLocker = _lpLocker;

        // Transfer reward to deployer
        SafeTransferLib.forceSafeTransferETH(DEPLOYER, DEPLOYER_REWARD);

        // Emit successful migration
        emit TokenMigrated(poolAddress, _lpLocker, ethAmount, tokenAmount);
    }

    /**
     * @dev  Internal function to attempt to move the Uniswap V3 pool price to the correct price.
     */
    function _adjustPoolPrice(
        address poolAddress,
        uint160 sqrtPriceX96,
        uint256 ethAmount
    ) internal returns (bool adjustFailed) {
        IUniswapV3Pool(poolAddress).swap(address(this), false, int256(uint256(ethAmount)), sqrtPriceX96, "");

        (uint160 sqrtPriceX96New,,,,,,) = IUniswapV3Pool(poolAddress).slot0();
        adjustFailed = sqrtPriceX96 != sqrtPriceX96New;
    }

    /**
     *  @dev  Restricted function for handling the Uniswap V3 Pool swap callback.
     */
    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata /*data*/ ) external {
        if (msg.sender != PAIR_ADDRESS) {
            revert InvalidCaller();
        }

        if (amount0Delta > 0) {
            _transfer(address(this), msg.sender, uint256(amount0Delta));
        }
        if (amount1Delta > 0) {
            WETH.transfer(msg.sender, uint256(amount1Delta));
        }
    }

    /**
     * @dev  Internal function to handle curve settings and balance updates when a migration fails.
     */
    function _handleMigrationFailed(uint256 sellValue) internal {
        curveStatus = (curveStatus ^ STATUS_MIGRATING_FLAG) | STATUS_MIGRATION_FAILED_FLAG;
        WETH.withdraw(WETH.balanceOf(address(this)));
        SafeTransferLib.forceSafeTransferETH(DEPLOYER, DEPLOYER_REWARD);

        uint112 tokenAmount = _balances[address(this)].classA;
        uint256 ethAmount = address(this).balance - sellValue;
        bReserve.reserve0 = uint112(ethAmount);
        bReserve.reserve1 = tokenAmount;

        emit MigrationFailed();
    }

    /**
     * @dev  Internal function to calculate the sqrtPriceX96 value for Uniswap V3 based on current liquidity.
     * 
     * @param token1  Balance of token1.
     * @param token0  Balance of token0.
     * 
     * @return priceInvalid  True if the token balances create an invalid Uniswap V3 price.
     * @param sqrtPriceX96   Price for tokens in Uniswap V3 sqrtPriceX96 format.
     */
    function _calculatePrice(uint256 token1, uint256 token0)
        internal
        pure
        returns (bool priceInvalid, uint160 sqrtPriceX96)
    {
        if (token1 == 0 || token0 == 0) {
            return (true, 0);
        }

        uint256 maxMultiplier = type(uint256).max / token1;
        uint256 multiplier;
        uint256 n = 96;
        while (true) {
            multiplier = 2 ** (n << 1);
            if (maxMultiplier > multiplier) {
                break;
            }
            --n;
        }

        unchecked {
            uint256 tmpPrice = FixedPointMathLib.sqrt(token1 * multiplier / token0) * (2 ** (96 - n));
            if (tmpPrice > type(uint160).max) {
                return (true, 0);
            }
            sqrtPriceX96 = uint160(tmpPrice);
        }
    }

    /**
     * @dev  Internal function to calculate the amount of tokens output
     * @dev  from a swap on the curve.
     *
     * @param amountIn    The amount of tokens input to the curve.
     * @param reserveIn   The reserve of the input token.
     * @param reserveOut  The reserve of the output token.
     *
     * @return amountOut  The calculated amount of output tokens.
     */
    function _getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
        internal
        pure
        returns (uint112 amountOut)
    {
        uint256 tmpAmountOut = (amountIn * reserveOut) / (reserveIn + amountIn);
        if (tmpAmountOut > type(uint112).max) revert InvalidAmount();

        amountOut = uint112(tmpAmountOut);
    }

    /**
     * @dev  Internal function to calculate the amount of tokens input
     * @dev  to the curve for a swap.
     *
     * @param amountOut   The calculated amount of output tokens.
     * @param reserveIn   The reserve of the input token.
     * @param reserveOut  The reserve of the output token.
     *
     * @return amountIn   The amount of tokens input to the curve.
     */
    function _getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut)
        internal
        pure
        returns (uint112 amountIn)
    {
        uint256 tmpAmountIn = (reserveIn * amountOut) / (reserveOut - amountOut);
        if (tmpAmountIn > type(uint112).max) revert InvalidAmount();

        amountIn = uint112(tmpAmountIn);
    }

    /**
     * @dev External function to retrieve the contract URI for the token.
     *
     * @return _contractURI  The contract URI for the token.
     */
    function contractURI() external view returns(string memory _contractURI) {
        _contractURI = G8KEEP_FACTORY.contractURI(address(this));
    }

    /**
     * @dev  Restricted receive function to receive value back from WETH withdrawals.
     */
    receive() external payable {
        if (msg.sender != address(WETH)) {
            revert InvalidCaller();
        }
    }
}

File 2 of 25 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

File 3 of 25 : SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

/// @notice Library for force safe transferring ETH and ERC20s in ZKsync.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ext/zksync/SafeTransferLib.sol)
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev A single use ETH vault has been created for `to`, with `amount`.
    event SingleUseETHVaultCreated(address indexed to, uint256 amount, address vault);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       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();

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

    /// @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 = 1000000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       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, 0x00, 0x00, 0x00, 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(), 0x00, 0x00, 0x00, 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    /// If force transfer is used, returns the vault. Else returns `address(0)`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (address vault)
    {
        if (amount == uint256(0)) return address(0); // Early return if `amount` is zero.
        uint256 selfBalanceBefore = address(this).balance;
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfBalanceBefore, amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            pop(call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00))
        }
        if (address(this).balance == selfBalanceBefore) {
            vault = address(new SingleUseETHVault());
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0x00, shr(96, shl(96, to)))
                if iszero(call(gas(), vault, amount, 0x00, 0x20, 0x00, 0x00)) { revert(0x00, 0x00) }
            }
            emit SingleUseETHVaultCreated(to, amount, vault);
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    /// If force transfer is used, returns the vault. Else returns `address(0)`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (address vault)
    {
        vault = forceSafeTransferETH(to, address(this).balance, gasStipend);
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    /// If force transfer is used, returns the vault. Else returns `address(0)`.
    function forceSafeTransferETH(address to, uint256 amount) internal returns (address vault) {
        vault = forceSafeTransferETH(to, amount, GAS_STIPEND_NO_GRIEF);
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    /// If force transfer is used, returns the vault. Else returns `address(0)`.
    function forceSafeTransferAllETH(address to) internal returns (address vault) {
        vault = forceSafeTransferETH(to, address(this).balance, GAS_STIPEND_NO_GRIEF);
    }

    /// @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, 0x00, 0x00, 0x00, 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(), 0x00, 0x00, 0x00, 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, 0x00, 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)
        }
    }
}

File 4 of 25 : 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)`.
    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 `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 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)
        }
    }
}

File 5 of 25 : Common.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import {INonfungiblePositionManager} from "./interfaces/INonfungiblePositionManager.sol";
import {IG8keepLockerFactory} from "./interfaces/IG8keepLockerFactory.sol";

struct Parameters {
    string TOKEN_NAME;
    string TOKEN_SYMBOL;
    uint256 FID;
    string IMAGE;
    string CAST_HASH;
    uint112 TOTAL_SUPPLY;
    address G8KEEP_FEE_WALLET;
    uint16 G8KEEP_FEE;
    uint16 LIQUIDITY_SUPPLEMENT_FEE;
    address DEPLOYER;
    uint112 DEPLOYER_REWARD;
    INonfungiblePositionManager UNISWAP_POSITION_MANAGER;
    IG8keepLockerFactory LOCKER_FACTORY;
    uint24 UNISWAP_FEE_TIER;
    address WETH;
    uint112 LIQUIDITY_SHIFT;
    uint112 MIGRATION_MINIMUM_LIQUIDITY;
    uint40 SNIPE_PROTECTION_SECONDS;
    uint40 SNIPE_PROTECTION_HEAVY_PENALTY_SECONDS;
    uint8 SNIPE_PROTECTION_HEAVY_EXPONENT_START;
    uint160 START_SQRT_PRICE;
}

struct CurveSettings {
    address deployer;
    uint112 deployerReward;
    address g8keepFeeWallet;
    uint16 g8keepFee;
    uint112 liquidityShift;
    uint16 liquiditySupplementFee;
    uint112 migrationMinimumLiquidity;
    address migrationPositionManager;
    uint24 migrationFeeTier;
    uint40 genesisTime;
    uint40 snipeProtectionEnd;
    uint40 snipeProtectionSeconds;
    uint40 snipeProtectionHeavySeconds;
    uint8 snipeProtectionHeavyExponentStart;
    address pairAddress;
    address lpLocker;
}

File 6 of 25 : g8keepBondingCurveFactory.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import {Ownable} from "solady/auth/Ownable.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {g8keepBondingCurve} from "./g8keepBondingCurve.sol";
import {g8keepLockerFactory} from "./g8keepLockerFactory.sol";
import {g8keepBondingCurveFactoryConfiguration} from "./g8keepBondingCurveFactoryConfiguration.sol";
import {INonfungiblePositionManager} from "./interfaces/INonfungiblePositionManager.sol";
import {IG8keepLockerFactory} from "./interfaces/IG8keepLockerFactory.sol";
import {IUniswapV3Factory} from "./interfaces/IUniswapV3Factory.sol";
import {TickMath} from "./libraries/TickMath.sol";
import {Parameters, CurveSettings} from "./Common.sol";

/**
 * @title   g8keepBondingCurveFactory
 * @notice  Factory for managing deployment parameters and deploying g8keep
 * @notice  bonding curve contracts.
 */
contract g8keepBondingCurveFactory is Ownable {
    /// @dev Uniswap Nonfungible Position Manager address for the chain.
    INonfungiblePositionManager public immutable UNISWAP_POSITION_MANAGER;
    /// @dev Uniswap Nonfungible Position Manager address for the chain.
    IG8keepLockerFactory public lockerFactory;
    /// @dev WETH address for the chain.
    address private immutable WETH;
    /// @dev Base URI for g8keepBondingCurve contract metadata.
    string public g8keepBaseContractUri = "https://g8.xyz/api/metadata";
    /// @dev Fee in native token, paid to g8keep, for the deployment of a bonding curve.
    uint112 public deploymentFee = 0 ether;
    /// @dev Initial supply of a deployed token.
    uint112 public tokenInitialSupply = 1_000_000_000 ether;
    /// @dev Fee in BPS assessed on token trades in curve, paid to g8keep. Initial value: 0.7%
    uint16 public g8keepFee = 70;
    /// @dev Uniswap Fee Tier
    uint24 public uniswapFeeTier = 10_000;
    /// @dev Fee in BPS assessed on token trades in curve to make up
    /// @dev deployer reward. Initial value: 0.3%
    uint16 public liquiditySupplementFee = 30;
    /// @dev Reward to a deployer whose token migrates.
    uint112 public deployerMigrationReward = 0.05 ether;
    /// @dev Initial shift in liquidity for a deployed curve.
    uint112 public tokenLiquidityShift = 0.5 ether;
    /// @dev Liquidity required for a token to migrate.
    uint112 public tokenMigrationLiquidity = 5 ether;
    /// @dev Time in seconds that a deployed curve will have snipe protection enabled.
    uint24 public snipeProtectionSeconds = 15 minutes;
    /// @dev Time in seconds that a deployed curve will have heavy snipe protection enabled.
    uint24 public heavySnipeProtectionSeconds = 7.5 minutes;
    /// @dev Exponent that a deployed curve will have for heavy snipe penalties.
    uint8 public heavySnipeExponent = 20;
    /// @dev Maximum amount of ETH that can be sent during deployment for a bundle buy.
    uint256 public maxBundleBuyAmount = 0.025 ether;
    /// @dev Uniswap V3 tick spacing based on the current fee tier.
    int24 private tickSpacing;
    /// @dev Uniswap V3 lower tick for the minimum viable price to set LP guard at based on the current fee tier.
    int24 private constrainedTickLower;
    /// @dev Uniswap V3 upper tick for the minimum viable price to set LP guard at based on the current fee tier.
    int24 private constrainedTickUpper;
    /// @dev Uniswap V3 sqrtPriceX96 for the lower tick to set LP guard at based on the current fee tier.
    uint160 private startSqrtPriceX96;

    /// @dev Mapping of token address to LP guard token ids.
    mapping (address => uint256) public bondingCurveLPGuardTokens;
    /// @dev Mapping of allowed fee wallets.
    mapping (address => bool) public allowedFeeWallets;

    /// @dev Minimum value that can be set for the lower tick for pre-migration LP guard.
    int24 private constant MIN_CONSTRAINED_TICK_LOWER = -665459;
    /// @dev Guardrail for `g8keepFee` to prevent it from being set over 100 BPS.
    uint16 private constant MAX_SETTING_G8KEEP_FEE = 100;
    /// @dev Guardrail for `liquiditySupplementFee` to prevent it from being set over 100 BPS.
    uint16 private constant MAX_SETTING_LIQUIDITY_SUPPLEMENT_FEE = 100;
    /// @dev Guardrail for `snipeProtectionSeconds` to prevent it from being set under 15 minutes.
    uint40 private constant MIN_SETTING_SNIPE_PROTECTION_SECONDS = 15 minutes;
    /// @dev Guardrail for `snipeProtectionSeconds` to prevent it from being set over 7 days.
    uint40 private constant MAX_SETTING_SNIPE_PROTECTION_SECONDS = 7 days;
    /// @dev Guardrail for `heavySnipeProtectionSeconds` to prevent it from being set under 5 minutes.
    uint40 private constant MIN_SETTING_HEAVY_SNIPE_PROTECTION_SECONDS = 5 minutes;
    /// @dev Guardrail for `heavySnipeProtectionSeconds` to prevent it from being set over 5 days.
    uint40 private constant MAX_SETTING_HEAVY_SNIPE_PROTECTION_SECONDS = 5 days;
    /// @dev Guardrail for `heavySnipeExponent` to prevent it from being set under 2.
    uint8 private constant MIN_SETTING_HEAVY_SNIPE_EXPONENT = 2;
    /// @dev Guardrail for `heavySnipeExponent` to prevent it from being set over 100.
    uint8 private constant MAX_SETTING_HEAVY_SNIPE_EXPONENT = 100;
    /// @dev Guardrail for `tokenInitialSupply` to prevent it from being set under 100K at 18 decimals.
    uint112 private constant MIN_SETTING_TOKEN_INITIAL_SUPPLY = 100_000 ether;
    /// @dev Guardrail for `tokenInitialSupply` to prevent it from being set over 100T at 18 decimals.
    uint112 private constant MAX_SETTING_TOKEN_INITIAL_SUPPLY = 100_000_000_000_000 ether;

    /// @dev Emitted when a token is deployed.
    event TokenDeployed(
        address indexed token, address indexed deployer, string symbol, uint40 snipeProtectionEnd, address pairAddress
    );
    /// @dev Emitted when a fee wallet allowance has been updated.
    event FeeWalletUpdated(address indexed feeWallet, bool allowed);

    /// @dev Emitted when the g8keepBondingCurveFactory owner updates deployment settings.
    event DeploymentSettingsUpdated(
        uint112 deploymentFee,
        uint112 tokenInitialSupply,
        uint16 g8keepFee,
        uint24 uniswapFeeTier,
        uint16 liquiditySupplementFee,
        uint112 deployerMigrationReward,
        uint112 tokenLiquidityShift,
        uint112 tokenMigrationLiquidity,
        uint24 snipeProtectionSeconds,
        uint24 heavySnipeProtectionSeconds,
        uint8 heavySnipeExponent
    );

    /// @dev Thrown when the supplied value is not sufficient for the deployment fee.
    error InvalidDeploymentFee();
    /// @dev Thrown when including value for a bundle buy and it exceeds the maximum allowed.
    error InvalidBundleBuy();
    /// @dev Thrown when the g8keepBondingCurveFactory owner is updating deployment settings and
    /// @dev they are not compliant with the constant guardrails.
    error InvalidSettings();
    /// @dev Thrown when attempting to use a fee wallet that is not an allowed fee wallet.
    error InvalidFeeWallet();
    /// @dev Thrown when the token address is greater than the WETH address.
    error InvalidTokenAddress();
    /// @dev Thrown when withdrawing native token from the contract and the withdrawal fails.
    error WithdrawalFailed();
    /// @dev Thrown when a supplied address that must be non-zero is zero.
    error ZeroAddress();

    /**
     * @dev Constructs the g8keepBondingCurveFactory contract.
     *
     * @dev Initialization parameters are retrieved from a configuration contract so that
     * @dev the g8keepBondingCurveFactory may be deterministically deployed on EVM chains
     * @dev at the same address.
     *
     * @param _configuration  The address of the factory configuration contract to retrieve configuration parameters.
     */
    constructor(address _configuration) {
        g8keepBondingCurveFactoryConfiguration config = g8keepBondingCurveFactoryConfiguration(_configuration);
        (address _defaultOwner, address _g8keepFeeWallet, address _uniswapPositionManager, uint24 _uniswapFeeTier) =
            config.getBondingCurveFactoryConfiguration();

        if (
            _defaultOwner == address(0) || _g8keepFeeWallet == address(0) || _uniswapPositionManager == address(0)
                || _uniswapFeeTier == 0
        ) {
            revert InvalidSettings();
        }

        _initializeOwner(_defaultOwner);
        UNISWAP_POSITION_MANAGER = INonfungiblePositionManager(_uniswapPositionManager);
        int24 _tickSpacing = IUniswapV3Factory(UNISWAP_POSITION_MANAGER.factory()).feeAmountTickSpacing(_uniswapFeeTier);
        if (_tickSpacing == 0) {
            revert InvalidSettings();
        }
        tickSpacing = _tickSpacing;
        (startSqrtPriceX96, constrainedTickLower, constrainedTickUpper) = _tickSpacingToMinimumPrice(_tickSpacing);
        allowedFeeWallets[_g8keepFeeWallet] = true;
        emit FeeWalletUpdated(_g8keepFeeWallet, true);
        
        uniswapFeeTier = _uniswapFeeTier;
        WETH = UNISWAP_POSITION_MANAGER.WETH9();

        lockerFactory = IG8keepLockerFactory(
            address(new g8keepLockerFactory(_defaultOwner, _g8keepFeeWallet, UNISWAP_POSITION_MANAGER))
        );
    }

    /////////// PUBLIC FUNCTIONS ///////////

    /**
     * @notice Deploys a g8keepBondingCurve contract using current settings, creates an LP guard token to 
     * @notice prevent price movement in Uniswap V3 pair prior to token migration, executes a bundle buy
     * @notice if value has been supplied for a bundle buy.
     *
     * @param _g8keepFeeWallet         Address of the fee wallet for the deployment.
     * @param _deployer                Address of the deployer for the token.
     * @param _name                    Name of the token being deployed.
     * @param _symbol                  Symbol of the token being deployed.
     * @param _fid                     Farcaster ID for the token deployer.
     * @param _image                   Image of token if deployed off g8keep
     * @param _castHash                Hash of the cast for the token if deployed off g8keep
     * @param _tokenSalt               Salt for token deployment to generate a deterministic address.
     */
    function deployToken(
        address _g8keepFeeWallet,
        address _deployer,
        string memory _name,
        string memory _symbol,
        uint256 _fid,
        string memory _image,
        string memory _castHash,
        bytes32 _tokenSalt
    ) external payable returns (address _tokenAddress) {
        if (!allowedFeeWallets[_g8keepFeeWallet]) {
            revert InvalidFeeWallet();
        }
        uint256 bundleBuyAmount = msg.value;
        uint256 _deploymentFee = deploymentFee;
        if (msg.value < _deploymentFee) revert InvalidDeploymentFee();
        if (_deploymentFee > 0) {
            bundleBuyAmount -= _deploymentFee;
            SafeTransferLib.safeTransferETH(_g8keepFeeWallet, _deploymentFee);
        }
        if (bundleBuyAmount > maxBundleBuyAmount) {
            revert InvalidBundleBuy();
        }

        uint24 _uniswapFeeTier = uniswapFeeTier;

        _tokenAddress = address(new g8keepBondingCurve{salt: _tokenSalt}(
            Parameters({
                TOKEN_NAME: _name,
                TOKEN_SYMBOL: _symbol,
                FID: _fid,
                IMAGE: _image,
                CAST_HASH: _castHash,
                TOTAL_SUPPLY: tokenInitialSupply,
                G8KEEP_FEE_WALLET: _g8keepFeeWallet,
                G8KEEP_FEE: g8keepFee,
                LIQUIDITY_SUPPLEMENT_FEE: liquiditySupplementFee,
                DEPLOYER: _deployer,
                DEPLOYER_REWARD: deployerMigrationReward,
                UNISWAP_POSITION_MANAGER: UNISWAP_POSITION_MANAGER,
                LOCKER_FACTORY: lockerFactory,
                UNISWAP_FEE_TIER: _uniswapFeeTier,
                WETH: WETH,
                LIQUIDITY_SHIFT: tokenLiquidityShift,
                MIGRATION_MINIMUM_LIQUIDITY: tokenMigrationLiquidity,
                SNIPE_PROTECTION_SECONDS: snipeProtectionSeconds,
                SNIPE_PROTECTION_HEAVY_PENALTY_SECONDS: heavySnipeProtectionSeconds,
                SNIPE_PROTECTION_HEAVY_EXPONENT_START: heavySnipeExponent,
                START_SQRT_PRICE: startSqrtPriceX96
            })
        ));

        if (_tokenAddress == address(0) || _tokenAddress > WETH) {
            revert InvalidTokenAddress();
        }

        _addLPGuardLiquidity(_tokenAddress, _uniswapFeeTier);

        if (bundleBuyAmount > 0) {
            g8keepBondingCurve(payable(_tokenAddress)).buy{value: bundleBuyAmount}(_deployer, 0);
        }

        CurveSettings memory curveSettings = g8keepBondingCurve(payable(_tokenAddress)).getCurveSettings();

        emit TokenDeployed(
            _tokenAddress, _deployer, _symbol, curveSettings.snipeProtectionEnd, curveSettings.pairAddress
        );
    }

    function buy(address token, uint256 minimumAmount) external payable {
        g8keepBondingCurve(payable(token)).buy{value: msg.value}(msg.sender, minimumAmount);
    }

    function sell(address token, uint112 amount, uint112 minimumOut) external payable {
        g8keepBondingCurve(payable(token)).factorySell(msg.sender, amount, minimumOut);
    }

    /////////// OWNER FUNCTIONS ///////////

    /**
     * @notice  Admin function to configure deployment parameters.
     *
     * @dev     Settings parameters must be within the constant guardrails defined.
     *
     * @param _deploymentFee                Fee in native token for deploying a bonding curve.
     * @param _tokenInitialSupply           Initial supply of bonding curve tokens.
     * @param _g8keepFee                    Fee in BPS to assess on token trades.
     * @param _uniswapFeeTier               Fee tier when migrating to Uniswap.
     * @param _liquiditySupplementFee       Minimum time in seconds that a deployer tokens must vest over.
     * @param _deployerMigrationReward      Reward to the curve deployer when a token migrates.
     * @param _tokenLiquidityShift          Initial liquidity shift for a bonding curve.
     * @param _tokenMigrationLiquidity      Liquidity required to migrate a bonding curve.
     * @param _snipeProtectionSeconds       Time in seconds for snipe protection to be enabled.
     * @param _heavySnipeProtectionSeconds  Time in seconds for heavy snipe protection to be enabled.
     * @param _heavySnipeExponent           Exponent penalty during heavy snipe protection.
     */
    function setDeploymentSettings(
        uint112 _deploymentFee,
        uint112 _tokenInitialSupply,
        uint16 _g8keepFee,
        uint24 _uniswapFeeTier,
        uint16 _liquiditySupplementFee,
        uint112 _deployerMigrationReward,
        uint112 _tokenLiquidityShift,
        uint112 _tokenMigrationLiquidity,
        uint24 _snipeProtectionSeconds,
        uint24 _heavySnipeProtectionSeconds,
        uint8 _heavySnipeExponent
    ) external onlyOwner {
        if (
            _tokenInitialSupply < MIN_SETTING_TOKEN_INITIAL_SUPPLY
                || _tokenInitialSupply > MAX_SETTING_TOKEN_INITIAL_SUPPLY || _g8keepFee > MAX_SETTING_G8KEEP_FEE
                || _tokenLiquidityShift > _tokenMigrationLiquidity
                || _liquiditySupplementFee > MAX_SETTING_LIQUIDITY_SUPPLEMENT_FEE
                || _snipeProtectionSeconds < MIN_SETTING_SNIPE_PROTECTION_SECONDS
                || _snipeProtectionSeconds > MAX_SETTING_SNIPE_PROTECTION_SECONDS
                || _heavySnipeProtectionSeconds > _snipeProtectionSeconds
                || _heavySnipeProtectionSeconds < MIN_SETTING_HEAVY_SNIPE_PROTECTION_SECONDS
                || _heavySnipeProtectionSeconds > MAX_SETTING_HEAVY_SNIPE_PROTECTION_SECONDS
                || _heavySnipeExponent < MIN_SETTING_HEAVY_SNIPE_EXPONENT
                || _heavySnipeExponent > MAX_SETTING_HEAVY_SNIPE_EXPONENT
        ) {
            revert InvalidSettings();
        }

        int24 _tickSpacing = IUniswapV3Factory(UNISWAP_POSITION_MANAGER.factory()).feeAmountTickSpacing(_uniswapFeeTier);
        if (tickSpacing == 0) {
            revert InvalidSettings();
        }
        tickSpacing = _tickSpacing;
        (startSqrtPriceX96, constrainedTickLower, constrainedTickUpper) = _tickSpacingToMinimumPrice(_tickSpacing);

        deploymentFee = _deploymentFee;
        tokenInitialSupply = _tokenInitialSupply;
        g8keepFee = _g8keepFee;
        uniswapFeeTier = _uniswapFeeTier;
        liquiditySupplementFee = _liquiditySupplementFee;
        deployerMigrationReward = _deployerMigrationReward;
        tokenLiquidityShift = _tokenLiquidityShift;
        tokenMigrationLiquidity = _tokenMigrationLiquidity;
        snipeProtectionSeconds = _snipeProtectionSeconds;
        heavySnipeProtectionSeconds = _heavySnipeProtectionSeconds;
        heavySnipeExponent = _heavySnipeExponent;

        emit DeploymentSettingsUpdated(
            _deploymentFee,
            _tokenInitialSupply,
            _g8keepFee,
            _uniswapFeeTier,
            _liquiditySupplementFee,
            _deployerMigrationReward,
            _tokenLiquidityShift,
            _tokenMigrationLiquidity,
            _snipeProtectionSeconds,
            _heavySnipeProtectionSeconds,
            _heavySnipeExponent
        );
    }

    /**
     * @notice  Admin function to set the address of the current locker factory.
     * 
     * @dev     The locker factory must contain code to be set.
     * 
     * @param _lockerFactory  Address of the locker factory.
     */
    function setG8keepLockerFactory(address _lockerFactory) external onlyOwner {
        if (_lockerFactory.code.length == 0) {
            revert InvalidSettings();
        }

        lockerFactory = IG8keepLockerFactory(_lockerFactory);
    }

    /**
     * @notice  Admin function to set the maximum value of a bundle buy.
     *
     * @param _maxBundleBuyAmount  Amount to set as the max bundle buy without a revert on deployment.
     */
    function setMaximumBundleBuy(uint256 _maxBundleBuyAmount) external onlyOwner {
        maxBundleBuyAmount = _maxBundleBuyAmount;
    }

    /**
     * @notice  Admin function to set an allowed g8keep fee wallet to receive initial liquidity fees.
     *
     * @dev     The fee wallet cannot be set to the zero address.
     *
     * @param _g8keepFeeWallet  Address of the g8keep fee wallet.
     */
    function setAllowedG8keepFeeWallet(address _g8keepFeeWallet, bool allowed) external onlyOwner {
        if (_g8keepFeeWallet == address(0)) {
            revert ZeroAddress();
        }

        allowedFeeWallets[_g8keepFeeWallet] = allowed;

        emit FeeWalletUpdated(_g8keepFeeWallet, allowed);
    }

    /**
     * @notice  Admin function to withdraw a token that is held by the factory contract.
     *
     * @dev     Will withdraw the entire balance of the token.
     *
     * @param tokenAddress  Address of the token to withdraw.
     * @param to            Address to withdraw the token to.
     */
    function withdrawToken(address tokenAddress, address to) external onlyOwner {
        if (tokenAddress == address(UNISWAP_POSITION_MANAGER)) {
            revert InvalidTokenAddress();
        }
        SafeTransferLib.safeTransferAll(tokenAddress, to);
    }

    /**
     * @notice  Admin function to withdraw a token that is held by the factory contract.
     *
     * @dev     Will withdraw the specified `amount` of token.
     *
     * @param tokenAddress  Address of the token to withdraw.
     * @param to            Address to withdraw the token to.
     * @param amount        Amount of the token to withdraw.
     */
    function withdrawToken(address tokenAddress, address to, uint256 amount) external onlyOwner {
        if (tokenAddress == address(UNISWAP_POSITION_MANAGER)) {
            revert InvalidTokenAddress();
        }
        SafeTransferLib.safeTransfer(tokenAddress, to, amount);
    }

    /**
     * @notice  Admin function to withdraw native token that is held by the factory contract.
     *
     * @dev     Will withdraw the entire balance.
     *
     * @param to            Address to withdraw the token to.
     */
    function withdrawETH(address to) external onlyOwner {
        if (to == address(0)) revert ZeroAddress();

        (bool success,) = to.call{value: address(this).balance}("");
        if (!success) revert WithdrawalFailed();
    }

    /////////// LP GUARD FUNCTIONS ///////////

    /**
     * @dev  Internal function to create an LP guard to prevent LP positions from being created
     * @dev  and price to move prior to the token migrating to Uniswap V3.
     * 
     * @param tokenAddress     Address of the token to add the LP guard for.
     * @param _uniswapFeeTier  Fee tier the token will migrate to.
     */
    function _addLPGuardLiquidity(address tokenAddress, uint24 _uniswapFeeTier) internal {
        g8keepBondingCurve(payable(tokenAddress)).approve(address(UNISWAP_POSITION_MANAGER), type(uint256).max);

        INonfungiblePositionManager.MintParams memory params = INonfungiblePositionManager.MintParams({
            token0: tokenAddress,
            token1: address(WETH),
            fee: _uniswapFeeTier,
            tickLower: constrainedTickLower,
            tickUpper: constrainedTickUpper,
            amount0Desired: 1 ether,
            amount1Desired: 0,
            amount0Min: 0,
            amount1Min: 0,
            recipient: address(this),
            deadline: block.timestamp
        });

        (uint256 lpTokenId,,,) = UNISWAP_POSITION_MANAGER.mint(params);
        bondingCurveLPGuardTokens[tokenAddress] = lpTokenId;
    }

    /**
     * @dev  Restricted function called by a token during migration to remove the LP guard.
     * @dev  Reverts if the caller does not have an LP guard set.
     */
    function removeLPGuardLiquidity() external {
        address tokenAddress = msg.sender;

        uint256 lpTokenId = bondingCurveLPGuardTokens[tokenAddress];
        if (lpTokenId == 0) {
            revert InvalidTokenAddress();
        }
        bondingCurveLPGuardTokens[tokenAddress] = 0;

        (,,,,,,, uint128 liquidity,,,,) = UNISWAP_POSITION_MANAGER.positions(lpTokenId);
        INonfungiblePositionManager.DecreaseLiquidityParams memory liquidityParams = INonfungiblePositionManager
            .DecreaseLiquidityParams({
            tokenId: lpTokenId,
            liquidity: liquidity,
            amount0Min: 0,
            amount1Min: 0,
            deadline: block.timestamp
        });

        UNISWAP_POSITION_MANAGER.decreaseLiquidity(liquidityParams);

        (,,,,,,,,,, uint128 tokensOwed0, uint128 tokensOwed1) = UNISWAP_POSITION_MANAGER.positions(lpTokenId);
        INonfungiblePositionManager.CollectParams memory collectParams = INonfungiblePositionManager.CollectParams({
            tokenId: lpTokenId,
            recipient: tokenAddress,
            amount0Max: tokensOwed0,
            amount1Max: tokensOwed1
        });
        UNISWAP_POSITION_MANAGER.collect(collectParams);

        UNISWAP_POSITION_MANAGER.burn(lpTokenId);
    }

    /**
     *  @dev  Allows a token contract to transfer dust remaining from guard tokens to an address it specifies.
     */
    function transferGuardTokens(address to) external {
        SafeTransferLib.safeTransferAll(msg.sender, to);
    }

    /**
     * @dev  Internal function to calculate the Uniswap V3 pool parameters based on the fee tier tick spacing.
     * 
     * @param _tickSpacing  Uniswap V3 tick spacing based on the current fee tier.
     * 
     * @return _startSqrtPriceX96     Uniswap V3 sqrtPriceX96 based on the constrained lower tick.
     * @return _constrainedTickLower  Uniswap V3 minimum viable lower tick based on current fee tier tick spacing.
     * @return _constrainedTickUpper  Uniswap V3 minimum viable upper tick based on current fee tier tick spacing.
     */
    function _tickSpacingToMinimumPrice(int24 _tickSpacing) internal pure returns (uint160 _startSqrtPriceX96, int24 _constrainedTickLower, int24 _constrainedTickUpper) {
        _constrainedTickLower = MIN_CONSTRAINED_TICK_LOWER;
        unchecked {
            _constrainedTickLower = _constrainedTickLower / _tickSpacing * _tickSpacing;
            _constrainedTickUpper = _constrainedTickLower + _tickSpacing;
            _startSqrtPriceX96 = TickMath.getSqrtRatioAtTick(_constrainedTickLower);
        }
    }

    /**
     * @dev Admin function to retrieve the contract URI for a deployed g8keepBondingCurve contract.
     *
     * @param contractAddress  Address of the deployed g8keepBondingCurve contract.
     *
     * @return _contractURI  URI for the g8keepBondingCurve contract metadata.
     */
    function contractURI(
        address contractAddress
    ) external view returns (string memory _contractURI) {
        _contractURI = string(
            abi.encodePacked(
                g8keepBaseContractUri,
                "/",
                _toString(block.chainid),
                "/0x",
                _toString(contractAddress)
            )
        );
    }

    /**
     * @dev  Admin function to set the base URI for the g8keepBondingCurve contract metadata.
     *
     * @param _g8keepBaseContractUri  Base URI for the g8keepBondingCurve contract metadata.
     */
    function setg8keepBaseContractUri(
        string calldata _g8keepBaseContractUri
    ) external onlyOwner {
        g8keepBaseContractUri = _g8keepBaseContractUri;
    }

    /**
     * @dev  Internal function to convert a uint256 to a string.
     * 
     * @param value  Value to convert to a string.
     */
    function _toString(uint256 value) internal pure virtual returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := add(mload(0x40), 0xa0)
            mstore(0x40, m)
            str := sub(m, 0x20)
            mstore(str, 0)
            let end := str

            for { let temp := value } 1 {} {
                str := sub(str, 1)
                mstore8(str, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            str := sub(str, 0x20)
            mstore(str, length)
        }
    }

    /**
     * @dev Internal function to convert an address to a string.
     *
     * @param value  Address to convert to a string.
     */
    function _toString(address value) internal pure returns (string memory) {
        bytes memory s = new bytes(40);
        for (uint i = 0; i < 20; ) {
            bytes1 b = bytes1(uint8(uint(uint160(value)) / (2**(8*(19 - i)))));
            bytes1 hi = bytes1(uint8(b) / 16);
            bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi));
            s[2*i] = _char(hi);
            s[2*i+1] = _char(lo);
            unchecked { ++i; }
        }
        return string(s);
    }

    /**
     * @dev Internal function to convert a bytes1 to a bytes1.
     * 
     * @param b  Bytes1 to convert to a bytes1.
     */
    function _char(bytes1 b) internal pure returns (bytes1 c) {
        if (uint8(b) < 10) return bytes1(uint8(b) + 0x30);
        else return bytes1(uint8(b) + 0x57);
    }
}

File 7 of 25 : TickMath.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.26;

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
    int24 internal constant MIN_TICK = -887272;
    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
    int24 internal constant MAX_TICK = -MIN_TICK;

    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
    uint160 internal constant MIN_SQRT_RATIO = 4295128739;
    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    /// @notice Calculates sqrt(1.0001^tick) * 2^96
    /// @dev Throws if |tick| > max tick
    /// @param tick The input tick for the above formula
    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
    /// at the given tick
    function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
        uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
        require(absTick <= uint256(int256(MAX_TICK)), "T");

        uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
        if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
        if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
        if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
        if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
        if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
        if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
        if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
        if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
        if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
        if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
        if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
        if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
        if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
        if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
        if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
        if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
        if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
        if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
        if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;

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

        // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
        // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
        // we round up in the division so getTickAtSqrtRatio of the output price is always consistent
        sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
    }

    /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
    /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
    /// ever return.
    /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
    /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
    function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
        // second inequality must be < because the price can never reach the price at the max tick
        require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, "R");
        uint256 ratio = uint256(sqrtPriceX96) << 32;

        uint256 r = ratio;
        uint256 msb = 0;

        assembly {
            let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(5, gt(r, 0xFFFFFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(4, gt(r, 0xFFFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(3, gt(r, 0xFF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(2, gt(r, 0xF))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := shl(1, gt(r, 0x3))
            msb := or(msb, f)
            r := shr(f, r)
        }
        assembly {
            let f := gt(r, 0x1)
            msb := or(msb, f)
        }

        if (msb >= 128) r = ratio >> (msb - 127);
        else r = ratio << (127 - msb);

        int256 log_2 = (int256(msb) - 128) << 64;

        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(63, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(62, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(61, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(60, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(59, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(58, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(57, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(56, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(55, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(54, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(53, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(52, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(51, f))
            r := shr(f, r)
        }
        assembly {
            r := shr(127, mul(r, r))
            let f := shr(128, r)
            log_2 := or(log_2, shl(50, f))
        }

        int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number

        int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
        int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);

        tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
    }
}

File 8 of 25 : IWETH.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

interface IWETH is IERC20 {
    function deposit() external payable;
    function withdraw(uint256) external;
}

File 9 of 25 : INonfungiblePositionManager.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./IPoolInitializer.sol";
import "./IPeripheryImmutableState.sol";

/// @title Non-fungible token for positions
/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface INonfungiblePositionManager is IERC721, IPoolInitializer, IPeripheryImmutableState {
    /// @notice Emitted when liquidity is increased for a position NFT
    /// @dev Also emitted when a token is minted
    /// @param tokenId The ID of the token for which liquidity was increased
    /// @param liquidity The amount by which liquidity for the NFT position was increased
    /// @param amount0 The amount of token0 that was paid for the increase in liquidity
    /// @param amount1 The amount of token1 that was paid for the increase in liquidity
    event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
    /// @notice Emitted when liquidity is decreased for a position NFT
    /// @param tokenId The ID of the token for which liquidity was decreased
    /// @param liquidity The amount by which liquidity for the NFT position was decreased
    /// @param amount0 The amount of token0 that was accounted for the decrease in liquidity
    /// @param amount1 The amount of token1 that was accounted for the decrease in liquidity
    event DecreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
    /// @notice Emitted when tokens are collected for a position NFT
    /// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior
    /// @param tokenId The ID of the token for which underlying tokens were collected
    /// @param recipient The address of the account that received the collected tokens
    /// @param amount0 The amount of token0 owed to the position that was collected
    /// @param amount1 The amount of token1 owed to the position that was collected
    event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1);

    /// @notice Returns the position information associated with a given token ID.
    /// @dev Throws if the token ID is not valid.
    /// @param tokenId The ID of the token that represents the position
    /// @return nonce The nonce for permits
    /// @return operator The address that is approved for spending
    /// @return token0 The address of the token0 for a specific pool
    /// @return token1 The address of the token1 for a specific pool
    /// @return fee The fee associated with the pool
    /// @return tickLower The lower end of the tick range for the position
    /// @return tickUpper The higher end of the tick range for the position
    /// @return liquidity The liquidity of the position
    /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
    /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
    /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
    /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation
    function positions(uint256 tokenId)
        external
        view
        returns (
            uint96 nonce,
            address operator,
            address token0,
            address token1,
            uint24 fee,
            int24 tickLower,
            int24 tickUpper,
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    /// @notice Creates a new position wrapped in a NFT
    /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
    /// a method does not exist, i.e. the pool is assumed to be initialized.
    /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
    /// @return tokenId The ID of the token that represents the minted position
    /// @return liquidity The amount of liquidity for this position
    /// @return amount0 The amount of token0
    /// @return amount1 The amount of token1
    function mint(MintParams calldata params)
        external
        payable
        returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);

    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`
    /// @param params tokenId The ID of the token for which liquidity is being increased,
    /// amount0Desired The desired amount of token0 to be spent,
    /// amount1Desired The desired amount of token1 to be spent,
    /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
    /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
    /// deadline The time by which the transaction must be included to effect the change
    /// @return liquidity The new liquidity amount as a result of the increase
    /// @return amount0 The amount of token0 to acheive resulting liquidity
    /// @return amount1 The amount of token1 to acheive resulting liquidity
    function increaseLiquidity(IncreaseLiquidityParams calldata params)
        external
        payable
        returns (uint128 liquidity, uint256 amount0, uint256 amount1);

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    /// @notice Decreases the amount of liquidity in a position and accounts it to the position
    /// @param params tokenId The ID of the token for which liquidity is being decreased,
    /// amount The amount by which liquidity will be decreased,
    /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
    /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
    /// deadline The time by which the transaction must be included to effect the change
    /// @return amount0 The amount of token0 accounted to the position's tokens owed
    /// @return amount1 The amount of token1 accounted to the position's tokens owed
    function decreaseLiquidity(DecreaseLiquidityParams calldata params)
        external
        payable
        returns (uint256 amount0, uint256 amount1);

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
    /// @param params tokenId The ID of the NFT for which tokens are being collected,
    /// recipient The account that should receive the tokens,
    /// amount0Max The maximum amount of token0 to collect,
    /// amount1Max The maximum amount of token1 to collect
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

    /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens
    /// must be collected first.
    /// @param tokenId The ID of the token that is being burned
    function burn(uint256 tokenId) external payable;
}

File 10 of 25 : IG8keepLockerFactory.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

interface IG8keepLockerFactory {
    function deploy(uint256 lpTokenId, address beneficiary, address feeWallet) external returns (address lpLocker);
}

File 11 of 25 : IUniswapV3Factory.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the Uniswap V3 Factory
/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
interface IUniswapV3Factory {
    /// @notice Emitted when the owner of the factory is changed
    /// @param oldOwner The owner before the owner was changed
    /// @param newOwner The owner after the owner was changed
    event OwnerChanged(address indexed oldOwner, address indexed newOwner);

    /// @notice Emitted when a pool is created
    /// @param token0 The first token of the pool by address sort order
    /// @param token1 The second token of the pool by address sort order
    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
    /// @param tickSpacing The minimum number of ticks between initialized ticks
    /// @param pool The address of the created pool
    event PoolCreated(
        address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool
    );

    /// @notice Emitted when a new fee amount is enabled for pool creation via the factory
    /// @param fee The enabled fee, denominated in hundredths of a bip
    /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
    event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);

    /// @notice Returns the current owner of the factory
    /// @dev Can be changed by the current owner via setOwner
    /// @return The address of the factory owner
    function owner() external view returns (address);

    /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
    /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
    /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
    /// @return The tick spacing
    function feeAmountTickSpacing(uint24 fee) external view returns (int24);

    /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
    /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
    /// @param tokenA The contract address of either token0 or token1
    /// @param tokenB The contract address of the other token
    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
    /// @return pool The pool address
    function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);

    /// @notice Creates a pool for the given two tokens and fee
    /// @param tokenA One of the two tokens in the desired pool
    /// @param tokenB The other of the two tokens in the desired pool
    /// @param fee The desired fee for the pool
    /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
    /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
    /// are invalid.
    /// @return pool The address of the newly created pool
    function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool);

    /// @notice Updates the owner of the factory
    /// @dev Must be called by the current owner
    /// @param _owner The new owner of the factory
    function setOwner(address _owner) external;

    /// @notice Enables a fee amount with the given tickSpacing
    /// @dev Fee amounts may never be removed once enabled
    /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
    /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
    function enableFeeAmount(uint24 fee, int24 tickSpacing) external;
}

File 12 of 25 : IUniswapV3Pool.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import "./IUniswapV3PoolActions.sol";
import "./IUniswapV3PoolImmutables.sol";
import "./IUniswapV3PoolState.sol";

/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is IUniswapV3PoolActions, IUniswapV3PoolImmutables, IUniswapV3PoolState {
    function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
        external
        view
        returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside);
}

File 13 of 25 : SingleUseETHVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice A single-use vault that allows a designated caller to withdraw all ETH in it.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ext/zksync/SingleUseETHVault.sol)
contract SingleUseETHVault {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unable to withdraw all.
    error WithdrawAllFailed();

    /// @dev Not authorized.
    error Unauthorized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        WITHDRAW ALL                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    fallback() external payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x40, 0) // Optimization trick to remove free memory pointer initialization.
            let owner := sload(0)
            // Initialization.
            if iszero(owner) {
                sstore(0, calldataload(0x00)) // Store the owner.
                return(0x00, 0x00) // Early return.
            }
            // Authorization check.
            if iszero(eq(caller(), owner)) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
            let to := calldataload(0x00)
            // If the calldata is less than 32 bytes, zero-left-pad it to 32 bytes.
            // Then use the rightmost 20 bytes of the word as the `to` address.
            // This allows for the calldata to be `abi.encode(to)` or `abi.encodePacked(to)`.
            to := shr(mul(lt(calldatasize(), 0x20), shl(3, sub(0x20, calldatasize()))), to)
            // If `to` is `address(0)`, set it to `msg.sender`.
            to := xor(mul(xor(to, caller()), iszero(to)), to)
            // Transfers the whole balance to `to`.
            if iszero(call(gas(), to, selfbalance(), 0x00, 0x00, 0x00, 0x00)) {
                mstore(0x00, 0x651aee10) // `WithdrawAllFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

File 14 of 25 : Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

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

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

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

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

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

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

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

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

File 15 of 25 : SafeTransferLib.sol
// 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)
            }
        }
    }
}

File 16 of 25 : g8keepLockerFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {INonfungiblePositionManager} from "./interfaces/INonfungiblePositionManager.sol";
import {IG8keepLockerFactory} from "./interfaces/IG8keepLockerFactory.sol";
import {g8keepLiquidityLocker} from "./g8keepLiquidityLocker.sol";
import {Ownable} from "solady/auth/Ownable.sol";

/**
 * @title   g8keepLockerFactory
 * @notice  Factory contract for deploying liquidity locker contracts.
 */
contract g8keepLockerFactory is Ownable, IG8keepLockerFactory {
    /// @dev Emitted when a liquidity locker is deployed.
    event LockerDeployed(
        address indexed lockerAddress, address indexed owner, uint256 lpTokenId, uint256 durationSeconds
    );

    /// @dev Constant value for the max BPS value.
    uint16 private constant BPS = 10_000;

    /// @dev Address of the Uniswap V3 position manager.
    INonfungiblePositionManager public immutable UNISWAP_POSITION_MANAGER;
    /// @dev Address of the g8keep fee collector.
    address public feeCollector;
    /// @dev Amount of time, in seconds, that liquidity positions will be locked.
    uint64 public lockDuration = 75 * 365 days;
    /// @dev Amount of LP fees, in BPS, that will be allocated to g8keep.
    uint16 public lpFeesCut = 6_000; // 60%

    /// @dev Thrown when a supplied address that must be non-zero is zero.
    error ZeroAddress();
    /// @dev Thrown when a locker fails to deploy.
    error LockerFailedToDeploy();
    /// @dev Thrown when a setting is supplied that is invalid.
    error InvalidSettings();

    constructor(address _defaultOwner, address _feeCollector, INonfungiblePositionManager _uniswapPositionManager) {
        _initializeOwner(_defaultOwner);
        feeCollector = _feeCollector;
        UNISWAP_POSITION_MANAGER = _uniswapPositionManager;
    }

    /**
     * @notice  Deploys a new liquidity locker for a Uniswap V3 position.
     * 
     * @dev     Locker factory must have approval to transfer the LP token.
     *
     * @param lpTokenId    ID of the LP token to be locked.
     * @param beneficiary  Address of the wallet that will own the LP locker and receive fees.
     * @param feeWallet    Address of the g8keep fee wallet that will receive g8keep fees.
     *
     * @return lpLocker  Address of the deployed LP locker.
     */
    function deploy(uint256 lpTokenId, address beneficiary, address feeWallet) external returns (address lpLocker) {
        uint256 _lockDuration = lockDuration;
        lpLocker = address(
            new g8keepLiquidityLocker(
                UNISWAP_POSITION_MANAGER, lpTokenId, beneficiary, _lockDuration, lpFeesCut, feeWallet
            )
        );

        if (lpLocker == address(0)) {
            revert LockerFailedToDeploy();
        }

        UNISWAP_POSITION_MANAGER.transferFrom(msg.sender, lpLocker, lpTokenId);

        emit LockerDeployed(lpLocker, beneficiary, lpTokenId, _lockDuration);
    }

    /**
     * @notice  Admin function to update the LP locker settings.
     * 
     * @param _feeCollector  Address of the account that is allowed to initiate fee collection on behalf of g8keep.
     * @param _lockDuration  Time, in seconds, that LP positions will be locked.
     * @param _lpFeesCut     Fee rate, in BPS, that will be allocated to g8keep.
     */
    function setLockerSettings(address _feeCollector, uint64 _lockDuration, uint16 _lpFeesCut) external onlyOwner {
        if (_lpFeesCut > BPS) revert InvalidSettings();
        if (_feeCollector == address(0)) {
            revert ZeroAddress();
        }

        feeCollector = _feeCollector;
        lockDuration = _lockDuration;
        lpFeesCut = _lpFeesCut;
    }
}

File 17 of 25 : g8keepBondingCurveFactoryConfiguration.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import {Ownable} from "solady/auth/Ownable.sol";

/**
 * @title   g8keepBondingCurveFactoryConfiguration
 * @notice  Configuration contract for factory deployment to allow deterministic 
 * @notice  addresses across chains that have different Uniswap addresses.
 */
contract g8keepBondingCurveFactoryConfiguration is Ownable {
    address private g8keepBondingCurveFactoryAdmin;
    address private g8keepFeeWallet;
    address private uniswapPositionManager;
    uint24 private uniswapFeeTier;

    error ZeroAddress();

    constructor(address _configurationOwner) {
        _initializeOwner(_configurationOwner);
    }

    function setBondingCurveFactoryConfiguration(
        address _g8keepFactoryAdmin,
        address _g8keepFeeWallet,
        address _uniswapPositionManager,
        uint24 _uniswapFeeTier
    ) external onlyOwner {
        if (
            _g8keepFactoryAdmin == address(0) || _g8keepFeeWallet == address(0) || _uniswapPositionManager == address(0)
                || _uniswapFeeTier == 0
        ) {
            revert ZeroAddress();
        }

        g8keepBondingCurveFactoryAdmin = _g8keepFactoryAdmin;
        g8keepFeeWallet = _g8keepFeeWallet;
        uniswapPositionManager = _uniswapPositionManager;
        uniswapFeeTier = _uniswapFeeTier;
    }

    function getBondingCurveFactoryConfiguration()
        external
        view
        returns (
            address _g8keepBondingCurveFactoryAdmin,
            address _g8keepFeeWallet,
            address _uniswapPositionManager,
            uint24 _uniswapFeeTier
        )
    {
        _g8keepBondingCurveFactoryAdmin = g8keepBondingCurveFactoryAdmin;
        _g8keepFeeWallet = g8keepFeeWallet;
        _uniswapPositionManager = uniswapPositionManager;
        _uniswapFeeTier = uniswapFeeTier;
    }
}

File 18 of 25 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC-721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

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

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - 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 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - 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 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 19 of 25 : IPoolInitializer.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

/// @title Creates and initializes V3 Pools
/// @notice Provides a method for creating and initializing a pool, if necessary, for bundling with other methods that
/// require the pool to exist.
interface IPoolInitializer {
    /// @notice Creates a new pool if it does not exist, then initializes if not initialized
    /// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool
    /// @param token0 The contract address of token0 of the pool
    /// @param token1 The contract address of token1 of the pool
    /// @param fee The fee amount of the v3 pool for the specified token pair
    /// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value
    /// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary
    function createAndInitializePoolIfNecessary(address token0, address token1, uint24 fee, uint160 sqrtPriceX96)
        external
        payable
        returns (address pool);
}

File 20 of 25 : IPeripheryImmutableState.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Immutable state
/// @notice Functions that return immutable state of the router
interface IPeripheryImmutableState {
    /// @return Returns the address of the Uniswap V3 factory
    function factory() external view returns (address);

    /// @return Returns the address of WETH9
    function WETH9() external view returns (address);
}

File 21 of 25 : IUniswapV3PoolActions.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Permissionless pool actions
/// @notice Contains pool methods that can be called by anyone
interface IUniswapV3PoolActions {
    /// @notice Sets the initial price for the pool
    /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
    /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
    function initialize(uint160 sqrtPriceX96) external;

    /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
    /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback
    /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
    /// on tickLower, tickUpper, the amount of liquidity, and the current price.
    /// @param recipient The address for which the liquidity will be created
    /// @param tickLower The lower tick of the position in which to add liquidity
    /// @param tickUpper The upper tick of the position in which to add liquidity
    /// @param amount The amount of liquidity to mint
    /// @param data Any data that should be passed through to the callback
    /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
    /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
    function mint(address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes calldata data)
        external
        returns (uint256 amount0, uint256 amount1);

    /// @notice Collects tokens owed to a position
    /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
    /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
    /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
    /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
    /// @param recipient The address which should receive the fees collected
    /// @param tickLower The lower tick of the position for which to collect fees
    /// @param tickUpper The upper tick of the position for which to collect fees
    /// @param amount0Requested How much token0 should be withdrawn from the fees owed
    /// @param amount1Requested How much token1 should be withdrawn from the fees owed
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);

    /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
    /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
    /// @dev Fees must be collected separately via a call to #collect
    /// @param tickLower The lower tick of the position for which to burn liquidity
    /// @param tickUpper The upper tick of the position for which to burn liquidity
    /// @param amount How much liquidity to burn
    /// @return amount0 The amount of token0 sent to the recipient
    /// @return amount1 The amount of token1 sent to the recipient
    function burn(int24 tickLower, int24 tickUpper, uint128 amount)
        external
        returns (uint256 amount0, uint256 amount1);

    /// @notice Swap token0 for token1, or token1 for token0
    /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
    /// @param recipient The address to receive the output of the swap
    /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
    /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
    /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
    /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
    /// @param data Any data to be passed through to the callback
    /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
    /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96,
        bytes calldata data
    ) external returns (int256 amount0, int256 amount1);

    /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
    /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback
    /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling
    /// with 0 amount{0,1} and sending the donation amount(s) from the callback
    /// @param recipient The address which will receive the token0 and token1 amounts
    /// @param amount0 The amount of token0 to send
    /// @param amount1 The amount of token1 to send
    /// @param data Any data to be passed through to the callback
    function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external;

    /// @notice Increase the maximum number of price and liquidity observations that this pool will store
    /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to
    /// the input observationCardinalityNext.
    /// @param observationCardinalityNext The desired minimum number of observations for the pool to store
    function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
}

File 22 of 25 : IUniswapV3PoolImmutables.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that never changes
/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
interface IUniswapV3PoolImmutables {
    /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
    /// @return The contract address
    function factory() external view returns (address);

    /// @notice The first of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token0() external view returns (address);

    /// @notice The second of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token1() external view returns (address);

    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
    /// @return The fee
    function fee() external view returns (uint24);

    /// @notice The pool tick spacing
    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
    /// This value is an int24 to avoid casting even though it is always positive.
    /// @return The tick spacing
    function tickSpacing() external view returns (int24);

    /// @notice The maximum amount of position liquidity that can use any tick in the range
    /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
    /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
    /// @return The max amount of liquidity per tick
    function maxLiquidityPerTick() external view returns (uint128);
}

File 23 of 25 : IUniswapV3PoolState.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that can change
/// @notice These methods compose the pool's state, and can change with any frequency including multiple times
/// per transaction
interface IUniswapV3PoolState {
    /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
    /// when accessed externally.
    /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
    /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
    /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
    /// boundary.
    /// observationIndex The index of the last oracle observation that was written,
    /// observationCardinality The current maximum number of observations stored in the pool,
    /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
    /// feeProtocol The protocol fee for both tokens of the pool.
    /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
    /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
    /// unlocked Whether the pool is currently locked to reentrancy
    function slot0()
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext,
            uint8 feeProtocol,
            bool unlocked
        );

    /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal0X128() external view returns (uint256);

    /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal1X128() external view returns (uint256);

    /// @notice The amounts of token0 and token1 that are owed to the protocol
    /// @dev Protocol fees will never exceed uint128 max in either token
    function protocolFees() external view returns (uint128 token0, uint128 token1);

    /// @notice The currently in range liquidity available to the pool
    /// @dev This value has no relationship to the total liquidity across all ticks
    function liquidity() external view returns (uint128);

    /// @notice Look up information about a specific tick in the pool
    /// @param tick The tick to look up
    /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
    /// tick upper,
    /// liquidityNet how much liquidity changes when the pool price crosses the tick,
    /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
    /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
    /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
    /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
    /// secondsOutside the seconds spent on the other side of the tick from the current tick,
    /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
    /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
    /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
    /// a specific position.
    function ticks(int24 tick)
        external
        view
        returns (
            uint128 liquidityGross,
            int128 liquidityNet,
            uint256 feeGrowthOutside0X128,
            uint256 feeGrowthOutside1X128,
            int56 tickCumulativeOutside,
            uint160 secondsPerLiquidityOutsideX128,
            uint32 secondsOutside,
            bool initialized
        );

    /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
    function tickBitmap(int16 wordPosition) external view returns (uint256);

    /// @notice Returns the information about a position by the position's key
    /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
    /// @return _liquidity The amount of liquidity in the position,
    /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
    /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
    /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
    /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
    function positions(bytes32 key)
        external
        view
        returns (
            uint128 _liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    /// @notice Returns data about a specific observation index
    /// @param index The element of the observations array to fetch
    /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
    /// ago, rather than at a specific index in the array.
    /// @return blockTimestamp The timestamp of the observation,
    /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
    /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
    /// Returns initialized whether the observation has been initialized and the values are safe to use
    function observations(uint256 index)
        external
        view
        returns (
            uint32 blockTimestamp,
            int56 tickCumulative,
            uint160 secondsPerLiquidityCumulativeX128,
            bool initialized
        );
}

File 24 of 25 : g8keepLiquidityLocker.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {Ownable} from "solady/auth/Ownable.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {INonfungiblePositionManager} from "./interfaces/INonfungiblePositionManager.sol";
import {g8keepLockerFactory} from "./g8keepLockerFactory.sol";

/**
 * @title   g8keepLiquidityLocker
 * @notice  Locks a liquidity position for a Uniswap V3 pool while allowing
 * @notice  fees to be collected and split between g8keep and the deployer.
 */
contract g8keepLiquidityLocker is Ownable {
    /// @dev Emitted when a liquidity token is locked.
    event LiquidityLocked(uint256 tokenId, uint256 duration);
    /// @dev Emitted when the liquidity token is released.
    event LiquidityReleased(uint256 tokenId);
    /// @dev Emitted when liquidity fees are collected.
    event ClaimedFees(
        address indexed claimer,
        address indexed token0,
        address indexed token1,
        uint256 amount0,
        uint256 amount1
    );

    /// @dev Constant value for the max BPS value.
    uint16 private constant BPS = 10_000;
    /// @dev Timestamp that vesting of the liquidity position ends.
    uint256 public immutable VESTING_END;
    /// @dev Address of the Uniswap V3 position manager.
    INonfungiblePositionManager immutable UNISWAP_POSITION_MANAGER;
    /// @dev g8keep fee percentage of liquidity fees in BPS.
    uint16 public immutable G8KEEP_FEE;
    /// @dev Token ID for the LP token that is locked.
    uint256 public immutable LP_TOKEN_ID;
    /// @dev Address of the locker factory that deployed the liquidity locker.
    g8keepLockerFactory public immutable LOCKER_FACTORY;
    /// @dev Address of the g8keep fee wallet that receives g8keep fees from liquidity fees.
    address public g8keepFeeWallet;

    /// @dev Thrown when attempting to withdraw the locked LP token before vesting has ended.
    error NotVested();
    /// @dev Thrown when a caller is not allowed for the function they are calling.
    error InvalidCaller();

    /**
     * @dev Creates the liquidity locker for the given LP token.
     */
    constructor(
        INonfungiblePositionManager _uniswapPositionManager,
        uint256 lpTokenId,
        address beneficiary,
        uint256 durationSeconds,
        uint16 fee,
        address feeWallet
    ) {
        _initializeOwner(beneficiary);

        UNISWAP_POSITION_MANAGER = _uniswapPositionManager;
        unchecked {
            if (block.timestamp + durationSeconds < block.timestamp) {
                VESTING_END = type(uint256).max;
            } else {
                VESTING_END = block.timestamp + durationSeconds;
            }
        }
        G8KEEP_FEE = fee;
        g8keepFeeWallet = feeWallet;
        LOCKER_FACTORY = g8keepLockerFactory(msg.sender);
        LP_TOKEN_ID = lpTokenId;

        emit LiquidityLocked(lpTokenId, durationSeconds);
    }

    /**
     * @notice  Collects fees and transfers the LP token to the owner.
     * 
     * @dev     Cannot be called before the vesting period expires.
     * @dev     Emits a `LiquidityReleased` event.
     */
    function release() external onlyOwner {
        if (block.timestamp < VESTING_END) {
            revert NotVested();
        }
        collectFees();

        UNISWAP_POSITION_MANAGER.transferFrom(address(this), msg.sender, LP_TOKEN_ID);
        emit LiquidityReleased(LP_TOKEN_ID);
    }

    /**
     * @notice  Withdraws an ERC721 token from the contract to the owner.
     * 
     * @dev     If the token address is the Uniswap position manager, the tokenId cannot be 
     * @dev     the locked LP token.
     *
     * @param _token    Address of the token to withdraw.
     * @param _tokenId  ID of the token to withdraw.
     */
    function withdrawNFT(address _token, uint256 _tokenId) external onlyOwner {
        if (_token == address(UNISWAP_POSITION_MANAGER) && _tokenId == LP_TOKEN_ID) {
            revert NotVested();
        }
        IERC721(_token).transferFrom(address(this), msg.sender, _tokenId);
    }

    /**
     * @notice  Withdraws an ERC20 token from the contract to the owner.
     *
     * @dev     The token address cannot be the Uniswap position manager.
     *
     * @param _token    Address of the token to withdraw.
     */
    function withdrawERC20(address _token) external onlyOwner {
        if (_token == address(UNISWAP_POSITION_MANAGER)) revert NotVested();
        SafeTransferLib.safeTransferAll(_token, msg.sender);
    }

    /**
     * @notice  Withdraws ETH that is in the contract to the owner.
     */
    function withdrawETH() external onlyOwner {
        SafeTransferLib.safeTransferAllETH(msg.sender);
    }

    /**
     * @notice  Updates the g8keep fee wallet address.
     * 
     * @dev     Caller must be the current fee wallet address.
     * 
     * @param _newFeeWallet  Address to set as the new fee wallet recipient.
     */
    function updateFeeWallet(address _newFeeWallet) external {
        if (msg.sender != g8keepFeeWallet) {
            revert InvalidCaller();
        }

        g8keepFeeWallet = _newFeeWallet;
    }

    /**
     * @notice  Collects fees from the locked LP token. Fees are split between the locker owner and g8keep.
     * 
     * @dev     Caller must be the locker owner, locker factory fee collector or locker factory owner.
     * @dev     Emits a `ClaimedFees` event for each recipient of fees.
     */
    function collectFees() public {
        address _owner = owner();
        address _g8keepFeeRecipient = g8keepFeeWallet;
        if (msg.sender != _owner) {
            if (msg.sender != LOCKER_FACTORY.feeCollector()) {
                if (msg.sender != LOCKER_FACTORY.owner()) {
                    revert InvalidCaller();
                }
            }
        }

        (,, address token0, address token1,,,,,,,,) = UNISWAP_POSITION_MANAGER.positions(LP_TOKEN_ID);

        if (G8KEEP_FEE == 0) {
            (uint256 amount0, uint256 amount1) = UNISWAP_POSITION_MANAGER.collect(
                INonfungiblePositionManager.CollectParams({
                    recipient: _owner,
                    amount0Max: type(uint128).max,
                    amount1Max: type(uint128).max,
                    tokenId: LP_TOKEN_ID
                })
            );

            emit ClaimedFees(_owner, token0, token1, amount0, amount1);
        } else {
            (uint256 amount0, uint256 amount1) = UNISWAP_POSITION_MANAGER.collect(
                INonfungiblePositionManager.CollectParams({
                    recipient: address(this),
                    amount0Max: type(uint128).max,
                    amount1Max: type(uint128).max,
                    tokenId: LP_TOKEN_ID
                })
            );


            uint256 protocolFee0 = (amount0 * G8KEEP_FEE) / BPS;
            uint256 protocolFee1 = (amount1 * G8KEEP_FEE) / BPS;

            uint256 recipientFee0 = amount0 - protocolFee0;
            uint256 recipientFee1 = amount1 - protocolFee1;

            SafeTransferLib.safeTransfer(token0, _owner, recipientFee0);
            SafeTransferLib.safeTransfer(token1, _owner, recipientFee1);

            SafeTransferLib.safeTransfer(token0, _g8keepFeeRecipient, protocolFee0);
            SafeTransferLib.safeTransfer(token1, _g8keepFeeRecipient, protocolFee1);

            emit ClaimedFees(_owner, token0, token1, recipientFee0, recipientFee1);
            emit ClaimedFees(_g8keepFeeRecipient, token0, token1, protocolFee0, protocolFee1);
        }
    }

    /**
     * @dev Returns the amount of time left on the liquidity vesting.
     */
    function vestingSchedule() external view returns (uint256) {
        unchecked {
            if (block.timestamp > VESTING_END) {
                return 0;
            } else {
                return VESTING_END - block.timestamp;
            }
        }
    }

    /**
     * @dev Allow contract to receive ETH.
     */
    receive() external payable {}
}

File 25 of 25 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Settings
{
  "viaIR": false,
  "codegen": "yul",
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solady/=lib/solady/src/",
    "solmate/=lib/solmate/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/"
  ],
  "evmVersion": "cancun",
  "outputSelection": {
    "*": {
      "*": [
        "abi",
        "metadata"
      ],
      "": [
        "ast"
      ]
    }
  },
  "optimizer": {
    "enabled": true,
    "mode": "3",
    "fallback_to_optimizing_for_size": false,
    "disable_system_request_memoization": true
  },
  "metadata": {},
  "libraries": {},
  "enableEraVMExtensions": false,
  "forceEVMLA": false
}

Contract ABI

[{"inputs":[{"components":[{"internalType":"string","name":"TOKEN_NAME","type":"string"},{"internalType":"string","name":"TOKEN_SYMBOL","type":"string"},{"internalType":"uint256","name":"FID","type":"uint256"},{"internalType":"string","name":"IMAGE","type":"string"},{"internalType":"string","name":"CAST_HASH","type":"string"},{"internalType":"uint112","name":"TOTAL_SUPPLY","type":"uint112"},{"internalType":"address","name":"G8KEEP_FEE_WALLET","type":"address"},{"internalType":"uint16","name":"G8KEEP_FEE","type":"uint16"},{"internalType":"uint16","name":"LIQUIDITY_SUPPLEMENT_FEE","type":"uint16"},{"internalType":"address","name":"DEPLOYER","type":"address"},{"internalType":"uint112","name":"DEPLOYER_REWARD","type":"uint112"},{"internalType":"contract INonfungiblePositionManager","name":"UNISWAP_POSITION_MANAGER","type":"address"},{"internalType":"contract IG8keepLockerFactory","name":"LOCKER_FACTORY","type":"address"},{"internalType":"uint24","name":"UNISWAP_FEE_TIER","type":"uint24"},{"internalType":"address","name":"WETH","type":"address"},{"internalType":"uint112","name":"LIQUIDITY_SHIFT","type":"uint112"},{"internalType":"uint112","name":"MIGRATION_MINIMUM_LIQUIDITY","type":"uint112"},{"internalType":"uint40","name":"SNIPE_PROTECTION_SECONDS","type":"uint40"},{"internalType":"uint40","name":"SNIPE_PROTECTION_HEAVY_PENALTY_SECONDS","type":"uint40"},{"internalType":"uint8","name":"SNIPE_PROTECTION_HEAVY_EXPONENT_START","type":"uint8"},{"internalType":"uint160","name":"START_SQRT_PRICE","type":"uint160"}],"internalType":"struct Parameters","name":"parameters","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CurveMigrated","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientOutput","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[],"name":"InvalidReserves","type":"error"},{"inputs":[],"name":"NotMigrated","type":"error"},{"inputs":[],"name":"PoolAlreadyCreated","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethAmountA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethAmountB","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"classA","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"classB","type":"uint256"}],"name":"Buy","type":"event"},{"anonymous":false,"inputs":[],"name":"MigrationFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"}],"name":"Sell","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"vault","type":"address"}],"name":"SingleUseETHVaultCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pair","type":"address"},{"indexed":true,"internalType":"address","name":"lpLocker","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"TokenMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"G8KEEP_FEE_WALLET","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"_allowance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"_balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"minimumOut","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minimumOut","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint112","name":"ethAmount","type":"uint112"}],"name":"calculateBuy","outputs":[{"internalType":"uint112","name":"tokenAmount","type":"uint112"},{"internalType":"uint112","name":"ethA","type":"uint112"},{"internalType":"uint112","name":"ethB","type":"uint112"},{"internalType":"uint112","name":"classA","type":"uint112"},{"internalType":"uint112","name":"classB","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint112","name":"tokenAmount","type":"uint112"}],"name":"calculateSell","outputs":[{"internalType":"uint112","name":"ethAmount","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"castHash","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"classBalanceOf","outputs":[{"internalType":"uint256","name":"_balanceA","type":"uint256"},{"internalType":"uint256","name":"_balanceB","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"_contractURI","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"seller","type":"address"},{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"minimumOut","type":"uint112"}],"name":"factorySell","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurveSettings","outputs":[{"components":[{"internalType":"address","name":"deployer","type":"address"},{"internalType":"uint112","name":"deployerReward","type":"uint112"},{"internalType":"address","name":"g8keepFeeWallet","type":"address"},{"internalType":"uint16","name":"g8keepFee","type":"uint16"},{"internalType":"uint112","name":"liquidityShift","type":"uint112"},{"internalType":"uint16","name":"liquiditySupplementFee","type":"uint16"},{"internalType":"uint112","name":"migrationMinimumLiquidity","type":"uint112"},{"internalType":"address","name":"migrationPositionManager","type":"address"},{"internalType":"uint24","name":"migrationFeeTier","type":"uint24"},{"internalType":"uint40","name":"genesisTime","type":"uint40"},{"internalType":"uint40","name":"snipeProtectionEnd","type":"uint40"},{"internalType":"uint40","name":"snipeProtectionSeconds","type":"uint40"},{"internalType":"uint40","name":"snipeProtectionHeavySeconds","type":"uint40"},{"internalType":"uint8","name":"snipeProtectionHeavyExponentStart","type":"uint8"},{"internalType":"address","name":"pairAddress","type":"address"},{"internalType":"address","name":"lpLocker","type":"address"}],"internalType":"struct CurveSettings","name":"curveSettings","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurveStatus","outputs":[{"components":[{"internalType":"uint112","name":"reserve0","type":"uint112"},{"internalType":"uint112","name":"reserve1","type":"uint112"}],"internalType":"struct g8keepBondingCurve.Reserves","name":"_aReserve","type":"tuple"},{"components":[{"internalType":"uint112","name":"reserve0","type":"uint112"},{"internalType":"uint112","name":"reserve1","type":"uint112"}],"internalType":"struct g8keepBondingCurve.Reserves","name":"_bReserve","type":"tuple"},{"internalType":"uint256","name":"_liquiditySupplement","type":"uint256"},{"internalType":"uint256","name":"_minimumLiquiditySupplement","type":"uint256"},{"internalType":"bool","name":"_curveLiquidityMet","type":"bool"},{"internalType":"bool","name":"_curveVolumeMet","type":"bool"},{"internalType":"bool","name":"_curveMigrated","type":"bool"},{"internalType":"bool","name":"_migrationFailed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"image","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxBuyWithoutPenalty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"minimumOut","type":"uint112"}],"name":"sell","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"_totalSupply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawExcess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

028221cc000000000000000000000000b20c44d202743cbcc393d67af242ceb3fd4177ad00000000000000000000000007fd9c1b103b10fd4ef45e665d330be3e123f580000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084747474747474747000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000838383838383838380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x0003000000000002003a000000000002000000600310027000000af803300197000200000031035500010000000103550000000100200190000000b90000c13d0000008002000039000000400020043f000000040030008c000000ee0000413d000000000201043b000000e00220027000000b5f0020009c000001060000213d00000b710020009c0000013e0000a13d00000b720020009c000001b80000a13d00000b730020009c0000020b0000213d00000b760020009c000002710000613d00000b770020009c0000090b0000c13d000000240030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000000401100370000000000101043b001300000001001d00000afe0010009c0000090b0000213d0000000501000039000000000201041a00000b8a0120019700000b8b0010009c000004c00000613d001200000002001d00000b5d01000041000000000010044300000000010004120000000400100443000001c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d0000000002000412000000120300002900000b9500300198000000000101043b0000ffff0110018f00000013011000b900000b9701100197000027100110011a0000001304100069000000730000c13d001300000004001d0000000601000039000000000101041a001100000001001d00000b5d010000410000000000100443000000040020044300000160010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b001000000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000140010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d0000001003000029000000110230006a00000afe02200197000000000101043b0000ffff0110018f00000013011000b900000b9701100197000027100110011a000000000012004b000000000201801900000013042000690000000002000412000000120300002900000b960030019800000afe01400197001300000001001d0000075a0000c13d0000000701000039000000000101041a001200000001001d00000b5d010000410000000000100443000000040020044300000120010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d0000001202000029000000000502001900110afe0020019b000000000101043b00000afe01100197000000110110006a00000afe0010009c000007ed0000213d000000130210006b0000000004000019001000130000002d000000980000a13d00000afe0020009c0000000004020019001000000001001d000007ed0000213d000f00000004001d0000001002000029000000110120002a000007f70000613d000000700250027000000afe03200197001200000003001d00000010023000b900000000021200d900000afe0020009c000008410000213d0000000001020019000e00000002001d2bdc2a0b0000040f00000000040100190000000e0010006b000008c90000a13d000000120140006b000007ed0000413d000007f70000613d00000011024000b900000000011200d9001200000001001d00000afe0010009c0000000f01000029000008410000213d000000100110002900000afe0010009c000007ed0000213d000000120310006a00000afe0030009c000008cb0000a13d000007ed0000013d0000036004000039000000400040043f0000000002000416000000000002004b0000090b0000c13d0000001f0230003900000af9022001970000036002200039000000400020043f0000001f0530018f00000afa063001980000036002600039000000cb0000613d000000000701034f000000007807043c0000000004840436000000000024004b000000c70000c13d000000000005004b000000d80000613d000000000161034f0000000304500210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f0000000000120435000000200030008c0000090b0000413d000003600100043d00000afb0010009c0000090b0000213d00000360033000390000036004100039000000000243004900000afc0020009c0000090b0000213d000002a00020008c0000090b0000413d000000400200043d003a00000002001d00000afd0020009c000002440000a13d00000b9201000041000000000010043f0000004101000039000000040010043f00000b0e0100004100002bde00010430000000000003004b0000090b0000c13d00000b5d01000041000000000010044300000000010004120000000400100443000000c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b00000aff011001970000000002000411000000000012004b000004a90000c13d000000000100001900002bdd0001042e00000b600020009c000001570000a13d00000b610020009c000001cc0000a13d00000b620020009c0000022f0000213d00000b650020009c000003040000613d00000b660020009c0000090b0000c13d000000640030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000000402100370000000000202043b001300000002001d00000aff0020009c0000090b0000213d0000002402100370000000000202043b001200000002001d00000afe0020009c0000090b0000213d0000004401100370000000000101043b001100000001001d00000afe0010009c0000090b0000213d00000b5d0100004100000000001004430000000001000412000000040010044300000060010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b00000aff011001970000000002000411000000000012004b000004a90000c13d0000001301000029000000120200002900000011030000292bdc1e9e0000040f000000000100001900002bdd0001042e00000b7b0020009c000001f00000213d00000b7f0020009c000003af0000613d00000b800020009c000004d90000613d00000b810020009c0000090b0000c13d0000000001000416000000000001004b0000090b0000c13d0000000001000412003900000001001d003800000000003d000080050100003900000044030000390000000004000415000000390440008a000000050440021000000b5d020000412bdc2bb40000040f00000afe01100197000000800010043f00000b880100004100002bdd0001042e00000b6a0020009c000001fd0000213d00000b6e0020009c000003c60000613d00000b6f0020009c000004e70000613d00000b700020009c0000090b0000c13d0000000001000416000000000001004b0000090b0000c13d2bdc10890000040f2bdc10890000040f000000400100043d001200000001001d2bdc10240000040f0000000701000039000000000101041a000000700210027000000afe0220019700000012040000290000002003400039000000000023043500000afe011001970000000000140435000000400100043d001100000001001d2bdc10240000040f0000000801000039000000000101041a000000700210027000000afe0220019700000011040000290000002003400039000000000023043500000afe0110019700000000001404350000000601000039000000000101041a000c00000001001d2bdc11ce0000040f001000000001001d000f00000002001d000e00000003001d000d00000004001d000000400200043d001300000002001d00000012010000292bdc10760000040f0000001301000029000000400210003900000011010000292bdc10760000040f0000000c0100002900000afe011001970000001302000029000000800220003900000000001204350000000001000412001500000001001d001401600000003d000080050100003900000044030000390000000004000415000000150440008a000000050440021000000b5d020000412bdc2bb40000040f0000000d0000006b0000000002000039000000010200c0390000001304000029000001200340003900000000002304350000000e0000006b0000000002000039000000010200c039000001000340003900000000002304350000000f0000006b0000000002000039000000010200c039000000e0034000390000000000230435000000100000006b0000000002000039000000010200c039000000c003400039000000000023043500000afe01100197000000a002400039000000000012043500000af80040009c00000af804008041000000400140021000000b93011001c700002bdd0001042e00000b780020009c0000038c0000613d00000b790020009c000004ad0000613d00000b7a0020009c0000090b0000c13d000000240030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000000401100370000000000101043b00000aff0010009c0000090b0000213d2bdc12300000040f00000afe0220019700000afe011001970000000001210019000004f50000013d00000b670020009c000003940000613d00000b680020009c000004c40000613d00000b690020009c0000090b0000c13d0000000001000416000000000001004b0000090b0000c13d00000b8401000041000000800010043f0000000001000410000000840010043f00000b5d0100004100000000001004430000000001000412000000040010044300000060010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000201043b000000000100041400000aff02200197000000040020008c000006380000c13d00000002010003670000000003000031000006430000013d00000b7c0020009c000003db0000613d00000b7d0020009c000004fc0000613d00000b7e0020009c0000090b0000c13d0000000001000416000000000001004b0000090b0000c13d0000001201000039000000800010043f00000b880100004100002bdd0001042e00000b6b0020009c000004110000613d00000b6c0020009c000005f40000613d00000b6d0020009c0000090b0000c13d000000240030008c0000090b0000413d0000000401100370000000000201043b00000000010004112bdc12450000040f000000000100001900002bdd0001042e00000b740020009c0000031c0000613d00000b750020009c0000090b0000c13d0000000001000416000000000001004b0000090b0000c13d0000000103000039000000000203041a000000010520019000000001012002700000007f0410018f00000000010460190000001f0010008c00000000060000390000000106002039000000000662013f0000000100600190000003d50000c13d000000800010043f000000000005004b000006160000613d000000000030043f000000020020008c000006ad0000413d00000b060200004100000000040000190000000003040019000000000402041a000000a005300039000000000045043500000001022000390000002004300039000000000014004b000002260000413d000006d70000013d00000b630020009c0000032d0000613d00000b640020009c0000090b0000c13d000000440030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000000402100370000000000202043b00000afe0020009c0000090b0000213d0000002401100370000000000301043b00000afe0030009c0000090b0000213d00000000010004112bdc1e9e0000040f000000000100001900002bdd0001042e000002a005200039000000400050043f000000000604043300000afb0060009c0000090b0000213d00000000064600190000001f07600039000000000037004b0000090b0000813d000000007606043400000afb0060009c000000e80000213d0000001f0860003900000ba0088001970000003f0880003900000ba008800197000000000858001900000afb0080009c000000e80000213d000000400080043f00000000006504350000000008760019000000000038004b0000090b0000213d00000ba00a6001970000001f0960018f000002c008200039000000000087004b0000070f0000813d00000000000a004b0000026d0000613d000000000c970019000000000b980019000000200bb0008a000000200cc0008a000000000dab0019000000000eac0019000000000e0e04330000000000ed0435000000200aa0008c000002670000c13d000000000009004b000007250000613d000000000b0800190000071b0000013d0000000001000416000000000001004b0000090b0000c13d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000000101043b000000000101041a001300000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000220010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b001200000001001d00000b50010000410000000000100443000000000100041400000af80010009c00000af801008041000000c00110021000000b51011001c70000800b020000392bdc2bd70000040f000000010020019000000f180000613d000000130200002900000afe02200197000000000301043b000000120100002900000b0101100197000000000013004b000002fd0000813d001300000003001d001200000002001d00000b5d01000041000000000010044300000000010004120000000400100443000001e0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b00000b0101100197000000130110006b000007ed0000413d001300000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000200010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b00000afe01100198000000130300002900110000003100ad000002d40000613d00000011011000f9000000000031004b000007ed0000c13d00000b5d0100004100000000001004430000000001000412000000040010044300000240010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b00130b010010019c000007f70000613d00000b5d010000410000000000100443000000000100041200000004001004430000002400000443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000130300002900000011023000f9000000000101043b00000afe01100197000000000121004b000007ed0000413d000000120110006b00000000020000190000000002012019000000400100043d000000000021043500000af80010009c00000af801008041000000400110021000000b94011001c700002bdd0001042e0000000001000416000000000001004b0000090b0000c13d0000000301000039000000000601041a000000010360019000000001056002700000007f0250018f00000000050260190000001f0050008c00000000040000390000000104002039000000000446013f0000000100400190000003d50000c13d000000800050043f000000000003004b0000061c0000c13d00000ba101600197000000a00010043f000000000002004b000000c001000039000000a001006039000006d80000013d0000000001000416000000000001004b0000090b0000c13d0000000001000412001700000001001d001600e00000003d000080050100003900000044030000390000000004000415000000170440008a000000050440021000000b5d020000412bdc2bb40000040f00000aff01100197000000800010043f00000b880100004100002bdd0001042e000000640030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000002402100370000000000202043b001300000002001d0000000402100370000000000202043b001200000002001d0000004402100370000000000202043b00000afb0020009c0000090b0000213d0000002304200039000000000034004b0000090b0000813d0000000404200039000000000141034f000000000101043b00000afb0010009c0000090b0000213d00000000011200190000002401100039000000000031004b0000090b0000213d00000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b00000aff011001970000000002000411000000000012004b000004a90000c13d000000120300002900000afc0030009c000003630000213d000000000003004b000003630000613d00000000010004102bdc10d50000040f000000130100002900000afc0010009c000001040000213d000000130000006b000001040000613d000000400200043d00000024012000390000001303000029000000000031043500000b82010000410000000000120435000000000100041100000aff01100197001200000002001d0000000402200039000000000012043500000b5d01000041000000000010044300000000010004120000000400100443000000c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000201043b000000000100041400000aff02200197000000040020008c000007fd0000c13d0000000003000031000000200030008c00000020040000390000000004034019000008270000013d0000000001000416000000000001004b0000090b0000c13d0000000201000039000000000101041a000000800010043f00000b880100004100002bdd0001042e000000440030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000000402100370000000000202043b00000aff0020009c0000090b0000213d0000002401100370000000000101043b001300000001001d00000aff0010009c0000090b0000213d000000000020043f0000000a01000039000000200010043f000000400200003900000000010000192bdc2b9f0000040f0000001302000029000000000020043f000000200010043f000000000100001900000040020000392bdc2b9f0000040f000003900000013d0000000001000416000000000001004b0000090b0000c13d000000000200041a000000010420019000000001012002700000007f0310018f00000000010360190000001f0010008c00000000050000390000000105002039000000000552013f0000000100500190000003d50000c13d000000800010043f000000000004004b0000069c0000c13d00000ba101200197000000a00010043f000000000003004b000000c001000039000000a001006039000006d80000013d0000000001000416000000000001004b0000090b0000c13d0000000403000039000000000203041a000000010520019000000001012002700000007f0410018f00000000010460190000001f0010008c00000000060000390000000106002039000000000662013f0000000100600190000006130000613d00000b9201000041000000000010043f0000002201000039000000040010043f00000b0e0100004100002bde00010430000000640030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000000402100370000000000202043b001300000002001d00000aff0020009c0000090b0000213d0000002402100370000000000202043b001200000002001d00000aff0020009c0000090b0000213d0000004401100370000000000101043b001100000001001d0000001301000029000000000010043f0000000a01000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000000101043b000000000200041100000aff02200197001000000002001d000000000020043f000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000000101043b000000000101041a00000ba20010009c000007810000c13d000000130100002900000012020000290000001103000029000004f30000013d0000000001000416000000000001004b0000090b0000c13d00000b5d0100004100000000001004430000000001000412000000040010044300000180010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b001300000001001d00000aff011001970000000002000411000000000012004b000004a90000c13d0000000501000039000000000101041a00000b8a0110019700000b8b0010009c0000060f0000c13d000000000100041000000aff01100197000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000000101043b000000000101041a0000007002100270000000000312019f00000afe00300198000004470000613d000000000112001900000afe03100197000000000100041000000013020000292bdc10d50000040f00000b5d0100004100000000001004430000000001000412000000040010044300000060010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b00000b8d02000041000000000020044300000aff01100197001200000001001d0000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b000000000001004b0000090b0000613d000000400200043d00000b8f010000410000000000120435001100000002001d00000004012000390000000002000411000000000021043500000000010004140000001202000029000000040020008c000004830000613d000000110200002900000af80020009c00000af802008041000000400220021000000af80010009c00000af801008041000000c001100210000000000121019f00000b0e011001c700000012020000292bdc2bd20000040f000000600310027000000af80030019d000200000001035500000001002001900000086c0000613d000000110100002900000afb0010009c000000e80000213d0000001101000029000000400010043f00000b5d01000041000000000010044300000000010004120000000400100443000000c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000201043b000000400300043d00000b90010000410000000000130435001200000003001d000000040130003900000000030004100000000000310435000000000100041400000aff02200197001100000002001d000000040020008c000008d10000c13d0000000003000031000000200030008c00000020040000390000000004034019000008fc0000013d00000b8901000041000000000010043f00000b0c0100004100002bde00010430000000440030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000000402100370000000000202043b00000aff0020009c0000090b0000213d0000002401100370000000000101043b001300000001001d00000afe0010009c0000090b0000213d0000000501000039000000000301041a00000b8a0130019700000b8b0010009c000006e90000c13d00000b9b01000041000000000010043f00000b0c0100004100002bde00010430000000240030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000000401100370000000000101043b00000aff0010009c0000090b0000213d2bdc12300000040f00000afe02200197000000400300043d0000002004300039000000000024043500000afe01100197000000000013043500000af80030009c00000af803008041000000400130021000000b87011001c700002bdd0001042e000000440030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000000402100370000000000202043b00000aff0020009c0000090b0000213d0000002401100370000000000301043b00000000010004112bdc10980000040f000004f40000013d000000440030008c0000090b0000413d0000000002000416000000000002004b0000090b0000c13d0000000402100370000000000202043b00000aff0020009c0000090b0000213d0000002401100370000000000301043b00000000010004112bdc10d50000040f0000000101000039000000400200043d000000000012043500000af80020009c00000af802008041000000400120021000000b94011001c700002bdd0001042e0000000001000416000000000001004b0000090b0000c13d0000028001000039000000400010043f0000000001000412003700000001001d003601800000003d000080050100003900000044030000390000000004000415000000370440008a000000050440021000000b5d020000412bdc2bb40000040f00000aff01100197000000800010043f0000000001000412003500000001001d003401a00000003d0000000004000415000000350440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000afe01100197001300000001001d000000a00010043f0000000001000412003300000001001d003200e00000003d0000000004000415000000330440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000aff01100197001200000001001d000000c00010043f0000000001000412003100000001001d003001c00000003d0000000004000415000000310440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f0000ffff0110018f001100000001001d000000e00010043f0000000001000412002f00000001001d002e01000000003d00000000040004150000002f0440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000afe01100197001000000001001d000001000010043f0000000001000412002d00000001001d002c01200000003d00000000040004150000002d0440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000afe01100197000f00000001001d000001400010043f0000000001000412002b00000001001d002a01400000003d00000000040004150000002b0440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f0000ffff0110018f000e00000001001d000001200010043f0000000001000412002900000001001d002800400000003d0000000004000415000000290440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000aff01100197000d00000001001d000001600010043f0000000001000412002700000001001d002600a00000003d0000000004000415000000270440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000b0001100197000c00000001001d000001800010043f0000000001000412002500000001001d002401e00000003d0000000004000415000000250440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000b0101100197000b00000001001d000001a00010043f0000000001000412002300000001001d002202200000003d0000000004000415000000230440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000b0101100197000a00000001001d000001c00010043f0000000001000412002100000001001d002002400000003d0000000004000415000000210440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000b0101100197000900000001001d000001e00010043f0000000001000412001f00000001001d001e02600000003d00000000040004150000001f0440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000b0101100197000800000001001d000002000010043f0000000001000412001d00000001001d001c02800000003d00000000040004150000001d0440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f000000ff0110018f000700000001001d000002200010043f0000000001000412001b00000001001d001a00200000003d00000000040004150000001b0440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000aff01100197000600000001001d000002400010043f0000000501000039000000000101041a00000aff01100197000500000001001d000002600010043f0000000001000412001900000001001d001801800000003d0000000004000415000000190440008a0000000504400210000080050100003900000b5d0200004100000044030000392bdc2bb40000040f00000aff01100197000002800010043f0000001301000029000002a00010043f0000001201000029000002c00010043f0000001101000029000002e00010043f0000001001000029000003000010043f0000000e01000029000003200010043f0000000f01000029000003400010043f0000000d01000029000003600010043f0000000c01000029000003800010043f0000000b01000029000003a00010043f0000000a01000029000003c00010043f0000000901000029000003e00010043f0000000801000029000004000010043f0000000701000029000004200010043f0000000601000029000004400010043f0000000501000029000004600010043f00000b9c0100004100002bdd0001042e000000440030008c0000090b0000413d0000000401100370000000000101043b001300000001001d00000aff0010009c0000090b0000213d00000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000000f180000613d000000000201043b0000001301000029000000000212013f00000aff00200198000007090000c13d00000b8c01000041000000000010043f00000b0c0100004100002bde00010430000000800010043f000000000005004b000006aa0000c13d00000ba101200197000000a00010043f000000000004004b000000c001000039000000a001006039000006d80000013d001300000006001d001200000005001d000000000010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b04011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d0000001302000029000000020020008c000006ad0000413d000000000101043b000000000300001900000012050000290000000002030019000000000301041a000000a004200039000000000034043500000001011000390000002003200039000000000053004b0000062e0000413d000000c001200039000006d80000013d00000af80010009c00000af801008041000000c00110021000000b85011001c72bdc2bd70000040f000000600310027000000af80030019d00000af80330019700020000000103550000000100200190000006af0000613d00000ba0053001980000001f0630018f000000800250003900000080090000390000064d0000613d000000000701034f000000007807043c0000000009890436000000000029004b000006490000c13d000000000006004b0000065a0000613d000000000151034f0000000305600210000000000602043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001204350000001f0130003900000ba00610019700000b860060009c000000e80000213d0000008002600039000000400020043f00000afc0030009c0000090b0000213d000000200030008c0000090b0000413d000000800500043d00000afb0050009c0000090b0000213d00000080073000390000009f01500039000000000071004b000000000300001900000b2a0300804100000b2a0870019700000b2a01100197000000000981013f000000000081004b000000000100001900000b2a0100404100000b2a0090009c000000000103c019000000000001004b0000090b0000c13d0000008001500039000000000101043300000afb0010009c000000e80000213d0000001f0310003900000ba0033001970000003f0330003900000ba003300197000000000323001900000afb0030009c000000e80000213d000000400030043f0000000000120435000000a0035000390000000005310019000000000075004b0000090b0000213d00000ba0071001970000001f0510018f000000a004600039000000000043004b000008450000813d000000000007004b000006980000613d00000000085300190000000006540019000000200660008a000000200880008a0000000009760019000000000a780019000000000a0a04330000000000a90435000000200770008c000006920000c13d000000000005004b0000085b0000613d0000000006040019000008510000013d000000000000043f000000020020008c000006ad0000413d00000b030200004100000000040000190000000003040019000000000402041a000000a005300039000000000045043500000001022000390000002004300039000000000014004b000006a10000413d000006d70000013d000000000030043f000000020020008c000006cd0000813d000000a001000039000006d80000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000006ba0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000006b60000c13d000000000005004b000006c70000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f0000000000140435000000600130021000000af80020009c00000af8020080410000004002200210000000000112019f00002bde0001043000000b0a0200004100000000040000190000000003040019000000000402041a000000a005300039000000000045043500000001022000390000002004300039000000000014004b000006cf0000413d000000c001300039000000800210008a00000080010000392bdc102f0000040f000000400100043d001300000001001d00000080020000392bdc10410000040f0000001302000029000000000121004900000af80010009c00000af801008041000000600110021000000af80020009c00000af8020080410000004002200210000000000121019f00002bdd0001042e001200000003001d000000000020043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000120200002900000b9602200197000000000101043b000000000101041a00000afe03100197000000700110027000000afe05100198000007870000c13d00000000010000190000001304000029000000000004004b000007920000c13d2bdc11e00000040f000000120200002900000b95002001980000000002000039000000010200c0392bdc11fa0000040f000004f50000013d00000024020000390000000102200367000000000202043b2bdc12450000040f000000000100001900002bdd0001042e000000000ba8001900000000000a004b000007180000613d000000000c070019000000000d08001900000000ce0c0434000000000ded04360000000000bd004b000007140000c13d000000000009004b000007250000613d0000000007a700190000000309900210000000000a0b0433000000000a9a01cf000000000a9a022f00000000070704330000010009900089000000000797022f00000000079701cf0000000007a7019f00000000007b0435000000000686001900000000000604350000000005520436001300000005001d0000038005100039000000000505043300000afb0050009c0000090b0000213d00000000054500190000001f06500039000000000036004b0000090b0000813d000000007505043400000afb0050009c000000e80000213d0000001f0650003900000ba0066001970000003f0660003900000ba008600197000000400600043d0000000008860019000000000068004b0000000009000039000000010900403900000afb0080009c000000e80000213d0000000100900190000000e80000c13d000000400080043f00000000085604360000000009750019000000000039004b0000090b0000213d00000ba00a5001970000001f0950018f000000000087004b000008790000813d00000000000a004b000007560000613d000000000c970019000000000b980019000000200bb0008a000000200cc0008a000000000dab0019000000000eac0019000000000e0e04330000000000ed0435000000200aa0008c000007500000c13d000000000009004b0000088f0000613d000000000b080019000008850000013d001200000000001d00000000040000190000000003010019000000000003004b00000000020000190000076a0000613d0000000801000039000000000101041a00000afe021001970000000002320019000000700110027000000afe0110019700000000013100a900000000022100d900000afe0020009c000008410000213d0000000001040019001100000002001d001000000004001d2bdc107d0000040f000000400200043d000000200320003900000012040000290000000000430435000000400320003900000013040000290000000000430435000000600320003900000010040000290000000000430435000000800320003900000011040000290000000000430435000000000012043500000af80020009c00000af802008041000000400120021000000b98011001c700002bdd0001042e000000110110006c0000079b0000813d00000b9f01000041000000000010043f00000b0c0100004100002bde00010430000000000002004b000007d10000c13d0000000801000039000000000101041a000000130450006c000007ea0000813d000000130450006900000afe0040009c001300000005001d000007ed0000213d000007f30000013d000000000002004b00000008020000390000000702006039000000000343004b000007d70000813d00000b9a01000041000000000010043f00000b0c0100004100002bde00010430000f00000001001d000000130000006b000007e60000613d000000100000006b000007e60000613d0000001301000029000000000010043f0000000a01000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000000101043b0000001002000029000000000020043f000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000000101043b0000000f02000029000000000021041b000000400100043d000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b04011001c70000800d02000039000000030300003900000b9d04000041000000130500002900000010060000292bdc2bd20000040f00000001002001900000040d0000c13d0000090b0000013d000000000335001900000afe0030009c00000000010000190000001304000029000007ed0000213d000007000000013d00000afe0030009c000007ed0000213d000000000202041a00000afe0320019700000000034300a9000000700220027000000afe02200197000000000242001900000000022300d900000afe0020009c000008410000213d000000000112001900000afe0010009c000007ed0000213d000007020000013d00000b9e01000041000000000010043f00000b0c0100004100002bde0001043000000afe0040009c0000000004000019000007f30000a13d00000b9201000041000000000010043f0000001101000039000000040010043f00000b0e0100004100002bde00010430000000700510027000000afe05500197000000130550002a0000083c0000c13d00000b9201000041000000000010043f0000001201000039000000040010043f00000b0e0100004100002bde00010430000000120300002900000af80030009c00000af803008041000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b83011001c72bdc2bd20000040f000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f00000020074001900000001205700029000008160000613d000000000801034f0000001209000029000000008a08043c0000000009a90436000000000059004b000008120000c13d000000000006004b000008230000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000008600000613d0000001f01400039000000600210018f0000001201200029000000000021004b0000000002000039000000010200403900000afb0010009c000000e80000213d0000000100200190000000e80000c13d000000400010043f000000200030008c0000090b0000413d00000012010000290000000001010433000000000001004b0000000002000039000000010200c039000000000021004b000001040000613d0000090b0000013d00000afe0110019700000013011000b900000000015100d900000afe0010009c000007000000a13d00000b9901000041000000000010043f00000b0c0100004100002bde000104300000000006740019000000000007004b0000084e0000613d00000000080300190000000009040019000000008a0804340000000009a90436000000000069004b0000084a0000c13d000000000005004b0000085b0000613d00000000037300190000000305500210000000000706043300000000075701cf000000000757022f00000000030304330000010005500089000000000353022f00000000035301cf000000000373019f000000000036043500000000014100190000000000010435000000400100043d001300000001001d000006de0000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000006ba0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000008670000c13d000006ba0000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d0000000004620019000006ba0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000008740000c13d000006ba0000013d000000000ba8001900000000000a004b000008820000613d000000000c070019000000000d08001900000000ce0c0434000000000ded04360000000000bd004b0000087e0000c13d000000000009004b0000088f0000613d0000000007a700190000000309900210000000000a0b0433000000000a9a01cf000000000a9a022f00000000070704330000010009900089000000000797022f00000000079701cf0000000007a7019f00000000007b043500000000055800190000000000050435000000130500002900000000006504350000004006200039000003a0051000390000000005050433001200000006001d0000000000560435000003c005100039000000000505043300000afb0050009c0000090b0000213d00000000054500190000001f06500039000000000036004b0000090b0000813d000000007505043400000afb0050009c000000e80000213d0000001f0650003900000ba0066001970000003f0660003900000ba008600197000000400600043d0000000008860019000000000068004b0000000009000039000000010900403900000afb0080009c000000e80000213d0000000100900190000000e80000c13d000000400080043f00000000085604360000000009750019000000000039004b0000090b0000213d00000ba00a5001970000001f0950018f000000000087004b000009780000813d00000000000a004b000008c50000613d000000000c970019000000000b980019000000200bb0008a000000200cc0008a000000000dab0019000000000eac0019000000000e0e04330000000000ed0435000000200aa0008c000008bf0000c13d000000000009004b0000098e0000613d000000000b080019000009840000013d001200100000002d0000000f0300002900000012020000290000001301200069001300000001001d00000afe0010009c000007ed0000213d0000075d0000013d000000120200002900000af80020009c00000af802008041000000400220021000000af80010009c00000af801008041000000c001100210000000000121019f00000b0e011001c700000011020000292bdc2bd70000040f000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f00000020074001900000001205700029000008eb0000613d000000000801034f0000001209000029000000008a08043c0000000009a90436000000000059004b000008e70000c13d000000000006004b000008f80000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f000200000001035500000001002001900000090d0000613d0000001f01400039000000600110018f0000001204100029000000000014004b00000000020000390000000102004039001000000004001d00000afb0040009c000000e80000213d0000000100200190000000e80000c13d0000001002000029000000400020043f000000200030008c000009190000813d000000000100001900002bde000104300000001f0530018f00000afa06300198000000400200043d0000000004620019000006ba0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000009140000c13d000006ba0000013d00000012020000290000000002020433000000000002004b000009310000c13d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000000f180000613d000000000201043b000000000002004b000001040000613d00000013010000292bdc2aeb0000040f000000000100001900002bdd0001042e00000010050000290000002404500039000000000024043500000b8202000041000000000025043500000004025000390000000004000411000000000042043500000000020004140000001104000029000000040040008c0000096a0000613d000000100100002900000af80010009c00000af801008041000000400110021000000af80020009c00000af802008041000000c002200210000000000112019f00000b83011001c700000011020000292bdc2bd20000040f000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f00000020074001900000001005700029000009570000613d000000000801034f0000001009000029000000008a08043c0000000009a90436000000000059004b000009530000c13d000000000006004b000009640000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000009c40000613d0000001f01400039000000600110018f000000100110002900000afb0010009c000000e80000213d000000400010043f000000200030008c0000090b0000413d00000010010000290000000001010433000000000001004b0000000002000039000000010200c039000000000021004b0000090b0000c13d0000091d0000013d000000000ba8001900000000000a004b000009810000613d000000000c070019000000000d08001900000000ce0c0434000000000ded04360000000000bd004b0000097d0000c13d000000000009004b0000098e0000613d0000000007a700190000000309900210000000000a0b0433000000000a9a01cf000000000a9a022f00000000070704330000010009900089000000000797022f00000000079701cf0000000007a7019f00000000007b0435000000000558001900000000000504350000006005200039001100000005001d0000000000650435000003e005100039000000000505043300000afb0050009c0000090b0000213d00000000044500190000001f05400039000000000035004b0000090b0000813d000000006404043400000afb0040009c000000e80000213d0000001f0540003900000ba0055001970000003f0550003900000ba007500197000000400500043d0000000007750019000000000057004b0000000008000039000000010800403900000afb0070009c000000e80000213d0000000100800190000000e80000c13d000000400070043f00000000074504360000000008640019000000000038004b0000090b0000213d00000ba0084001970000001f0340018f000000000076004b000009d00000813d000000000008004b000009c00000613d000000000a3600190000000009370019000000200990008a000000200aa0008a000000000b890019000000000c8a0019000000000c0c04330000000000cb0435000000200880008c000009ba0000c13d000000000003004b000009e60000613d0000000009070019000009dc0000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000006ba0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000009cb0000c13d000006ba0000013d0000000009870019000000000008004b000009d90000613d000000000a060019000000000b07001900000000ac0a0434000000000bcb043600000000009b004b000009d50000c13d000000000003004b000009e60000613d00000000068600190000000303300210000000000809043300000000083801cf000000000838022f00000000060604330000010003300089000000000636022f00000000033601cf000000000383019f0000000000390435000000000347001900000000000304350000008003200039001000000003001d00000000005304350000040003100039000000000303043300000afe0030009c0000090b0000213d000000a004200039000e00000004001d00000000003404350000042003100039000000000303043300000aff0030009c0000090b0000213d000000c004200039000d00000004001d0000000000340435000004400310003900000000030304330000ffff0030008c0000090b0000213d000000e004200039000c00000004001d0000000000340435000004600310003900000000030304330000ffff0030008c0000090b0000213d0000010004200039000b00000004001d00000000003404350000048003100039000000000303043300000aff0030009c0000090b0000213d0000012004200039000a00000004001d0000000000340435000004a003100039000000000303043300000afe0030009c0000090b0000213d0000014004200039000900000004001d0000000000340435000004c003100039000000000303043300000aff0030009c0000090b0000213d0000016004200039000800000004001d0000000000340435000004e003100039000000000303043300000aff0030009c0000090b0000213d0000018004200039000700000004001d00000000003404350000050003100039000000000303043300000b000030009c0000090b0000213d000001a004200039000600000004001d00000000003404350000052003100039000000000303043300000aff0030009c0000090b0000213d000001c004200039000500000004001d00000000003404350000054003100039000000000303043300000afe0030009c0000090b0000213d000001e004200039000400000004001d00000000003404350000056003100039000000000303043300000afe0030009c0000090b0000213d0000020004200039000300000004001d00000000003404350000058003100039000000000303043300000b010030009c0000090b0000213d00000220042000390000000000340435000005a003100039000000000303043300000b010030009c0000090b0000213d00000240042000390000000000340435000005c0031000390000000003030433000000ff0030008c0000090b0000213d00000260042000390000000000340435000005e001100039000000000101043300000aff0010009c0000090b0000213d000002800320003900000000001304350000000001000411000000e00010043f0000000001020433000f00000001001d0000000012010434000200000002001d00000afb0020009c000000e80000213d000000000200041a000000010420019000000001032002700000007f0330618f0000001f0030008c00000000020000390000000102002039000000000024004b000003d50000c13d000000200030008c00000a7a0000413d00000002040000290000001f02400039000000050220027000000b020220009a000000200040008c00000b0302004041000000000000043f0000001f03300039000000050330027000000b020330009a000000000032004b00000a7a0000813d000000000002041b0000000102200039000000000032004b00000a760000413d00000002020000290000001f0020008c00000a8d0000a13d000000000000043f000000000100041400000af80010009c00000af801008041000000c00110021000000b04011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000200200008a0000000202200180000000000101043b00000a990000c13d000000200300003900000aa50000013d000000020000006b000000000200001900000a910000613d00000000020104330000000204000029000000030140021000000ba20110027f00000ba201100167000000000112016f0000000102400210000000000121019f00000ab30000013d000000010320008a00000005033002700000000004310019000000200300003900000001044000390000000f053000290000000005050433000000000051041b00000020033000390000000101100039000000000041004b00000a9e0000c13d000000020020006c00000ab00000813d00000002020000290000000302200210000000f80220018f00000ba20220027f00000ba2022001670000000f033000290000000003030433000000000223016f000000000021041b0000000201000029000000010110021000000001011001bf000000000010041b00000013010000290000000001010433001300000001001d0000000012010434000f00000002001d00000afb0020009c000000e80000213d0000000102000039000000000302041a000000010030019000000001023002700000007f0220618f0000001f0020008c00000000040000390000000104002039000000000343013f0000000100300190000003d50000c13d000000200020008c00000ad90000413d0000000103000039000000000030043f0000000f040000290000001f03400039000000050330027000000b050330009a000000200040008c00000b06030040410000001f02200039000000050220027000000b050220009a000000000023004b00000ad90000813d000000000003041b0000000103300039000000000023004b00000ad50000413d0000000f020000290000001f0020008c0002000100200218000100030020021800000aef0000a13d0000000101000039000000000010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b04011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000200200008a0000000f02200180000000000101043b00000af90000c13d000000200300003900000b050000013d0000000f0000006b000000000200001900000af30000613d0000000002010433000000010100008a0000000103100250000000000113013f000000000112016f00000002011001af00000b110000013d000000010320008a000000050330027000000000043100190000002003000039000000010440003900000013053000290000000005050433000000000051041b00000020033000390000000101100039000000000041004b00000afe0000c13d0000000f0020006c00000b0f0000813d0000000102000029000000f80220018f00000ba20220027f00000ba20220016700000013033000290000000003030433000000000223016f000000000021041b000000020100002900000001011001bf0000000102000039000000000012041b0000000e01000029000000000101043300000afe01100197000000800010043f000000120100002900000000010104330000000202000039000000000012041b00000011010000290000000001010433001300000001001d0000000012010434001200000002001d00000afb0020009c000000e80000213d0000000302000039000000000302041a000000010030019000000001023002700000007f0220618f0000001f0020008c00000000040000390000000104002039000000000343013f0000000100300190000003d50000c13d000000200020008c00000b400000413d0000000303000039000000000030043f00000012040000290000001f03400039000000050330027000000b070330009a000000200040008c00000b08030040410000001f02200039000000050220027000000b070220009a000000000023004b00000b400000813d000000000003041b0000000103300039000000000023004b00000b3c0000413d00000012020000290000001f0020008c0011000100200218000f00030020021800000b560000a13d0000000301000039000000000010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b04011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000200200008a0000001202200180000000000101043b00000b600000c13d000000200300003900000b6c0000013d000000120000006b000000000200001900000b5a0000613d0000000002010433000000010100008a0000000f03100250000000000113013f000000000112016f00000011011001af00000b780000013d000000010320008a000000050330027000000000043100190000002003000039000000010440003900000013053000290000000005050433000000000051041b00000020033000390000000101100039000000000041004b00000b650000c13d000000120020006c00000b760000813d0000000f02000029000000f80220018f00000ba20220027f00000ba20220016700000013033000290000000003030433000000000223016f000000000021041b000000110100002900000001011001bf0000000302000039000000000012041b00000010010000290000000001010433001300000001001d0000000012010434001200000002001d00000afb0020009c000000e80000213d0000000402000039000000000302041a000000010030019000000001023002700000007f0220618f0000001f0020008c00000000040000390000000104002039000000000343013f0000000100300190000003d50000c13d000000200020008c00000b9f0000413d0000000403000039000000000030043f00000012040000290000001f03400039000000050330027000000b090330009a000000200040008c00000b0a030040410000001f02200039000000050220027000000b090220009a000000000023004b00000b9f0000813d000000000003041b0000000103300039000000000023004b00000b9b0000413d00000012020000290000001f0020008c0011000100200218001000030020021800000bb50000a13d0000000401000039000000000010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b04011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000200200008a0000001202200180000000000101043b00000bbf0000c13d000000200300003900000bcb0000013d000000120000006b000000000200001900000bb90000613d0000000002010433000000010100008a0000001003100250000000000113013f000000000112016f00000011011001af00000bd70000013d000000010320008a000000050330027000000000043100190000002003000039000000010440003900000013053000290000000005050433000000000051041b00000020033000390000000101100039000000000041004b00000bc40000c13d000000120020006c00000bd50000813d0000001002000029000000f80220018f00000ba20220027f00000ba20220016700000013033000290000000003030433000000000223016f000000000021041b000000110100002900000001011001bf0000000402000039000000000012041b0000000d01000029000000000101043300000aff01100197000001600010043f0000000c0100002900000000010104330000ffff0110018f000002400010043f0000000b0100002900000000010104330000ffff0110018f000001c00010043f0000000a01000029000000000101043300000aff01100197000002000010043f0000000901000029000000000101043300000afe01100197000002200010043f0000000802000029000000000202043300000aff02200197000000c00020043f0000000703000029000000000303043300000aff03300197000001000030043f0000000603000029000000000303043300000b0003300197000001200030043f0000000503000029000000000303043300000aff03300197000001400030043f0000000403000029000000000303043300000afe03300197000001800030043f0000000303000029000000000303043300000afe03300197000001a00030043f000001e00010043f00000b0b01000041000000400300043d001200000003001d00000000001304350000000001000414000000040020008c00000c120000c13d0000000003000031000000200030008c0000002004000039000000000403401900000c3c0000013d000000120300002900000af80030009c00000af803008041000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0c011001c72bdc2bd70000040f000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f0000002007400190000000120570002900000c2b0000613d000000000801034f0000001209000029000000008a08043c0000000009a90436000000000059004b00000c270000c13d000000000006004b00000c380000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f0002000000010355000000010020019000000ca20000613d0000001f01400039000000600110018f0000001204100029000000000014004b00000000020000390000000102004039001300000004001d00000afb0040009c000000e80000213d0000000100200190000000e80000c13d0000001302000029000000400020043f000000200030008c0000090b0000413d00000012020000290000000002020433001100000002001d00000aff0020009c0000090b0000213d000001200200043d00000b0d040000410000001305000029000000000045043500000b00022001970000000404500039000000000024043500000000020004140000001104000029000000040040008c00000c880000613d000000130100002900000af80010009c00000af801008041000000400110021000000af80020009c00000af802008041000000c002200210000000000112019f00000b0e011001c700000011020000292bdc2bd70000040f000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f0000002007400190000000130570002900000c750000613d000000000801034f0000001309000029000000008a08043c0000000009a90436000000000059004b00000c710000c13d000000000006004b00000c820000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f0002000000010355000000010020019000000cae0000613d0000001f01400039000000600110018f000000130610002900000afb0060009c000000e80000213d000000400060043f000000200030008c0000090b0000413d0000001301000029000000000401043300000b0f0040019800000b1001000041000000000100601900000b1102400197000000000121019f000000000014004b0000090b0000c13d000001800100043d00000afe0210019800000cfd0000613d000000800300043d00000afe0130019800000cfd0000613d00000b120020009c00000cba0000813d0000006007000039000000c00900003900000cc30000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000006ba0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00000ca90000c13d000006ba0000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000006ba0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00000cb50000c13d000006ba0000013d00000ba2082001290000006007000039000000000007004b000007ed0000613d000000010770008a0000000109700210000000010a90020f0000000000a8004b00000cbc0000a13d00000000089201cf00000000081800d900000b130080009c00000000090000390000008009002039000000000a98022f00000b1400a0009c00000040099021bf000000000a98022f00000b0100a0009c00000020099021bf000000000a98022f00000b0000a0009c00000010099021bf000000000a98022f00000b150aa0009a0000000109900270000000b50990020f0000000009a900a90000001209900272000000000a000019000000000a98c0d900000000099a00190000000109900272000000000a000019000000000a98c0d900000000099a00190000000109900272000000000a000019000000000a98c0d900000000099a00190000000109900272000000000a000019000000000a98c0d900000000099a00190000000109900272000000000a000019000000000a98c0d900000000099a00190000000109900272000000000a000019000000000a98c0d900000000099a00190000000109900272000000000a000019000000000a98c0d900000000099a00190000000109900272000000000a000019000000000a98c0d900000000009a004b000000010990408a000000600870008900000000078901cf000000ff0080008c000000000700201900000aff0070009c00000d010000a13d00000b5c01000041000000000010043f00000b0c0100004100002bde0001043000000b160870009a00000b170080009c00000d070000213d000000440160003900000b5b0200004100000dd10000013d000000200870021000000b180880019700000b190080009c00000000090000390000008009002039000000000a98022f00000afb00a0009c000000000b000039000000400b002039000000000aba022f00000af800a0009c000000000c000039000000200c002039000000000aca022f0000ffff00a0008c000000000d000039000000100d002039000000000ada022f000000ff00a0008c000000000e000039000000080e002039000000000aea022f0000000f00a0008c000000000f000039000000040f002039000000000afa022f0000000300a0008c00000000050000390000000205002039000000000a5a022f0000000100a0008c00000001099021bf0000000009b9019f0000000009c9019f0000000009d9019f0000000009e9019f0000000009f9019f000000000559019f0000007f0950008c000000000998022f0000007f0a5000890000000008a801cf000000000809201900000000088800a9000000ff098002700000007f0a80027000000000099a022f00000000099900a9000000ff0a9002700000007f0b900270000000000aab022f000000000aaa00a9000000ff0ba002700000007f0ca00270000000000bbc022f000000c00880027000000b1a088001970000004005500210000000000585019f0000000008bb00a9000000ff0b8002700000007f0c800270000000000bbc022f000000c10990027000000b1b09900197000000000595019f0000000009bb00a9000000ff0b9002700000007f0c900270000000000bbc022f000000c20aa0027000000b1c0aa001970000000005a5019f000000000abb00a9000000ff0ba002700000007f0ca00270000000000bbc022f000000c30880027000000b1d08800197000000000585019f0000000008bb00a9000000ff0b8002700000007f0c800270000000000bbc022f000000c40990027000000b1e09900197000000000595019f0000000009bb00a9000000ff0b9002700000007f0c900270000000000bbc022f000000c50aa0027000000b1f0aa0019700000b200550009a00000000055a019f000000000abb00a9000000ff0ba002700000007f0ca00270000000000bbc022f000000c60880027000000b2108800197000000000585019f0000000008bb00a9000000ff0b8002700000007f0c800270000000000bbc022f000000c70990027000000b2209900197000000000595019f0000000009bb00a9000000ff0b9002700000007f0c900270000000000bbc022f000000c80aa0027000000b230aa001970000000005a5019f000000000abb00a9000000ff0ba002700000007f0ca00270000000000bbc022f000000c90880027000000b2408800197000000000585019f0000000008bb00a9000000ff0b8002700000007f0c800270000000000bbc022f000000ca0990027000000b2509900197000000000595019f000000cb09a0027000000b2609900197000000000595019f000000cc0880027000000b2708800197000000000585019f0000000008bb00a9000000cd0880027000000b280880019700000000088501a000000b29098000d100000db30000613d00000ba20080009c00000d990000c13d00000b2a0090009c000007ed0000613d00000b2a8580012c00000b2a0880c09900000b2aba90012c00000000055a013f00000b2a0bb0c09900000b2b0090009c000000000a00001900000b2a0a00404100000b2a0c90019700000b2a0dc0016700000b2a00c0009c000000000c00001900000b2a0c00204100000b2a00d0009c000000000c0ac01900000000088b00d9000000ff05500212000000000a85004900000000055a019f0000000005086019000000000008004b000000000805c01900000000000c004b000007ed0000c13d00000b290080009c000007ed0000c13d00000b2c0890009a00000b2d0080019800000b100a000041000000000a00601900000afc0090009c00000dbb0000213d00000b2e0090009c000007ed0000213d000000800580027000000b110550019700000000085a019f00000b2f0590009a000000800950027000000b110990019700000b2d0050019800000b10050000410000000005006019000000000995019f000000000098004b00000e380000613d00000afc0090009c000000000a09001900000dcd0000a13d00000b2a0090009c000007ed0000613d000000000a90008900000b3000a0009c00000ddf0000a13d000000440160003900000b4a02000041000000000021043500000024016000390000000102000039000000000021043500000b4b01000041000000000016043500000004016000390000002002000039000000000021043500000af80060009c00000af806008041000000400160021000000b4c011001c700002bde000104300000000100a0019000000b320600004100000b310600604100000b33056000d100000080055002700000000200a00190000000000605c01900000b34056000d100000080055002700000000400a00190000000000605c01900000b35056000d100000080055002700000000800a00190000000000605c01900000b36056000d100000080055002700000001000a00190000000000605c01900000b37056000d100000080055002700000002000a00190000000000605c01900000b38056000d100000080055002700000004000a00190000000000605c01900000b39056000d100000080055002700000008000a00190000000000605c01900000b3a056000d100000080055002700000010000a00190000000000605c01900000b3b056000d100000080055002700000020000a00190000000000605c01900000b3c056000d100000080055002700000040000a00190000000000605c01900000b3d056000d100000080055002700000080000a00190000000000605c01900000b3e056000d100000080055002700000100000a00190000000000605c01900000b3f056000d100000080055002700000200000a00190000000000605c01900000b40056000d100000080055002700000400000a00190000000000605c01900000b41056000d100000080055002700000800000a00190000000000605c01900000b42056000d1000000800550027000000b4300a00198000000000605c01900000b44056000d1000000800550027000000b4500a00198000000000605c01900000b46056000d1000000800550027000000b4700a00198000000000605c01900000b4800a0019800000b49056000d1000000800650c27000000afc0090009c00000e320000213d000000000009004b00000e320000613d00000ba206600129000000200560027000000af800600198000000010550c03900000aff05500197000000000075004b000000000809a019000000000004004b000007f70000613d00000b110580019700000b0f0080019800000b10060000410000000006006019000000000656019f00000ba20040009c00000e440000c13d00000b2a0500004100000b2a0060009c00000e500000613d00000b2a7540012c00000b2a0770c09900000b2a8660012c000000000656013f00000b2a0880c09900000000057800d9000000ff066002120000000007560049000000000667019f0000000006056019000000000005004b000000000506c01900000b2a6040012c00000b2a0660c09900000b2a0700004100000b307070012b00000b2a0770c09900000000054500a900000b110850019700000b0f0050019800000b10050000410000000005006019000000000585019f000000000445004900000b110540019700000b0f0040019800000b10040000410000000004006019000000000454019f000003200040043f00000000406700d9000000000000004b0000000005040019000000000550c089000000000004004b000000000405c01900000b3004400099000003400040043f000000700330021000000b4d033001970000000704000039000000000504041a00000b4e05500197000000000353019f000000000323019f000000000034041b000001a00300043d00000afe03300197000000000423004900000afe0040009c000007ed0000213d000000000023004b000007ed0000413d00000000021400a900000000023200d900000b4f0020009c000008410000813d000002800020043f000000000121004900000afe0010009c000007ed0000213d0000000802000039000000000402041a00000b4e04400197000000700110021000000b4d01100197000000000141019f000000000131019f000000000012041b00000b50010000410000000000100443000000000100041400000af80010009c00000af801008041000000c00110021000000b51011001c70000800b020000392bdc2bd70000040f000000010020019000000f180000613d000000000101043b00000b0101100197000002600010043f0000003a02000029001000000002001d0000022002200039000000000202043300000b0102200197000002c00020043f000000000112001900000b010010009c000007ed0000213d000002a00010043f00000010020000290000024001200039000000000101043300000b0101100197000002e00010043f00000260012000390000000001010433000000ff0110018f000003000010043f000000800100043d00000afe0110019700000b520110009a001300000001001d00000afe0010009c000007ed0000213d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000000101043b000000000201041a00000b540220019700000013022001af000000000021041b000000800100043d00000afe0110019700000b520110009a00000afe0010009c000007ed0000213d000000400200043d000000000012043500000af80020009c00000af8020080410000004001200210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b04011001c70000800d02000039000000030300003900000b5504000041000000000500001900000000060004102bdc2bd20000040f00000001002001900000090b0000613d0000000001000411000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000090b0000613d000000000101043b000000000201041a00000b540220019700000b52022001c7000000000021041b00000b5201000041000000400200043d000000000012043500000af80020009c00000af8020080410000004001200210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b04011001c70000800d02000039000000030300003900000b5504000041000000000500001900000000060004112bdc2bd20000040f00000001002001900000090b0000613d000001200100043d00000b0001100197000000400400043d0000004402400039000001400300043d000000000012043500000aff013001970000002402400039000000000012043500000b56010000410000000000140435001200000004001d00000004014000390000000002000410000000000021043500000000010004140000001102000029000000040020008c00000f190000c13d0000000003000031000000200030008c0000002004000039000000000403401900000f440000013d000000000001042f000000120200002900000af80020009c00000af802008041000000400220021000000af80010009c00000af801008041000000c001100210000000000121019f00000b4c011001c700000011020000292bdc2bd70000040f000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f0000002007400190000000120570002900000f330000613d000000000801034f0000001209000029000000008a08043c0000000009a90436000000000059004b00000f2f0000c13d000000000006004b00000f400000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000010080000613d0000001f01400039000000600110018f0000001204100029000000000014004b00000000020000390000000102004039001300000004001d00000afb0040009c000000e80000213d0000000100200190000000e80000c13d0000001302000029000000400020043f000000200030008c0000090b0000413d0000001202000029000000000202043300000aff0020009c0000090b0000213d000000000002004b000010140000c13d000000100200002900000280022000390000000002020433000001200400043d00000b000440019700000013080000290000004405800039000000c00600043d000001400700043d000000000045043500000aff047001970000002405800039000000000045043500000aff022001970000006404800039000000000024043500000b58020000410000000000280435000000040280003900000000040004100000000000420435000000000400041400000aff02600197000000040020008c00000f9e0000613d000000130100002900000af80010009c00000af801008041000000400110021000000af80040009c00000af804008041000000c003400210000000000113019f00000b59011001c72bdc2bd20000040f000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f0000002007400190000000130570002900000f8b0000613d000000000801034f0000001309000029000000008a08043c0000000009a90436000000000059004b00000f870000c13d000000000006004b00000f980000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000010180000613d0000001f01400039000000600110018f000000130110002900000afb0010009c000000e80000213d000000400010043f000000200030008c0000090b0000413d0000001301000029000000000101043300000aff0010009c0000090b0000213d000000a00010043f000000800200043d0000014000000443000001600020044300000020030000390000018000300443000001a0001004430000004001000039000000c00200043d000001c000100443000001e0002004430000006001000039000000e00200043d000002000010044300000220002004430000008001000039000001000200043d00000240001004430000026000200443000000a001000039000001200200043d0000028000100443000002a000200443000000c001000039000001400200043d000002c000100443000002e000200443000000e001000039000001600200043d000003000010044300000320002004430000010001000039000001800200043d000003400010044300000360002004430000012001000039000001a00200043d0000038000100443000003a0002004430000014001000039000001c00200043d000003c000100443000003e0002004430000016001000039000001e00200043d000004000010044300000420002004430000018001000039000002000200043d00000440001004430000046000200443000001a001000039000002200200043d0000048000100443000004a000200443000001c001000039000002400200043d000004c000100443000004e000200443000001e001000039000002600200043d000005000010044300000520002004430000020001000039000002800200043d000005400010044300000560002004430000022001000039000002a00200043d0000058000100443000005a0002004430000024001000039000002c00200043d000005c000100443000005e0002004430000026001000039000002e00200043d000006000010044300000620002004430000028001000039000003000200043d00000640001004430000066000200443000002a001000039000003200200043d0000068000100443000006a000200443000002c001000039000003400200043d000006c000100443000006e00020044300000100003004430000001701000039000001200010044300000b5a0100004100002bdd0001042e0000001f0530018f00000afa06300198000000400200043d0000000004620019000006ba0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b0000100f0000c13d000006ba0000013d00000b5701000041000000000010043f00000b0c0100004100002bde000104300000001f0530018f00000afa06300198000000400200043d0000000004620019000006ba0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b0000101f0000c13d000006ba0000013d00000ba30010009c000010290000813d0000004001100039000000400010043f000000000001042d00000b9201000041000000000010043f0000004101000039000000040010043f00000b0e0100004100002bde000104300000001f0220003900000ba0022001970000000001120019000000000021004b0000000002000039000000010200403900000afb0010009c0000103b0000213d00000001002001900000103b0000c13d000000400010043f000000000001042d00000b9201000041000000000010043f0000004101000039000000040010043f00000b0e0100004100002bde00010430000000200300003900000000033104360000000042020434000000000023043500000ba0062001970000001f0520018f0000004001100039000000000014004b0000105a0000813d000000000006004b000010560000613d00000000085400190000000007510019000000200770008a000000200880008a0000000009670019000000000a680019000000000a0a04330000000000a90435000000200660008c000010500000c13d000000000005004b000010700000613d0000000007010019000010660000013d0000000007610019000000000006004b000010630000613d00000000080400190000000009010019000000008a0804340000000009a90436000000000079004b0000105f0000c13d000000000005004b000010700000613d00000000046400190000000305500210000000000607043300000000065601cf000000000656022f00000000040404330000010005500089000000000454022f00000000045401cf000000000464019f0000000000470435000000000421001900000000000404350000001f0220003900000ba0022001970000000001210019000000000001042d000000003101043400000afe011001970000000001120436000000000203043300000afe022001970000000000210435000000000001042d00000afe0110019700000afe02200197000000000112001900000b4f0010009c000010830000813d000000000001042d00000b9201000041000000000010043f0000001101000039000000040010043f00000b0e0100004100002bde00010430000000400100043d00000ba30010009c000010920000813d0000004002100039000000400020043f000000200210003900000000000204350000000000010435000000000001042d00000b9201000041000000000010043f0000004101000039000000040010043f00000b0e0100004100002bde000104300003000000000002000200000003001d00000aff01100198000010d10000613d00030aff0020019c000010d10000613d000100000001001d000000000010043f0000000a01000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000000303000029000010cf0000613d000000000101043b000000000030043f000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000003060000290000000100200190000010cf0000613d000000000101043b0000000202000029000000000021041b000000400100043d000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b04011001c70000800d02000039000000030300003900000b9d0400004100000001050000292bdc2bd20000040f0000000100200190000010cf0000613d000000000001042d000000000100001900002bde0001043000000b9e01000041000000000010043f00000b0c0100004100002bde00010430000600000000000200060aff0010019c000011c10000613d00000aff02200198000011c10000613d0000000501000039000000000101041a000300000001001d00000b8a0110019700000b8b0010009c000500000002001d000400000003001d0000113e0000613d00000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000011c90000613d000000000101043b00000aff02100197000000050020006b0000000403000029000010f80000613d000000060020006b0000113e0000c13d000200000002001d0000000002000410000000060020006b000010fe0000c13d0000000101000039000011120000013d00000b5d0100004100000000001004430000000001000412000000040010044300000060010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000011c90000613d000000000101043b00000aff01100197000000060010006b00000000010000390000000101006039000100000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000011c90000613d000000000101043b00000aff021001970000000001000411000000000021004b00000005040000290000000403000029000000020600002900000000070004100000112d0000c13d000000010000006b0000113e0000c13d000000000061004b000011ca0000c13d000000060170014f000000000264013f00000000001201a000000000010000390000000101006039000000060060006b0000113c0000c13d000000000074004b0000113c0000c13d0000000502000039000000000202041a00000ba400200198000000010110c1bf0000000100100190000011ca0000613d00000afe0030009c000011c50000213d0000000601000029000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000000100200190000011bf0000613d000000030200002900000b9600200198000000000101043b000000000601041a00000afe02600197000000700360027000000004050000290000117f0000c13d000000000052004b000011910000813d00000afe02300197000000000565004900000afe04500197000000000042004b0000000504000029000011c50000413d000300000005001d0000000002530049000000700220021000000b4d0220019700000b4e03600197000000000232019f000000000021041b000000000040043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c70000801002000039000200000006001d2bdc2bd70000040f0000000100200190000011bf0000613d000000000101043b000000000201041a000000020320002900000afe0330019700000b4e04200197000000000343019f00000003040000290000007004400210000000000242001900000b4d02200197000000000223019f000000000021041b0000000404000029000011ab0000013d000000000052004b0000000504000029000011870000813d000000000263001900000afe02200197000000000052004b000011c50000413d00000ba506600197000000000252004900000afe0220019700000b5403600197000000000223019f000000000021041b000000000040043f0000000901000039000000200010043f00000000010004140000119b0000013d000000000256004900000afe0220019700000b5403600197000000000232019f000000000021041b0000000501000029000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000000100200190000011bf0000613d000000000101043b000000000201041a0000000404000029000000000342001900000afe0330019700000b5402200197000000000223019f000000000021041b000000400100043d000000000041043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b04011001c70000800d02000039000000030300003900000b5504000041000000060500002900000005060000292bdc2bd20000040f0000000100200190000011bf0000613d000000000001042d000000000100001900002bde0001043000000b9e01000041000000000010043f00000b0c0100004100002bde0001043000000b9a01000041000000000010043f00000b0c0100004100002bde00010430000000000001042f00000b8c01000041000000000010043f00000b0c0100004100002bde000104300000000501000039000000000201041a00000ba20120016700000b8b0110019700000ba60420019700000000004101a00000000003000039000000010300603900000b96002001980000000001000039000000010100c03900000b95002001980000000002000039000000010200c039000000000004004b0000000004000039000000010400c039000000000001042d0001000000000002000100000001001d00000b5d01000041000000000010044300000000010004120000000400100443000001c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000011f90000613d000000000101043b0000ffff0110018f00000001011000b900000b9701100197000027100110011a000000010110006900000afe01100197000000000001042d000000000001042f0003000000000002000000000002004b000011fe0000613d000000000001042d000300000001001d0000000601000039000000000101041a000200000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000160010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f00000001002001900000122f0000613d000000000101043b000100000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000140010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f00000001002001900000122f0000613d0000000103000029000000020230006a00000afe02200197000000000101043b0000ffff0110018f00000003011000b900000b9701100197000027100110011a000000000012004b0000000002018019000000030120006900000afe01100197000000000001042d000000000001042f00000aff01100197000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000000100200190000012430000613d000000000101043b000000000201041a00000afe01200197000000700220027000000afe02200197000000000001042d000000000100001900002bde000104300015000000000002000e00000002001d000d00000001001d0000000001000416000000010110008a00000afe0010009c00001d710000813d0000000501000039000000000101041a001000000001001d00000b8a0110019700000b8b0010009c00001d750000613d00000b5d01000041000000000010044300000000010004120000000400100443000001c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b001100000001001d00000b5d01000041000000000010044300000000010004120000000400100443000000e0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d00000011020000290000ffff0220018f000000000300041600000000023200a900000b97022001970011271000200122000000000101043b000f00000001001d000027100020008c000012f50000413d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000110020006c00001d830000413d000c00000002001d0000000f01000029000000040010008c000012990000613d000080090200003900000ba70100004100000011030000290000000f0400002900000000050000192bdc2bd20000040f000000600210027000000af80020019d000200000001035500000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b0000000c0010006c000012f50000c13d000000400100043d00000ba80010009c00001d6b0000813d000000240210003900000ba9030000410000000000320435000000440210003900000000030004140000006004000039000000000042043500000baa020000410000000000210435000000640210003900000000000204350000000402100039000000000002043500000af80010009c00000af801008041000000400110021000000af80030009c00000af803008041000000c002300210000000000121019f00000bab011001c700008006020000392bdc2bd20000040f000000010020019000001d8b0000613d000000000201043b000000000002004b00001d900000613d0000000f0100002900000aff05100197000000000050043f000000000100041400000aff04200197000000040040008c000012e10000613d00000af80010009c00000af801008041000000c00110021000000b04011001c700008009020000390000001103000029000f00000004001d0000000f04000029000c00000005001d00000000050000192bdc2bd20000040f0000000f040000290000000c05000029000000600310027000000af80030019d0002000000010355000000010020019000001d630000613d000000400100043d000000200210003900000000004204350000001102000029000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b53011001c70000800d02000039000000020300003900000bac040000412bdc2bd20000040f000000010020019000001d630000613d000000100100002900000b95001001980000000001000416000000110510006a000013380000c13d001100000005001d0000000601000039000000000101041a000f00000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000160010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000c00000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000140010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d0000000f060000290000000c0260006900000afe02200197000000000101043b0000ffff0110018f000000110500002900000000015100a900000b9701100197000027100110011a000000000012004b0000000004000019000013300000213d0000000503000039000000000103041a00000b95011001c7000000000013041b00000001040000390000000001020019000000000261001900000afe0220019700000b5403600197000000000232019f0000000603000039000000000023041b0000000005150049000013390000013d0000000104000039000000100100002900000b960010019800000afe01500197001300000000003d001500000000003d001200000000003d000b00000004001d000014290000c13d000900000001001d0000000701000039000000000101041a000c00000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000120010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d0000000c0200002900000afe02200197000000000101043b00000afe01100197000500000001001d000a00000002001d000000000121004900000b4f0010009c00001d650000813d000000090210006b000800000000001d000f00090000002d000013650000a13d00000afe0020009c000800000002001d000f00000001001d00001d650000213d0015000f0000002d0000000a010000290000000f0110002a00001d7d0000613d0000000c02000029000000700220027000000afe02200197000700000002001d0000000f022000b900000000011200d9001100000001001d00000afe0010009c00001d710000213d00000b5d0100004100000000001004430000000001000412000000040010044300000220010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000600000001001d00000b50010000410000000000100443000000000100041400000af80010009c00000af801008041000000c00110021000000b51011001c70000800b020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000060100002900000b0101100197000000000012004b000014450000813d000600000002001d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f000000010020019000001d630000613d000000000101043b000000000101041a000400000001001d00000b5d01000041000000000010044300000000010004120000000400100443000001e0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b00000b0101100197000600060010007300001d650000413d00000b5d0100004100000000001004430000000001000412000000040010044300000200010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000060200002900010afe0020019b000000000101043b00000afe0110019700000001011000b9000300000001001d00000afe0010009c00001d650000213d00000b5d0100004100000000001004430000000001000412000000040010044300000240010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b00020b010010019c00001d7d0000613d00000b5d010000410000000000100443000000000100041200000004001004430000002400000443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000030200002900000b190220019700000002022000fa000000000101043b00000afe01100197000000000321004900000afe0030009c00001d650000213d0000000402000029000000110120006a00000afe01100197000000000013004b000014450000a13d000300000001001d000400000003001d00000b5d0100004100000000001004430000000001000412000000040010044300000260010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b00000b0102100197000000010020006b000014470000813d000200000002001d00000b5d0100004100000000001004430000000001000412000000040010044300000280010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d0000000203000029000000060230006a000000000101043b000000ff0110018f00000000012100a900000afe0110019700000000013100d90000000201100039000014480000013d001400000001001d0000000106000039001100000000001d00000afe051001980000148f0000613d0000000801000039000000000301041a00000afe023001970000000002520019000000700430027000000afe0440019700000000055400a900000000052500d900000afe0050009c00001d710000213d001200000005001d00000afe0020009c00001d650000213d000000000454004900000afe0040009c00001d650000213d00000b4e03300197000000700440021000000b4d04400197000000000343019f000000000223019f000000000021041b000014900000013d001300110000002d0000146e0000013d00000002010000390000000405000029000000030600002900000000020000190000001103000029000014510000013d00000000035400d90000000102200039000000000012004b000014580000813d00000000046300a9000000000003004b0000144d0000613d00000000033400d9000000000063004b0000144d0000613d00001d650000013d00130afe0030019b00000afe01300197000000110010006b0000146d0000a13d000000070210006b00001d650000413d00001d7d0000613d0000000a031000b900000000022300d900000afe0020009c00001d710000213d0000000f04000029000000080340002900000afe0030009c00001d650000213d0000000003230049000800000003001d00000afe0030009c00001d650000213d001500000002001d000f00000002001d001100000001001d0000000f02000029000000090120006900000afe0010009c00001d650000213d001400000001001d0000000f020000290000000a0120002900000afe0010009c00001d650000213d0000000703000029000000110230006a00000afe0020009c00001d650000213d0000000c0300002900000b4e03300197000000700220021000000b4d02200197000000000232019f000000000212019f0000000703000039000000000023041b000000050010006c0000148b0000c13d0000000502000039000000000102041a00000b96011001c7000000000012041b00000001060000390000148c0000013d0000000006000019000000080100002900000afe051001980000142e0000c13d0000000005000019000000110250002900000afe0020009c00001d650000213d000c00000005001d000a00000006001d000f00000002001d0000000e0020006c00001d790000413d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000000f0400002900001d630000613d000000000101043b000000000201041a000000000342004900000afe0330019700000b5402200197000000000223019f000000000021041b0000000d0100002900000aff01100197000e00000001001d000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000000f05000029000000010020019000001d630000613d000000000101043b000000000201041a000000110320002900000afe0330019700000b4e04200197000000000343019f0000000c040000290000007004400210000000000242001900000b4d02200197000000000223019f000000000021041b000000400100043d000000000051043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b04011001c70000800d02000039000000030300003900000b550400004100000000050004100000000e060000292bdc2bd20000040f000000010020019000001d630000613d000000100100002900000ba6021001970000000b0000006b0000000001000039000000010100c039000000000002004b000018050000c13d0000000a00100180000018050000613d0000000502000039000000000102041a00000ba4011001c7000000000012041b00000b5d0100004100000000001004430000000001000412000000040010044300000060010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b00000b8d02000041000000000020044300000aff01100197001100000001001d0000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000000000001004b00001d630000613d000000400400043d00000bad01000041000000000014043500000000010004140000001102000029000000040020008c000015220000613d00000af80040009c00000af8030000410000000003044019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0c011001c7001100000004001d2bdc2bd20000040f0000001104000029000000600310027000000af80030019d0002000000010355000000010020019000001d9e0000613d00000afb0040009c00001d6b0000213d000000400040043f00000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000000000200041000000aff0220019800001d870000613d00110aff0010019c00001d870000613d001000000002001d000000000020043f0000000a01000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000000100200190000000110300002900001d630000613d000000000101043b000000000030043f000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000001106000029000000010020019000001d630000613d000000000101043b000000010200008a000000000021041b000000400100043d000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b04011001c70000800d02000039000000030300003900000b9d0400004100000010050000292bdc2bd20000040f000000010020019000001d630000613d00000b5d01000041000000000010044300000000010004120000000400100443000000c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b001100000001001d000000400200043d00000bae01000041001000000002001d000000000012043500000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000110200002900000aff02200197000000000101043b000000100b0000290000002403b00039000000010400008a00000000004304350000000403b0003900000aff0110019700000000001304350000000001000414000000040020008c001100000002001d000015a10000c13d0000000003000031000000200030008c00000020040000390000000004034019000015cc0000013d00000af800b0009c00000af80300004100000000030b4019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b83011001c72bdc2bd20000040f000000100b000029000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000000057b0019000015bb0000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b000015b70000c13d000000000006004b000015c80000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f0002000000010355000000010020019000001dab0000613d0000001f01400039000000600210018f0000000001b20019000000000021004b0000000002000039000000010200403900000afb0010009c00001d6b0000213d000000010020019000001d6b0000c13d000000400010043f000000200030008c00001d630000413d00000000010b0433000000000001004b0000000002000039000000010200c039000000000021004b00001d630000c13d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f000000010020019000001d630000613d000000000101043b000000000101041a000d00000001001d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000f00000001001d00000b5d01000041000000000010044300000000010004120000000400100443000001a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b00000afe02100197000c00000002001d0000000f0120006b00001d650000413d001000000001001d00000b8d01000041000000000010044300000011010000290000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000000000001004b00001d630000613d000000400200043d00000baf01000041000000000012043500000000010004140000001104000029000000040040008c000016430000613d00000af80020009c000b00000002001d00000af802008041000000400220021000000af80010009c00000af801008041000000c001100210000000000121019f0000000c030000290000000f0030006b000016380000c13d00000b0c011001c700000000020400190000163c0000013d00000bb0011001c70000800902000039000000100300002900000000050000192bdc2bd20000040f0002000000010355000000600310027000000af80030019d00000001002001900000000b0200002900001db70000613d00000afb0020009c00001d6b0000213d000000400020043f00000b5d0100004100000000001004430000000001000412000000040010044300000100010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b00000afe011001970000001002000029000000000021001a00001d650000413d000000000221001a000016a60000613d0000000d01000029000f0afe0010019c000016a60000613d00000b120020009c000000c00400003900000060010000390000166c0000413d00000ba2032001290000006001000039000000000001004b00001d650000613d000000010110008a0000000104100210000000010540020f000000000053004b000016650000a13d00000000024201cf0000000f022000fa00000b130020009c00000000030000390000008003002039000000000432022f00000b140040009c00000040033021bf000000000432022f00000b010040009c00000020033021bf000000000432022f00000b000040009c00000010033021bf000000000432022f00000b150440009a0000000103300270000000b50330020f00000000034300a90000001203300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000232c0d90000000002006019000000000032004b000000010330408a000000600110008900000000021301cf000000ff0010008c000000000200201900000aff0020009c000018220000a13d0000000502000039000000000102041a00000bb40110019700000bb501100167000000000012041b00000b5d01000041000000000010044300000000010004120000000400100443000000c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000400b00043d00000b900100004100000000001b04350000000401b0003900000000030004100000000000310435000000000100041400000aff02200197000000040020008c001100000002001d000016cb0000c13d0000000003000031000000200030008c00000020040000390000000004034019000016f80000013d00000af800b0009c00000af80300004100000000030b4019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0e011001c700100000000b001d2bdc2bd70000040f000000100b000029000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000000057b0019000016e60000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b000016e20000c13d000000000006004b000016f30000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f0002000000010355000000010020019000001dc40000613d00000011020000290000001f01400039000000600410018f0000000001b40019000000000041004b0000000004000039000000010400403900000afb0010009c00001d6b0000213d000000010040019000001d6b0000c13d000000400010043f000000200030008c00001d630000413d00000000010b0433001000000001001d00000b8d0100004100000000001004430000000400200443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000000000001004b000000110200002900001d630000613d000000400400043d00000bb60100004100000000001404350000000401400039000000100300002900000000003104350000000001000414000000040020008c000017310000613d00000af80040009c00000af8030000410000000003044019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0e011001c7001100000004001d2bdc2bd20000040f0000001104000029000000600310027000000af80030019d0002000000010355000000010020019000001dd00000613d00000afb0040009c00001d6b0000213d000000400040043f00000b5d01000041000000000010044300000000010004120000000400100443000001a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b001100000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000180010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000110200002900110afe0020019c000000000101043b001000000001001d000017d20000613d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000110020006c00001d830000413d000f00000002001d0000001001000029000000040010008c000017760000613d000080090200003900000ba7010000410000001103000029000000100400002900000000050000192bdc2bd20000040f000000600210027000000af80020019d000200000001035500000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b0000000f0010006c000017d20000c13d000000400100043d00000bb70010009c00001d6b0000213d000000240210003900000ba9030000410000000000320435000000440210003900000000030004140000006004000039000000000042043500000baa020000410000000000210435000000640210003900000000000204350000000402100039000000000002043500000af80010009c00000af801008041000000400110021000000af80030009c00000af803008041000000c002300210000000000121019f00000bab011001c700008006020000392bdc2bd20000040f000000010020019000001ddd0000613d000000000201043b000000000002004b00001de20000613d000000100100002900000aff05100197000000000050043f000000000100041400000aff04200197000000040040008c000017be0000613d00000af80010009c00000af801008041000000c00110021000000b04011001c700008009020000390000001103000029001000000004001d0000001004000029000f00000005001d00000000050000192bdc2bd20000040f00000010040000290000000f05000029000000600310027000000af80030019d0002000000010355000000010020019000001d630000613d000000400100043d000000200210003900000000004204350000001102000029000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b53011001c70000800d02000039000000020300003900000bac040000412bdc2bd20000040f000000010020019000001d630000613d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f000000010020019000001d630000613d000000000101043b000000000101041a001100000001001d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d0000001102000029000000700220021000000b4d02200197000000000101043b00000afe01100197000000000121019f0000000802000039000000000302041a00000b4e03300197000000000131019f000000000012041b000000000100041400000af80010009c00000af801008041000000c00110021000000bb8011001c70000800d02000039000000010300003900000bb9040000412bdc2bd20000040f000000010020019000001d630000613d00000afe01000041000000150210017f000000400300043d0000000002230436000000140410017f00000000004204350000004002300039000000130410017f0000000000420435000000120110017f0000006002300039000000000012043500000af80030009c00000af8030080410000004001300210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000bc2011001c70000800d02000039000000020300003900000bc3040000410000000e050000292bdc2bd20000040f000000010020019000001d630000613d000000000001042d000b00000002001d000000400200043d00000bb101000041000c00000002001d0000000001120436000a00000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000000100041400000aff02200197000000040020008c000018420000c13d0000000003000031000000e00030008c000000e00400003900000000040340190000000c0b0000290000186d0000013d0000000c0300002900000af80030009c00000af803008041000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0c011001c72bdc2bd70000040f0000000c0b000029000000600310027000000af803300197000000e00030008c000000e00400003900000000040340190000001f0640018f000000e00740019000000000057b00190000185c0000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b000018580000c13d000000000006004b000018690000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f0002000000010355000000010020019000001e040000613d0000001f01400039000001e00110018f0000000005b10019000000000015004b0000000001000039000000010100403900000afb0050009c00001d6b0000213d000000010010019000001d6b0000c13d000000400050043f000000e00030008c00001d630000413d00000000010b043300000aff0010009c00001d630000213d0000000a02000029000000000202043300000b0f0020019800000b1003000041000000000300601900000b1104200197000000000343019f000000000032004b00001d630000c13d0000004002b0003900000000020204330000ffff0020008c00001d630000213d0000006002b0003900000000020204330000ffff0020008c00001d630000213d0000008002b0003900000000020204330000ffff0020008c00001d630000213d000000a002b000390000000002020433000000ff0020008c00001d630000213d000000c002b000390000000002020433000000000002004b0000000003000039000000010300c039000000000032004b00001d630000c13d0000000b02000029000000000021004b000000a003000039000019750000613d000000840150003900000000003104350000006401500039000000000021043500000044015000390000001002000029000000000021043500000bb2010000410000000000150435000000040150003900000000020004100000000000210435000000a40150003900000000000104350000002401500039000000000001043500000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c70000800502000039000d00000005001d2bdc2bd70000040f000000010020019000001d620000613d0000000d0b000029000000000201043b000000000100041400000aff02200197000000040020008c000018cc0000c13d0000000003000031000000400030008c00000040040000390000000004034019000018f70000013d00000af800b0009c00000af80300004100000000030b4019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000bb3011001c72bdc2bd20000040f0000000d0b000029000000600310027000000af803300197000000400030008c000000400400003900000000040340190000001f0640018f000000600740019000000000057b0019000018e60000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b000018e20000c13d000000000006004b000018f30000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f0002000000010355000000010020019000001e350000613d0000001f01400039000000e00110018f0000000002b1001900000afb0020009c00001d6b0000213d000000400020043f000000400030008c00001d630000413d00000bb101000041000d00000002001d0000000001120436000c00000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000000100041400000aff02200197000000040020008c0000191d0000c13d0000000003000031000000e00030008c000000e00400003900000000040340190000000d0b000029000019480000013d0000000d0300002900000af80030009c00000af803008041000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0c011001c72bdc2bd70000040f0000000d0b000029000000600310027000000af803300197000000e00030008c000000e00400003900000000040340190000001f0640018f000000e00740019000000000057b0019000019370000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b000019330000c13d000000000006004b000019440000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f0002000000010355000000010020019000001e410000613d0000001f01400039000001e00110018f0000000005b1001900000afb0050009c00001d6b0000213d000000400050043f000000e00030008c00001d630000413d00000000010b043300000aff0010009c00001d630000213d0000000c02000029000000000202043300000b0f0020019800000b1003000041000000000300601900000b1104200197000000000343019f000000000032004b00001d630000c13d0000004002b0003900000000020204330000ffff0020008c00001d630000213d0000006002b0003900000000020204330000ffff0020008c00001d630000213d0000008002b0003900000000020204330000ffff0020008c00001d630000213d000000a002b000390000000002020433000000ff0020008c00001d630000213d000000c002b000390000000002020433000000000002004b0000000003000039000000010300c039000000000032004b00001d630000c13d0000000b0010006b00001c0c0000c13d00000bba0050009c00001d6b0000213d0000016001500039000000400010043f000000000100041000000000021504360000001101000029000c00000002001d000000000012043500000b5d01000041000000000010044300000000010004120000000400100443000000a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c70000800502000039000d00000005001d2bdc2bd70000040f000000010020019000001d620000613d0000000d020000290000004002200039000000000101043b00000b0001100197000b00000002001d000000000012043500000b5d01000041000000000010044300000000010004120000000400100443000002a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b00000b110210019700000b0f0010019800000b10010000410000000001006019000000000121019f0000000d020000290000006002200039000a00000002001d000000000012043500000b5d01000041000000000010044300000000010004120000000400100443000002c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b0000000d0300002900000120043000390000000002000410000900000004001d0000000000240435000000c0043000390000001002000029000800000004001d0000000000240435000000a0043000390000000f02000029000600000004001d000000000024043500000b110210019700000b0f0010019800000b10010000410000000001006019000000000121019f0000008002300039000400000002001d00000000001204350000010001300039000700000001001d0000000000010435000000e001300039000500000001001d000000000001043500000b50010000410000000000100443000000000100041400000af80010009c00000af801008041000000c00110021000000b51011001c70000800b020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b0000000d030000290000014001300039000000000021043500000bbb02000041000000400400043d0000000002240436000300000002001d000000000203043300000aff02200197000000040340003900000000002304350000000c02000029000000000202043300000aff02200197000000240340003900000000002304350000000b02000029000000000202043300000b0002200197000000440340003900000000002304350000000a02000029000000000202043300000b110320019700000b0f0020019800000b10020000410000000002006019000000000232019f000000640340003900000000002304350000000402000029000000000202043300000b110320019700000b0f0020019800000b10020000410000000002006019000000000232019f0000008403400039000000000023043500000006020000290000000002020433000000a403400039000000000023043500000008020000290000000002020433000000c403400039000000000023043500000005020000290000000002020433000000e403400039000000000023043500000007020000290000000002020433000001040340003900000000002304350000000902000029000000000202043300000aff02200197000001240340003900000000002304350000000001010433001100000004001d0000014402400039000000000012043500000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000000100041400000aff02200197000000040020008c00001a3e0000c13d0000000003000031000000800030008c00000080040000390000000004034019000000110b00002900001a690000013d000000110300002900000af80030009c00000af803008041000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000bbc011001c72bdc2bd20000040f000000110b000029000000600310027000000af803300197000000800030008c000000800400003900000000040340190000001f0640018f000000e00740019000000000057b001900001a580000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b00001a540000c13d000000000006004b00001a650000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f0002000000010355000000010020019000001e100000613d0000001f01400039000001e00210018f0000000001b20019000000000021004b0000000002000039000000010200403900000afb0010009c00001d6b0000213d000000010020019000001d6b0000c13d000000400010043f000000800030008c00001d630000413d0000000301000029000000000101043300000b190010009c00001d630000213d00000000010b0433001100000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000080010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000d00000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b00000b8d02000041000000000020044300000aff011001970000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000000000001004b00001d630000613d0000000d0100002900000aff03100197000000400400043d00000024014000390000001102000029000000000021043500000bae010000410000000000140435000d00000004001d0000000401400039000c00000003001d000000000031043500000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000000100041400000aff02200197000000040020008c00001adc0000613d0000000d0300002900000af80030009c00000af803008041000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b83011001c72bdc2bd20000040f000000600310027000000af80030019d0002000000010355000000010020019000001e1c0000613d0000000d0100002900000afb0010009c00001d6b0000213d000000400010043f00000b5d0100004100000000001004430000000001000412000000040010044300000180010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000400400043d00000bbd010000410000000000140435000000040140003900000011030000290000000000310435000d00000004001d0000002401400039000b00000002001d00000aff02200197000a00000002001d000000000021043500000b5d01000041000000000010044300000000010004120000000400100443000000e0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d0000000d0b0000290000004402b00039000000000101043b00000aff01100197000000000012043500000000010004140000000c02000029000000040020008c00001b190000c13d0000000003000031000000200030008c0000002004000039000000000403401900001b440000013d00000af800b0009c00000af80300004100000000030b4019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b4c011001c72bdc2bd20000040f0000000d0b000029000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000000057b001900001b330000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b00001b2f0000c13d000000000006004b00001b400000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f0002000000010355000000010020019000001e290000613d0000001f01400039000000600210018f0000000001b20019000000000021004b0000000002000039000000010200403900000afb0010009c00001d6b0000213d000000010020019000001d6b0000c13d000000400010043f000000200030008c00001d630000413d00000000010b0433001100000001001d00000aff0010009c00001d630000213d0000000502000039000000000102041a00000bbe0110019700000011011001af000000000012041b00000b5d01000041000000000010044300000000010004120000000400100443000001a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000d0afe0010019c00001be20000613d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b0000000d0020006c00001d830000413d000c00000002001d0000000b04000029000000040040008c00001b880000613d000080090200003900000ba7010000410000000d0300002900000000050000192bdc2bd20000040f000000600210027000000af80020019d000200000001035500000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b0000000c0010006c00001be20000c13d000000400100043d00000bb70010009c00001d6b0000213d000000240210003900000ba9030000410000000000320435000000440210003900000000030004140000006004000039000000000042043500000baa020000410000000000210435000000640210003900000000000204350000000402100039000000000002043500000af80010009c00000af801008041000000400110021000000af80030009c00000af803008041000000c002300210000000000121019f00000bab011001c700008006020000392bdc2bd20000040f000000010020019000001e4d0000613d000000000201043b000000000002004b00001e520000613d0000000a01000029000000000010043f000000000100041400000aff04200197000000040040008c00001bcd0000613d00000af80010009c00000af801008041000000c00110021000000b04011001c700008009020000390000000d03000029000c00000004001d0000000c0400002900000000050000192bdc2bd20000040f0000000c04000029000000600310027000000af80030019d0002000000010355000000010020019000001d630000613d000000400100043d000000200210003900000000004204350000000d02000029000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b53011001c70000800d02000039000000020300003900000bac040000410000000a050000292bdc2bd20000040f000000010020019000001d630000613d000000400300043d00000020013000390000000f0200002900000000002104350000001001000029000f00000003001d000000000013043500000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b0000000f0100002900000af80010009c00000af8010080410000004001100210000000000300041400000af80030009c00000af803008041000000c003300210000000000113019f00000b53011001c700000aff052001970000800d02000039000000030300003900000bbf0400004100000011060000292bdc2bd20000040f0000000100200190000018050000c13d00001d630000013d0000000502000039000000000102041a00000bb40110019700000bb501100167000000000012041b00000b5d01000041000000000010044300000000010004120000000400100443000000c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000400b00043d00000b900100004100000000001b04350000000401b0003900000000030004100000000000310435000000000100041400000aff02200197000000040020008c001100000002001d00001c310000c13d0000000003000031000000200030008c0000002004000039000000000403401900001c5e0000013d00000af800b0009c00000af80300004100000000030b4019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0e011001c700100000000b001d2bdc2bd70000040f000000100b000029000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000000057b001900001c4c0000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b00001c480000c13d000000000006004b00001c590000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f0002000000010355000000010020019000001e600000613d00000011020000290000001f01400039000000600410018f0000000001b40019000000000041004b0000000004000039000000010400403900000afb0010009c00001d6b0000213d000000010040019000001d6b0000c13d000000400010043f000000200030008c00001d630000413d00000000010b0433001000000001001d00000b8d0100004100000000001004430000000400200443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b000000000001004b000000110200002900001d630000613d000000400400043d00000bb60100004100000000001404350000000401400039000000100300002900000000003104350000000001000414000000040020008c00001c970000613d00000af80040009c00000af8030000410000000003044019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0e011001c7001100000004001d2bdc2bd20000040f0000001104000029000000600310027000000af80030019d0002000000010355000000010020019000001e6c0000613d00000afb0040009c00001d6b0000213d000000400040043f00000b5d01000041000000000010044300000000010004120000000400100443000001a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b001100000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000180010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000001d620000613d000000110200002900110afe0020019c000000000101043b001000000001001d00001d380000613d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d000000000201043b000000110020006c00001d830000413d000f00000002001d0000001001000029000000040010008c00001cdc0000613d000080090200003900000ba7010000410000001103000029000000100400002900000000050000192bdc2bd20000040f000000600210027000000af80020019d000200000001035500000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d000000000101043b0000000f0010006c00001d380000c13d000000400100043d00000bb70010009c00001d6b0000213d000000240210003900000ba9030000410000000000320435000000440210003900000000030004140000006004000039000000000042043500000baa020000410000000000210435000000640210003900000000000204350000000402100039000000000002043500000af80010009c00000af801008041000000400110021000000af80030009c00000af803008041000000c002300210000000000121019f00000bab011001c700008006020000392bdc2bd20000040f000000010020019000001e8b0000613d000000000201043b000000000002004b00001e900000613d000000100100002900000aff05100197000000000050043f000000000100041400000aff04200197000000040040008c00001d240000613d00000af80010009c00000af801008041000000c00110021000000b04011001c700008009020000390000001103000029001000000004001d0000001004000029000f00000005001d00000000050000192bdc2bd20000040f00000010040000290000000f05000029000000600310027000000af80030019d0002000000010355000000010020019000001d630000613d000000400100043d000000200210003900000000004204350000001102000029000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b53011001c70000800d02000039000000020300003900000bac040000412bdc2bd20000040f000000010020019000001d630000613d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f000000010020019000001d630000613d000000000101043b000000000101041a001100000001001d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000001d620000613d0000001102000029000000700220021000000b4d02200197000000000101043b00000afe01100197000000000121019f0000000802000039000000000302041a00000b4e03300197000000000131019f000000000012041b0000000001000414000017fb0000013d000000000001042f000000000100001900002bde0001043000000b9201000041000000000010043f0000001101000039000000040010043f00000b0e0100004100002bde0001043000000b9201000041000000000010043f0000004101000039000000040010043f00000b0e0100004100002bde0001043000000b9901000041000000000010043f00000b0c0100004100002bde0001043000000b9b01000041000000000010043f00000b0c0100004100002bde0001043000000bc401000041000000000010043f00000b0c0100004100002bde0001043000000b9201000041000000000010043f0000001201000039000000040010043f00000b0e0100004100002bde0001043000000bc001000041000000000010043f00000bc10100004100002bde0001043000000b9e01000041000000000010043f00000b0c0100004100002bde000104300002000000010355000000600210027000000af80020019d00000af80220019700001d920000013d0000000201000367000000000200003100000ba0052001980000001f0620018f000000400300043d000000000453001900001def0000613d000000000701034f0000000008030019000000007907043c0000000008980436000000000048004b00001d990000c13d00001def0000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001da60000c13d00001e780000013d0000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001db20000c13d00001e780000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001dbf0000c13d00001e780000013d0000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001dcb0000c13d00001e780000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001dd80000c13d00001e780000013d0002000000010355000000600210027000000af80020019d00000af80220019700001de40000013d0000000201000367000000000200003100000ba0052001980000001f0620018f000000400300043d000000000453001900001def0000613d000000000701034f0000000008030019000000007907043c0000000008980436000000000048004b00001deb0000c13d000000000006004b00001dfc0000613d000000000151034f0000000305600210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f000000000014043500000af80020009c00000af802008041000000600120021000000af80030009c00000af8030080410000004002300210000000000112019f00002bde000104300000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001e0b0000c13d00001e780000013d0000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001e170000c13d00001e780000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001e240000c13d00001e780000013d0000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001e300000c13d00001e780000013d0000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001e3c0000c13d00001e780000013d0000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001e480000c13d00001e780000013d0002000000010355000000600210027000000af80020019d00000af80220019700001e540000013d0000000201000367000000000200003100000ba0052001980000001f0620018f000000400300043d000000000453001900001def0000613d000000000701034f0000000008030019000000007907043c0000000008980436000000000048004b00001e5b0000c13d00001def0000013d0000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001e670000c13d00001e780000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d000000000462001900001e780000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b00001e740000c13d000000000005004b00001e850000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f0000000000140435000000600130021000000af80020009c00000af8020080410000004002200210000000000112019f00002bde000104300002000000010355000000600210027000000af80020019d00000af80220019700001e920000013d0000000201000367000000000200003100000ba0052001980000001f0620018f000000400300043d000000000453001900001def0000613d000000000701034f0000000008030019000000007907043c0000000008980436000000000048004b00001e990000c13d00001def0000013d0013000000000002000c00000003001d000f00000001001d00110afe0020019c000028e00000613d0000000501000039000000000101041a001000000001001d00000b8a0110019700000b8b0010009c000028e80000613d0000000f0100002900000aff01100197000e00000001001d000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000000100200190000028d20000613d000000100200002900000b9604200197000000000101043b000000000301041a00000afe02300197000000700330027000000afe0730019800001ecc0000613d000000000004004b000000110500002900001ed00000c13d0000000806000039000000000806041a000000000357004b00001ed60000813d000000000575004900000afe0050009c000000000300001900001eda0000a13d000028d40000013d001200000000001d0000000003000019000000110500002900001ef40000013d000000000227001900000afe0020009c001200000000001d000000000300001900001ef40000a13d000028d40000013d00000afe0030009c00000000070500190000000005000019000028d40000213d00000afe09800197000000000a9700a9000000700b80027000000afe0bb00197000000000bb70019000000000aba00d900120000000a001d00000afe00a0009c000028e00000213d000000120990006a00000b4f0090009c000028d40000813d000000000a89019f000000700aa0027000000afe0aa0019700000000077a001900000afe0070009c000028d40000213d00000b4e08800197000000700770021000000b4d07700197000000000778019f000000000797019f000000000076041b000000000005004b00001f180000613d000000000004004b00000008040000390000000704006039000000000252004b000028f00000413d00000afe0020009c000028d40000213d000000000604041a00000afe0760019700000000085700a9000000700960027000000afe09900197000000000959001900000000089800d900000afe0080009c000028e00000213d0000001209800029001200000009001d00000afe0090009c000028d40000213d000000000787004900000afe0070009c000028d40000213d00000b5406600197000000000667019f000000000064041b000000700760027000000afe07700197000000000557001900000afe0050009c000028d40000213d00000ba506600197000000700550021000000b4d05500197000000000565019f000000000054041b000000000401041a00000b4e04400197000000000224019f000000700330021000000b4d03300197000000000223019f000000000021041b0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000000100200190000028d20000613d000000000101043b000000000201041a00000afe03200197000000110330002900000afe0030009c000028d40000213d00000b5402200197000000000223019f000000000021041b0000000e05000029001300000005001d000000400100043d0000001102000029000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b04011001c70000800d02000039000000030300003900000b550400004100000000060004102bdc2bd20000040f0000000100200190000028d20000613d00000b5d01000041000000000010044300000000010004120000000400100443000001c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000e00000001001d00000b5d01000041000000000010044300000000010004120000000400100443000000e0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d0000000e020000290000ffff0220018f00000012022000b900000b9702200197000e271000200122000000000101043b000d00000001001d000027100020008c00001fec0000413d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b0000000e0020006c000028e40000413d000b00000002001d0000000d01000029000000040010008c00001f900000613d000080090200003900000ba7010000410000000e030000290000000d0400002900000000050000192bdc2bd20000040f000000600210027000000af80020019d000200000001035500000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b0000000b0010006c00001fec0000c13d000000400100043d00000ba80010009c000028da0000813d000000240210003900000ba9030000410000000000320435000000440210003900000000030004140000006004000039000000000042043500000baa020000410000000000210435000000640210003900000000000204350000000402100039000000000002043500000af80010009c00000af801008041000000400110021000000af80030009c00000af803008041000000c002300210000000000121019f00000bab011001c700008006020000392bdc2bd20000040f0000000100200190000028f80000613d000000000201043b000000000002004b000028fd0000613d0000000d0100002900000aff05100197000000000050043f000000000100041400000aff04200197000000040040008c00001fd80000613d00000af80010009c00000af801008041000000c00110021000000b04011001c700008009020000390000000e03000029000d00000004001d0000000d04000029000b00000005001d00000000050000192bdc2bd20000040f0000000d040000290000000b05000029000000600310027000000af80030019d00020000000103550000000100200190000028d20000613d000000400100043d000000200210003900000000004204350000000e02000029000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b53011001c70000800d02000039000000020300003900000bac040000412bdc2bd20000040f0000000100200190000028d20000613d000000100100002900000b95001001980000000e0200002900000012052000690000202f0000c13d000e00000005001d0000000601000039000000000101041a001200000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000160010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000d00000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000140010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d00000012040000290000000d0240006900000afe02200197000000000101043b0000ffff0110018f0000000e0500002900000000015100a900000b9701100197000027100310011a000000000032004b0000000001000019000020270000213d0000000503000039000000000103041a00000b95011001c7000000000013041b00000001010000390000000003020019000000000243001900000afe0220019700000b5404400197000000000242019f0000000604000039000000000024041b0000000005350049000020300000013d0000000101000039000e00000005001d00120afe0050019b000000100200002900000bc50220019700000b960020009c0000235d0000c13d000000000001004b0000235d0000613d0000000502000039000000000102041a00000ba4011001c7000000000012041b00000b5d0100004100000000001004430000000001000412000000040010044300000060010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b00000b8d02000041000000000020044300000aff01100197001000000001001d0000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000000000001004b000028d20000613d000000400400043d00000bad01000041000000000014043500000000010004140000001002000029000000040020008c000020750000613d00000af80040009c00000af8030000410000000003044019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0c011001c7001000000004001d2bdc2bd20000040f0000001004000029000000600310027000000af80030019d000200000001035500000001002001900000290b0000613d00000afb0040009c000028da0000213d000000400040043f00000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000000000200041000000aff02200198000028f40000613d00100aff0010019c000028f40000613d000d00000002001d000000000020043f0000000a01000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000001002001900000001003000029000028d20000613d000000000101043b000000000030043f000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f00000010060000290000000100200190000028d20000613d000000000101043b000000010200008a000000000021041b000000400100043d000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b04011001c70000800d02000039000000030300003900000b9d040000410000000d050000292bdc2bd20000040f0000000100200190000028d20000613d00000b5d01000041000000000010044300000000010004120000000400100443000000c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b001000000001001d000000400200043d00000bae01000041000d00000002001d000000000012043500000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000100200002900000aff02200197000000000101043b0000000d0b0000290000002403b00039000000010400008a00000000004304350000000403b0003900000aff0110019700000000001304350000000001000414000000040020008c001000000002001d000020f40000c13d0000000003000031000000200030008c000000200400003900000000040340190000211f0000013d00000af800b0009c00000af80300004100000000030b4019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b83011001c72bdc2bd20000040f0000000d0b000029000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000000057b00190000210e0000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b0000210a0000c13d000000000006004b0000211b0000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000029180000613d0000001f01400039000000600210018f0000000001b20019000000000021004b0000000002000039000000010200403900000afb0010009c000028da0000213d0000000100200190000028da0000c13d000000400010043f000000200030008c000028d20000413d00000000010b0433000000000001004b0000000002000039000000010200c039000000000021004b000028d20000c13d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000000100200190000028d20000613d000000000101043b000000000101041a000b00000001001d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000d00000001001d00000b5d01000041000000000010044300000000010004120000000400100443000001a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b00000afe011001970000000d0210006b000028d40000413d000000120120006c000028d40000413d000a00000002001d000d00000001001d00000b8d01000041000000000010044300000010010000290000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000000000001004b000028d20000613d000000400200043d00000baf01000041000000000012043500000000010004140000001004000029000000040040008c000021980000613d00000af80020009c000900000002001d00000af802008041000000400220021000000af80010009c00000af801008041000000c001100210000000000121019f0000000a03000029000000120030006c0000218d0000c13d00000b0c011001c70000000002040019000021910000013d00000bb0011001c700008009020000390000000d0300002900000000050000192bdc2bd20000040f0002000000010355000000600310027000000af80030019d00000001002001900000000902000029000029240000613d00000afb0020009c000028da0000213d000000400020043f00000b5d0100004100000000001004430000000001000412000000040010044300000100010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b00000afe011001970000000d02000029000000000021001a000028d40000413d000000000221001a000021fb0000613d0000000b01000029000b0afe0010019c000021fb0000613d00000b120020009c000000c0040000390000006001000039000021c10000413d00000ba2032001290000006001000039000000000001004b000028d40000613d000000010110008a0000000104100210000000010540020f000000000053004b000021ba0000a13d00000000024201cf0000000b022000fa00000b130020009c00000000030000390000008003002039000000000432022f00000b140040009c00000040033021bf000000000432022f00000b010040009c00000020033021bf000000000432022f00000b000040009c00000010033021bf000000000432022f00000b150440009a0000000103300270000000b50330020f00000000034300a90000001203300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000432c0d9000000000400601900000000033400190000000103300272000000000232c0d90000000002006019000000000032004b000000010330408a000000600110008900000000021301cf000000ff0010008c000000000200201900000aff0020009c0000238e0000a13d0000000502000039000000000102041a00000bb40110019700000bb501100167000000000012041b00000b5d01000041000000000010044300000000010004120000000400100443000000c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b000000400b00043d00000b900100004100000000001b04350000000401b0003900000000030004100000000000310435000000000100041400000aff02200197000000040020008c001000000002001d000022200000c13d0000000003000031000000200030008c000000200400003900000000040340190000224d0000013d00000af800b0009c00000af80300004100000000030b4019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0e011001c7000d0000000b001d2bdc2bd70000040f0000000d0b000029000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000000057b00190000223b0000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b000022370000c13d000000000006004b000022480000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000029310000613d00000010020000290000001f01400039000000600410018f0000000001b40019000000000041004b0000000004000039000000010400403900000afb0010009c000028da0000213d0000000100400190000028da0000c13d000000400010043f000000200030008c000028d20000413d00000000010b0433000d00000001001d00000b8d0100004100000000001004430000000400200443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000000000001004b0000001002000029000028d20000613d000000400400043d00000bb601000041000000000014043500000004014000390000000d0300002900000000003104350000000001000414000000040020008c000022860000613d00000af80040009c00000af8030000410000000003044019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0e011001c7001000000004001d2bdc2bd20000040f0000001004000029000000600310027000000af80030019d000200000001035500000001002001900000293d0000613d00000afb0040009c000028da0000213d000000400040043f00000b5d01000041000000000010044300000000010004120000000400100443000001a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b001000000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000180010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000100200002900100afe0020019c000000000101043b000d00000001001d000023270000613d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b000000100020006c000028e40000413d000b00000002001d0000000d01000029000000040010008c000022cb0000613d000080090200003900000ba70100004100000010030000290000000d0400002900000000050000192bdc2bd20000040f000000600210027000000af80020019d000200000001035500000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b0000000b0010006c000023270000c13d000000400100043d00000bb70010009c000028da0000213d000000240210003900000ba9030000410000000000320435000000440210003900000000030004140000006004000039000000000042043500000baa020000410000000000210435000000640210003900000000000204350000000402100039000000000002043500000af80010009c00000af801008041000000400110021000000af80030009c00000af803008041000000c002300210000000000121019f00000bab011001c700008006020000392bdc2bd20000040f00000001002001900000294a0000613d000000000201043b000000000002004b0000294f0000613d0000000d0100002900000aff05100197000000000050043f000000000100041400000aff04200197000000040040008c000023130000613d00000af80010009c00000af801008041000000c00110021000000b04011001c700008009020000390000001003000029000d00000004001d0000000d04000029000b00000005001d00000000050000192bdc2bd20000040f0000000d040000290000000b05000029000000600310027000000af80030019d00020000000103550000000100200190000028d20000613d000000400100043d000000200210003900000000004204350000001002000029000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b53011001c70000800d02000039000000020300003900000bac040000412bdc2bd20000040f0000000100200190000028d20000613d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000000100200190000028d20000613d000000000101043b000000000101041a001000000001001d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000000120010006c000028d40000413d0000000e0110006a00000afe011001970000001002000029000000700220021000000b4d02200197000000000121019f0000000802000039000000000302041a00000b4e03300197000000000131019f000000000012041b000000000100041400000af80010009c00000af801008041000000c00110021000000bb8011001c70000800d02000039000000010300003900000bb9040000412bdc2bd20000040f0000000100200190000028d20000613d0000000c0100002900000afe011001970000001203000029000000000013004b000028ec0000413d00000000010004140000000f02000029000000040020008c000023780000613d00000af80010009c00000af801008041000000c001100210000000000003004b000023700000613d00000bb8011001c700008009020000390000000f040000290000000005000019000023710000013d0000000f020000292bdc2bd20000040f0002000000010355000000600110027000000af80010019d00000001002001900000001203000029000028e40000613d000000400100043d000000200210003900000000003204350000001102000029000000000021043500000af80010009c00000af80100804100000040011002100000001305000029000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b53011001c70000800d02000039000000020300003900000bc6040000412bdc2bd20000040f0000000100200190000028d20000613d000000000001042d000800000002001d000000400200043d00000bb101000041000900000002001d0000000001120436000700000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b000000000100041400000aff02200197000000040020008c000023ae0000c13d0000000003000031000000e00030008c000000e0040000390000000004034019000000090b000029000023d90000013d000000090300002900000af80030009c00000af803008041000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0c011001c72bdc2bd70000040f000000090b000029000000600310027000000af803300197000000e00030008c000000e00400003900000000040340190000001f0640018f000000e00740019000000000057b0019000023c80000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b000023c40000c13d000000000006004b000023d50000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000029710000613d0000001f01400039000001e00110018f0000000005b10019000000000015004b0000000001000039000000010100403900000afb0050009c000028da0000213d0000000100100190000028da0000c13d000000400050043f000000e00030008c000028d20000413d00000000010b043300000aff0010009c000028d20000213d0000000702000029000000000202043300000b0f0020019800000b1003000041000000000300601900000b1104200197000000000343019f000000000032004b000028d20000c13d0000004002b0003900000000020204330000ffff0020008c000028d20000213d0000006002b0003900000000020204330000ffff0020008c000028d20000213d0000008002b0003900000000020204330000ffff0020008c000028d20000213d000000a002b000390000000002020433000000ff0020008c000028d20000213d000000c002b000390000000002020433000000000002004b0000000003000039000000010300c039000000000032004b000028d20000c13d0000000802000029000000000021004b000000a003000039000024e10000613d000000840150003900000000003104350000006401500039000000000021043500000044015000390000000d02000029000000000021043500000bb2010000410000000000150435000000040150003900000000020004100000000000210435000000a40150003900000000000104350000002401500039000000000001043500000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c70000800502000039000a00000005001d2bdc2bd70000040f0000000100200190000028d10000613d0000000a0b000029000000000201043b000000000100041400000aff02200197000000040020008c000024380000c13d0000000003000031000000400030008c00000040040000390000000004034019000024630000013d00000af800b0009c00000af80300004100000000030b4019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000bb3011001c72bdc2bd20000040f0000000a0b000029000000600310027000000af803300197000000400030008c000000400400003900000000040340190000001f0640018f000000600740019000000000057b0019000024520000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b0000244e0000c13d000000000006004b0000245f0000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000029a20000613d0000001f01400039000000e00110018f0000000002b1001900000afb0020009c000028da0000213d000000400020043f000000400030008c000028d20000413d00000bb101000041000a00000002001d0000000001120436000900000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b000000000100041400000aff02200197000000040020008c000024890000c13d0000000003000031000000e00030008c000000e00400003900000000040340190000000a0b000029000024b40000013d0000000a0300002900000af80030009c00000af803008041000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0c011001c72bdc2bd70000040f0000000a0b000029000000600310027000000af803300197000000e00030008c000000e00400003900000000040340190000001f0640018f000000e00740019000000000057b0019000024a30000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b0000249f0000c13d000000000006004b000024b00000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000029ae0000613d0000001f01400039000001e00110018f0000000005b1001900000afb0050009c000028da0000213d000000400050043f000000e00030008c000028d20000413d00000000010b043300000aff0010009c000028d20000213d0000000902000029000000000202043300000b0f0020019800000b1003000041000000000300601900000b1104200197000000000343019f000000000032004b000028d20000c13d0000004002b0003900000000020204330000ffff0020008c000028d20000213d0000006002b0003900000000020204330000ffff0020008c000028d20000213d0000008002b0003900000000020204330000ffff0020008c000028d20000213d000000a002b000390000000002020433000000ff0020008c000028d20000213d000000c002b000390000000002020433000000000002004b0000000003000039000000010300c039000000000032004b000028d20000c13d000000080010006b000027780000c13d00000bba0050009c000028da0000213d0000016001500039000000400010043f000000000100041000000000021504360000001001000029000e00000002001d000000000012043500000b5d01000041000000000010044300000000010004120000000400100443000000a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c70000800502000039000a00000005001d2bdc2bd70000040f0000000100200190000028d10000613d0000000a020000290000004002200039000000000101043b00000b0001100197000900000002001d000000000012043500000b5d01000041000000000010044300000000010004120000000400100443000002a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b00000b110210019700000b0f0010019800000b10010000410000000001006019000000000121019f0000000a020000290000006002200039000800000002001d000000000012043500000b5d01000041000000000010044300000000010004120000000400100443000002c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b0000000a0300002900000120043000390000000002000410000700000004001d0000000000240435000000c0043000390000000d02000029000600000004001d0000000000240435000000a0043000390000000b02000029000400000004001d000000000024043500000b110210019700000b0f0010019800000b10010000410000000001006019000000000121019f0000008002300039000200000002001d00000000001204350000010001300039000500000001001d0000000000010435000000e001300039000300000001001d000000000001043500000b50010000410000000000100443000000000100041400000af80010009c00000af801008041000000c00110021000000b51011001c70000800b020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b0000000a030000290000014001300039000000000021043500000bbb02000041000000400400043d0000000002240436000100000002001d000000000203043300000aff02200197000000040340003900000000002304350000000e02000029000000000202043300000aff02200197000000240340003900000000002304350000000902000029000000000202043300000b0002200197000000440340003900000000002304350000000802000029000000000202043300000b110320019700000b0f0020019800000b10020000410000000002006019000000000232019f000000640340003900000000002304350000000202000029000000000202043300000b110320019700000b0f0020019800000b10020000410000000002006019000000000232019f0000008403400039000000000023043500000004020000290000000002020433000000a403400039000000000023043500000006020000290000000002020433000000c403400039000000000023043500000003020000290000000002020433000000e403400039000000000023043500000005020000290000000002020433000001040340003900000000002304350000000702000029000000000202043300000aff02200197000001240340003900000000002304350000000001010433001000000004001d0000014402400039000000000012043500000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b000000000100041400000aff02200197000000040020008c000025aa0000c13d0000000003000031000000800030008c00000080040000390000000004034019000000100b000029000025d50000013d000000100300002900000af80030009c00000af803008041000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000bbc011001c72bdc2bd20000040f000000100b000029000000600310027000000af803300197000000800030008c000000800400003900000000040340190000001f0640018f000000e00740019000000000057b0019000025c40000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b000025c00000c13d000000000006004b000025d10000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f000200000001035500000001002001900000297d0000613d0000001f01400039000001e00210018f0000000001b20019000000000021004b0000000002000039000000010200403900000afb0010009c000028da0000213d0000000100200190000028da0000c13d000000400010043f000000800030008c000028d20000413d0000000101000029000000000101043300000b190010009c000028d20000213d00000000010b0433001000000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000080010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000e00000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b00000b8d02000041000000000020044300000aff011001970000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000000000001004b000028d20000613d0000000e0100002900000aff03100197000000400400043d00000024014000390000001002000029000000000021043500000bae010000410000000000140435000e00000004001d0000000401400039000a00000003001d000000000031043500000b5d0100004100000000001004430000000001000412000000040010044300000040010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b000000000100041400000aff02200197000000040020008c000026480000613d0000000e0300002900000af80030009c00000af803008041000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b83011001c72bdc2bd20000040f000000600310027000000af80030019d00020000000103550000000100200190000029890000613d0000000e0100002900000afb0010009c000028da0000213d000000400010043f00000b5d0100004100000000001004430000000001000412000000040010044300000180010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b000000400400043d00000bbd010000410000000000140435000000040140003900000010030000290000000000310435000e00000004001d0000002401400039000900000002001d00000aff02200197000800000002001d000000000021043500000b5d01000041000000000010044300000000010004120000000400100443000000e0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d0000000e0b0000290000004402b00039000000000101043b00000aff01100197000000000012043500000000010004140000000a02000029000000040020008c000026850000c13d0000000003000031000000200030008c00000020040000390000000004034019000026b00000013d00000af800b0009c00000af80300004100000000030b4019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b4c011001c72bdc2bd20000040f0000000e0b000029000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000000057b00190000269f0000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b0000269b0000c13d000000000006004b000026ac0000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000029960000613d0000001f01400039000000600210018f0000000001b20019000000000021004b0000000002000039000000010200403900000afb0010009c000028da0000213d0000000100200190000028da0000c13d000000400010043f000000200030008c000028d20000413d00000000010b0433001000000001001d00000aff0010009c000028d20000213d0000000502000039000000000102041a00000bbe0110019700000010011001af000000000012041b00000b5d01000041000000000010044300000000010004120000000400100443000001a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000e0afe0010019c0000274e0000613d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b0000000e0020006c000028e40000413d000a00000002001d0000000904000029000000040040008c000026f40000613d000080090200003900000ba7010000410000000e0300002900000000050000192bdc2bd20000040f000000600210027000000af80020019d000200000001035500000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b0000000a0010006c0000274e0000c13d000000400100043d00000bb70010009c000028da0000213d000000240210003900000ba9030000410000000000320435000000440210003900000000030004140000006004000039000000000042043500000baa020000410000000000210435000000640210003900000000000204350000000402100039000000000002043500000af80010009c00000af801008041000000400110021000000af80030009c00000af803008041000000c002300210000000000121019f00000bab011001c700008006020000392bdc2bd20000040f0000000100200190000029ba0000613d000000000201043b000000000002004b000029bf0000613d0000000801000029000000000010043f000000000100041400000aff04200197000000040040008c000027390000613d00000af80010009c00000af801008041000000c00110021000000b04011001c700008009020000390000000e03000029000a00000004001d0000000a0400002900000000050000192bdc2bd20000040f0000000a04000029000000600310027000000af80030019d00020000000103550000000100200190000028d20000613d000000400100043d000000200210003900000000004204350000000e02000029000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b53011001c70000800d02000039000000020300003900000bac0400004100000008050000292bdc2bd20000040f0000000100200190000028d20000613d000000400300043d00000020013000390000000b0200002900000000002104350000000d01000029000e00000003001d000000000013043500000b5d0100004100000000001004430000000001000412000000040010044300000020010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b0000000e0100002900000af80010009c00000af8010080410000004001100210000000000300041400000af80030009c00000af803008041000000c003300210000000000113019f00000b53011001c700000aff052001970000800d02000039000000030300003900000bbf0400004100000010060000292bdc2bd20000040f00000001002001900000235d0000c13d000028d20000013d0000000502000039000000000102041a00000bb40110019700000bb501100167000000000012041b00000b5d01000041000000000010044300000000010004120000000400100443000000c0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b000000400b00043d00000b900100004100000000001b04350000000401b0003900000000030004100000000000310435000000000100041400000aff02200197000000040020008c001000000002001d0000279d0000c13d0000000003000031000000200030008c00000020040000390000000004034019000027ca0000013d00000af800b0009c00000af80300004100000000030b4019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0e011001c7000d0000000b001d2bdc2bd70000040f0000000d0b000029000000600310027000000af803300197000000200030008c000000200400003900000000040340190000001f0640018f000000200740019000000000057b0019000027b80000613d000000000801034f00000000090b0019000000008a08043c0000000009a90436000000000059004b000027b40000c13d000000000006004b000027c50000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f0000000000650435000000000003001f00020000000103550000000100200190000029cd0000613d00000010020000290000001f01400039000000600410018f0000000001b40019000000000041004b0000000004000039000000010400403900000afb0010009c000028da0000213d0000000100400190000028da0000c13d000000400010043f000000200030008c000028d20000413d00000000010b0433000d00000001001d00000b8d0100004100000000001004430000000400200443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c700008002020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000000000001004b0000001002000029000028d20000613d000000400400043d00000bb601000041000000000014043500000004014000390000000d0300002900000000003104350000000001000414000000040020008c000028030000613d00000af80040009c00000af8030000410000000003044019000000400330021000000af80010009c00000af801008041000000c001100210000000000131019f00000b0e011001c7001000000004001d2bdc2bd20000040f0000001004000029000000600310027000000af80030019d00020000000103550000000100200190000029d90000613d00000afb0040009c000028da0000213d000000400040043f00000b5d01000041000000000010044300000000010004120000000400100443000001a0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b001000000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000180010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f0000000100200190000028d10000613d000000100200002900100afe0020019c000000000101043b000d00000001001d000028a40000613d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000201043b000000100020006c000028e40000413d000b00000002001d0000000d01000029000000040010008c000028480000613d000080090200003900000ba70100004100000010030000290000000d0400002900000000050000192bdc2bd20000040f000000600210027000000af80020019d000200000001035500000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b0000000b0010006c000028a40000c13d000000400100043d00000bb70010009c000028da0000213d000000240210003900000ba9030000410000000000320435000000440210003900000000030004140000006004000039000000000042043500000baa020000410000000000210435000000640210003900000000000204350000000402100039000000000002043500000af80010009c00000af801008041000000400110021000000af80030009c00000af803008041000000c002300210000000000121019f00000bab011001c700008006020000392bdc2bd20000040f0000000100200190000029f80000613d000000000201043b000000000002004b000029fd0000613d0000000d0100002900000aff05100197000000000050043f000000000100041400000aff04200197000000040040008c000028900000613d00000af80010009c00000af801008041000000c00110021000000b04011001c700008009020000390000001003000029000d00000004001d0000000d04000029000b00000005001d00000000050000192bdc2bd20000040f0000000d040000290000000b05000029000000600310027000000af80030019d00020000000103550000000100200190000028d20000613d000000400100043d000000200210003900000000004204350000001002000029000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b53011001c70000800d02000039000000020300003900000bac040000412bdc2bd20000040f0000000100200190000028d20000613d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f0000000100200190000028d20000613d000000000101043b000000000101041a001000000001001d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f0000000100200190000028d10000613d000000000101043b000000120010006c000028d40000413d0000000e0110006a00000afe011001970000001002000029000000700220021000000b4d02200197000000000121019f0000000802000039000000000302041a00000b4e03300197000000000131019f000000000012041b0000000001000414000023530000013d000000000001042f000000000100001900002bde0001043000000b9201000041000000000010043f0000001101000039000000040010043f00000b0e0100004100002bde0001043000000b9201000041000000000010043f0000004101000039000000040010043f00000b0e0100004100002bde0001043000000b9901000041000000000010043f00000b0c0100004100002bde0001043000000bc001000041000000000010043f00000bc10100004100002bde0001043000000b9b01000041000000000010043f00000b0c0100004100002bde0001043000000bc401000041000000000010043f00000b0c0100004100002bde0001043000000b9a01000041000000000010043f00000b0c0100004100002bde0001043000000b9e01000041000000000010043f00000b0c0100004100002bde000104300002000000010355000000600210027000000af80020019d00000af802200197000028ff0000013d0000000201000367000000000200003100000ba0052001980000001f0620018f000000400300043d00000000045300190000295c0000613d000000000701034f0000000008030019000000007907043c0000000008980436000000000048004b000029060000c13d0000295c0000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000029130000c13d000029e50000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b0000291f0000c13d000029e50000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b0000292c0000c13d000029e50000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000029380000c13d000029e50000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000029450000c13d000029e50000013d0002000000010355000000600210027000000af80020019d00000af802200197000029510000013d0000000201000367000000000200003100000ba0052001980000001f0620018f000000400300043d00000000045300190000295c0000613d000000000701034f0000000008030019000000007907043c0000000008980436000000000048004b000029580000c13d000000000006004b000029690000613d000000000151034f0000000305600210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f000000000014043500000af80020009c00000af802008041000000600120021000000af80030009c00000af8030080410000004002300210000000000112019f00002bde000104300000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000029780000c13d000029e50000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000029840000c13d000029e50000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000029910000c13d000029e50000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b0000299d0000c13d000029e50000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000029a90000c13d000029e50000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000029b50000c13d000029e50000013d0002000000010355000000600210027000000af80020019d00000af802200197000029c10000013d0000000201000367000000000200003100000ba0052001980000001f0620018f000000400300043d00000000045300190000295c0000613d000000000701034f0000000008030019000000007907043c0000000008980436000000000048004b000029c80000c13d0000295c0000013d0000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000029d40000c13d000029e50000013d00000af8033001970000001f0530018f00000afa06300198000000400200043d0000000004620019000029e50000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000029e10000c13d000000000005004b000029f20000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f0000000000140435000000600130021000000af80020009c00000af8020080410000004002200210000000000112019f00002bde000104300002000000010355000000600210027000000af80020019d00000af802200197000029ff0000013d0000000201000367000000000200003100000ba0052001980000001f0620018f000000400300043d00000000045300190000295c0000613d000000000701034f0000000008030019000000007907043c0000000008980436000000000048004b00002a060000c13d0000295c0000013d0006000000000002000500000001001d00000b5d0100004100000000001004430000000001000412000000040010044300000220010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000002ae20000613d000000000101043b000600000001001d00000b50010000410000000000100443000000000100041400000af80010009c00000af801008041000000c00110021000000b51011001c70000800b020000392bdc2bd70000040f000000010020019000002ae20000613d000000000201043b000000060100002900000b0101100197000000000012004b00002ac60000813d000600000002001d0000000001000410000000000010043f0000000901000039000000200010043f000000000100041400000af80010009c00000af801008041000000c00110021000000b53011001c700008010020000392bdc2bd70000040f000000010020019000002ae30000613d000000000101043b000000000101041a000400000001001d00000b5d01000041000000000010044300000000010004120000000400100443000001e0010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000002ae20000613d000000000101043b00000b0101100197000600060010007300002ada0000413d00000b5d0100004100000000001004430000000001000412000000040010044300000200010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000002ae20000613d000000060200002900010afe0020019b000000000101043b00000afe0110019700000001011000b9000300000001001d00000afe0010009c00002ada0000213d00000b5d0100004100000000001004430000000001000412000000040010044300000240010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000002ae20000613d000000000101043b00020b010010019c00002ae50000613d00000b5d010000410000000000100443000000000100041200000004001004430000002400000443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000002ae20000613d000000030200002900000b190220019700000002022000fa000000000101043b00000afe01100197000000000321004900000afe0030009c00002ada0000213d000000050100002900000afe04100197000000040110006900000afe01100197000000000013004b00002ac80000a13d000300000004001d000400000001001d000500000003001d00000b5d0100004100000000001004430000000001000412000000040010044300000260010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000002ae20000613d000000000101043b00000b0102100197000000010020006b00002aca0000813d000200000002001d00000b5d0100004100000000001004430000000001000412000000040010044300000280010000390000002400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b5e011001c700008005020000392bdc2bd70000040f000000010020019000002ae20000613d0000000203000029000000060230006a000000000101043b000000ff0110018f00000000012100a900000afe0110019700000000013100d9000000020110003900002acb0000013d0000000501000029000000000001042d0000000001040019000000000001042d0000000201000039000000050500002900000004060000290000000304000029000000000200001900002ad40000013d00000000045300d90000000102200039000000000012004b00002ae00000813d00000000036400a9000000000004004b00002ad00000613d00000000044300d9000000000064004b00002ad00000613d00000b9201000041000000000010043f0000001101000039000000040010043f00000b0e0100004100002bde0001043000000afe01400197000000000001042d000000000001042f000000000100001900002bde0001043000000b9201000041000000000010043f0000001201000039000000040010043f00000b0e0100004100002bde000104300003000000000002000200000001001d000300000002001d000000000002004b00002b690000613d00000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000002b6a0000613d000000000201043b000000030020006c00002b6b0000413d000100000002001d0000000201000029000000040010008c00002b0d0000613d000080090200003900000ba7010000410000000303000029000000020400002900000000050000192bdc2bd20000040f000000600210027000000af80020019d000200000001035500000b9101000041000000000010044300000000010004100000000400100443000000000100041400000af80010009c00000af801008041000000c00110021000000b8e011001c70000800a020000392bdc2bd70000040f000000010020019000002b6a0000613d000000000101043b000000010010006c00002b690000c13d000000400100043d00000ba80010009c00002b710000813d000000240210003900000ba9030000410000000000320435000000440210003900000000030004140000006004000039000000000042043500000baa020000410000000000210435000000640210003900000000000204350000000402100039000000000002043500000af80010009c00000af801008041000000400110021000000af80030009c00000af803008041000000c002300210000000000121019f00000bab011001c700008006020000392bdc2bd20000040f000000010020019000002b770000613d000000000201043b000000000002004b00002b7c0000613d000000020100002900000aff05100197000000000050043f000000000100041400000aff04200197000000040040008c00002b550000613d00000af80010009c00000af801008041000000c00110021000000b04011001c700008009020000390000000303000029000200000004001d0000000204000029000100000005001d00000000050000192bdc2bd20000040f00000002040000290000000105000029000000600310027000000af80030019d0002000000010355000000010020019000002b6f0000613d000000400100043d000000200210003900000000004204350000000302000029000000000021043500000af80010009c00000af8010080410000004001100210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000b53011001c70000800d02000039000000020300003900000bac040000412bdc2bd20000040f000000010020019000002b6f0000613d000000000001042d000000000001042f00000bc001000041000000000010043f00000bc10100004100002bde00010430000000000100001900002bde0001043000000b9201000041000000000010043f0000004101000039000000040010043f00000b0e0100004100002bde000104300002000000010355000000600210027000000af80020019d00000af80220019700002b7e0000013d0000000201000367000000000200003100000ba0052001980000001f0620018f000000400300043d000000000453001900002b890000613d000000000701034f0000000008030019000000007907043c0000000008980436000000000048004b00002b850000c13d000000000006004b00002b960000613d000000000151034f0000000305600210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f000000000014043500000af80030009c00000af803008041000000400130021000000af80020009c00000af8020080410000006002200210000000000112019f00002bde00010430000000000001042f00000af80010009c00000af801008041000000400110021000000af80020009c00000af8020080410000006002200210000000000112019f000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000bb8011001c700008010020000392bdc2bd70000040f000000010020019000002bb20000613d000000000101043b000000000001042d000000000100001900002bde0001043000000000050100190000000000200443000000050030008c00002bc20000413d000000040100003900000000020000190000000506200210000000000664001900000005066002700000000006060031000000000161043a0000000102200039000000000031004b00002bba0000413d00000af80030009c00000af8030080410000006001300210000000000200041400000af80020009c00000af802008041000000c002200210000000000112019f00000bc7011001c700000000020500192bdc2bd70000040f000000010020019000002bd10000613d000000000101043b000000000001042d000000000001042f00002bd5002104210000000102000039000000000001042d0000000002000019000000000001042d00002bda002104230000000102000039000000000001042d0000000002000019000000000001042d00002bdc0000043200002bdd0001042e00002bde00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000001ffffffe000000000000000000000000000000000000000000000000000000000ffffffe0000000000000000000000000000000000000000000000000ffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000fffffffffffffd5f000000000000000000000000000000000000ffffffffffffffffffffffffffff000000000000000000000000ffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000ffffff000000000000000000000000000000000000000000000000000000ffffffffffd6f21326ab749d5729fcba5677c79037b459436ab7bff709c9d06ce9f10c1a9d290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56302000000000000000000000000000000000000200000000000000000000000004ef1d2ad89edf8c4d91132028e8195cdf30bb4b5053d4f8cd260341d4805f30ab10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf63da8a5f161a6c3ff06a60736d0ed24d7963cc6a5c4fafd2fa1dae9bb908e07a5c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b75ca53043ea007e5c65182cbb028f60d7179ff4b55739a3949b401801c942e658a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19bc45a015500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000022afcccb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000000000000000000000000800000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000007fffff0000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000fffd8963efd1fc6a506488495d951d5263988d26ffffffffffffffffffffffff0002769c102e0395af9b77b6a26ae2ae9c69e97c0000000000000000ffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000003627a301d71055774c85800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000028f6481ab7f045a5af012a19d003aaa00000000000000000000000000000000028f6481ab7f045a5af012a19d003aaa00000000000000000000000000800000000000000000000000000000000000007fffffffffffffffffffffffffffffff24d20f617e6a657ebaa1d9f8665f9cd0ffffffffffffffffffffffffffffffff24d20f617e6a657ebaa1d9f8665f9cd100000000000000000000000000000000000000000000000000000000000d89e8000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000fffcb933bd6fad37aa2d162d1a59400100000000000000000000000000000000fff97272373d413259a46990580e213a00000000000000000000000000000000fff2e50f5f656932ef12357cf3c7fdcc00000000000000000000000000000000ffe5caca7e10e4e61c3624eaa0941cd000000000000000000000000000000000ffcb9843d60f6159c9db58835c92664400000000000000000000000000000000ff973b41fa98c081472e6896dfb254c000000000000000000000000000000000ff2ea16466c96a3843ec78b326b5286100000000000000000000000000000000fe5dee046a99a2a811c461f1969c305300000000000000000000000000000000fcbe86c7900a88aedcffc83b479aa3a400000000000000000000000000000000f987a7253ac413176f2b074cf7815e5400000000000000000000000000000000f3392b0822b70005940c7a398e4b70f300000000000000000000000000000000e7159475a2c29b7443b29c7fa6e889d900000000000000000000000000000000d097f3bdfd2022b8845ad8f792aa582500000000000000000000000000000000a9f746462d870fdf8a65dc1f90e061e50000000000000000000000000000000070d869a156d2a1b890bb3df62baf32f70000000000000000000000000000000031be135f97d08fd981231505542fcfa60000000000000000000000000000000009aa508b5b7a84e1c677de54f3e99bc9000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000005d6af8dedb81196699c329225ee60400000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000002216e584f5fa1ea926041bedfe98000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000048a170391f7dc42444e8fa2540000000000000000000000000000000000000000000000000000000000000008c379a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000ffffffffffffffffffffffffffff0000000000000000000000000000ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000796b89b91644bc98cd93958e4c9038275d622183e25ac5af08cc6b5d9553913202000002000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000200000000000000000000000000000000000040000000000000000000000000ffffffffffffffffffffffffffffffffffff0000000000000000000000000000ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef1698ee8200000000000000000000000000000000000000000000000000000000029375160000000000000000000000000000000000000000000000000000000013ead562000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084000000000000000000000000000000020000000000000000000000000000060000000100000000000000000052000000000000000000000000000000000000000000000000000000000000007b9c891600000000000000000000000000000000000000000000000000000000310ab089e4439a4c15d089f94afb7896ff553aecb10793d0ab882de59d99a32e020000020000000000000000000000000000004400000000000000000000000000000000000000000000000000000000000000000000000000000000a4dac28000000000000000000000000000000000000000000000000000000000dd62ed3d00000000000000000000000000000000000000000000000000000000f3ccaabf00000000000000000000000000000000000000000000000000000000fa461e3200000000000000000000000000000000000000000000000000000000fa461e3300000000000000000000000000000000000000000000000000000000fe330cc400000000000000000000000000000000000000000000000000000000f3ccaac000000000000000000000000000000000000000000000000000000000f680200d00000000000000000000000000000000000000000000000000000000dd62ed3e00000000000000000000000000000000000000000000000000000000e66ce9fe00000000000000000000000000000000000000000000000000000000e8a3d48500000000000000000000000000000000000000000000000000000000c264a06200000000000000000000000000000000000000000000000000000000c264a06300000000000000000000000000000000000000000000000000000000cce7ec1300000000000000000000000000000000000000000000000000000000d96a094a00000000000000000000000000000000000000000000000000000000a4dac28100000000000000000000000000000000000000000000000000000000a9059cbb00000000000000000000000000000000000000000000000000000000aea485cc000000000000000000000000000000000000000000000000000000003257b4f20000000000000000000000000000000000000000000000000000000084e1c22b000000000000000000000000000000000000000000000000000000008eca36ef000000000000000000000000000000000000000000000000000000008eca36f00000000000000000000000000000000000000000000000000000000095d89b410000000000000000000000000000000000000000000000000000000084e1c22c000000000000000000000000000000000000000000000000000000008d6118ad000000000000000000000000000000000000000000000000000000003257b4f30000000000000000000000000000000000000000000000000000000033d2220e0000000000000000000000000000000000000000000000000000000070a082310000000000000000000000000000000000000000000000000000000023b872dc0000000000000000000000000000000000000000000000000000000023b872dd00000000000000000000000000000000000000000000000000000000264c802200000000000000000000000000000000000000000000000000000000313ce5670000000000000000000000000000000000000000000000000000000006fdde0300000000000000000000000000000000000000000000000000000000095ea7b30000000000000000000000000000000000000000000000000000000018160ddda9059cbb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004400000000000000000000000000844d13000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000800000000000000000000000000000000000000000000000000000000000000000ffffffffffffff7f0000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000080000000000000000048f5c3ed0000000000000000000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000d7b2559b000000000000000000000000000000000000000000000000000000001806aa1896bbf26568e884a7374b41e002500962caba6a15023a8d90e8508b830200000200000000000000000000000000000024000000000000000000000000efa21fbd0000000000000000000000000000000000000000000000000000000070a08231000000000000000000000000000000000000000000000000000000009cc7f708afc65944829bd487b90b72536b1951864fbfc14e125fc972a6507f394e487b71000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffff000000000000000000000000000000000000000a00000000000000000000000002c5211c600000000000000000000000000000000000000000000000000000000f4d678b800000000000000000000000000000000000000000000000000000000b4e95d930000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000028000000000000000008c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925d92e233d0000000000000000000000000000000000000000000000000000000013be252b00000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000ffffffffffffffc00000000000000000000000040000000000000000000000000000000000000000ffffffff0000000000000000000000000000ffffffffffffffffffffffffffff000000000000000000000008000000000000000000000000000000000000000002000000000f4240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff7c01000023aa45d5fdf48a28b4820195a11f37d29c19eba9d845947d9342b5e7a59c4d535bdea7cd8a978f128b93471df48c7dbab89d703809115bdc118c235bfd0200000000000000000000000000000000000084000000000000000000000000104cf66a7dde109c15727b76799d604f2fae37e10ef912cd90a2378b917ec9900434ab2000000000000000000000000000000000000000000000000000000000095ea7b300000000000000000000000000000000000000000000000000000000d0e30db00000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000040000000000000000000000003850c7bd00000000000000000000000000000000000000000000000000000000128acb080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4000000000000000000000000fffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffff00000000000000000000000c00000000000000000000000000000000000000002e1a7d4d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff7b0200000000000000000000000000000000000000000000000000000000000000a27bfda2365303af1eb9ae3c438e6ab4bc7c77fd59a91e7157b1cfeed9ba4b58000000000000000000000000000000000000000000000000fffffffffffffe9f883164560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001640000000000000000000000009442fd6200000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000b7c992d1a5a2e5be4dcae907b56ba0b4f1dee27c150539027e64fcce6f3859d900000000000000000000000000000000000000000000000000000000b12d13eb00000000000000000000000000000000000000040000001c00000000000000000200000000000000000000000000000000000080000000000000000000000000064fb1933e186be0b289a87e98518dc18cc9856ecbc9f1353d1a138ddf733ec5bb2875c3000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000ed7a144fad14804d5c249145e3e0e2b63a9eb455b76aee5bc92d711e9bba3e4a02000002000000000000000000000000000000000000000000000000000000002f1c7c955ee43daa29c7c32b2611892749dadc5f9818602ec62a13b0ddf4fc3d

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

000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000033b2e3c9fd0803ce8000000000000000000000000000000b20c44d202743cbcc393d67af242ceb3fd4177ad0000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000007fd9c1b103b10fd4ef45e665d330be3e123f58000000000000000000000000000000000000000000000000000b1a2bc2ec50000000000000000000000000000069f199763c045a294c7913e64ba80e5f362a5d70000000000000000000000009a0ab7367d57dce66a28ee96bbabf7b6962d7d4e00000000000000000000000000000000000000000000000000000000000027100000000000000000000000009edcde0257f2386ce177c3a7fcdd97787f0d841d00000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000004563918244f40000000000000000000000000000000000000000000000000000000000000000038400000000000000000000000000000000000000000000000000000000000001c20000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000100b307abfa7e00000000000000000000000000000000000000000000000000000000000000084747474747474747000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000838383838383838380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : parameters (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
30 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [1] : 00000000000000000000000000000000000000000000000000000000000002a0
Arg [2] : 00000000000000000000000000000000000000000000000000000000000002e0
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000320
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000360
Arg [6] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000
Arg [7] : 000000000000000000000000b20c44d202743cbcc393d67af242ceb3fd4177ad
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000046
Arg [9] : 000000000000000000000000000000000000000000000000000000000000001e
Arg [10] : 00000000000000000000000007fd9c1b103b10fd4ef45e665d330be3e123f580
Arg [11] : 00000000000000000000000000000000000000000000000000b1a2bc2ec50000
Arg [12] : 000000000000000000000000069f199763c045a294c7913e64ba80e5f362a5d7
Arg [13] : 0000000000000000000000009a0ab7367d57dce66a28ee96bbabf7b6962d7d4e
Arg [14] : 0000000000000000000000000000000000000000000000000000000000002710
Arg [15] : 0000000000000000000000009edcde0257f2386ce177c3a7fcdd97787f0d841d
Arg [16] : 00000000000000000000000000000000000000000000000006f05b59d3b20000
Arg [17] : 0000000000000000000000000000000000000000000000004563918244f40000
Arg [18] : 0000000000000000000000000000000000000000000000000000000000000384
Arg [19] : 00000000000000000000000000000000000000000000000000000000000001c2
Arg [20] : 0000000000000000000000000000000000000000000000000000000000000014
Arg [21] : 000000000000000000000000000000000000000000000000000100b307abfa7e
Arg [22] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [23] : 4747474747474747000000000000000000000000000000000000000000000000
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [25] : 3838383838383838000000000000000000000000000000000000000000000000
Arg [26] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [27] : 2000000000000000000000000000000000000000000000000000000000000000
Arg [28] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [29] : 2000000000000000000000000000000000000000000000000000000000000000


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.